import datetime
import grok
import random
import re
from zope.event import notify
from waeup.ikoba.interfaces import MessageFactory as _
from waeup.ikoba.browser.layout import IkobaEditFormPage, action
from waeup.ikoba.payments.interfaces import (
IPaymentGatewayService, IPayment, IPayable, IPayer, IPayee,
STATE_AWAITING_GATEWAY_CONFIRM, STATE_PAID)
from waeup.ikoba.payments.payment import (
Payment, get_payment, find_payable_from_payable_id, format_amount,
find_payer_from_payer_id, PaymentProviderServiceBase,
PaymentWaitingForGatewayEvent, PaymentFinishedEvent)
grok.templatedir('browser_templates')
WARN_FINAL_SUBMIT = _(
'You can not edit your contract after final submission. '
'Do you really want to submit?'
)
RE_CC_NUMBER = re.compile('^[0-9]{9,25}$')
RE_CSC = re.compile('^[0-9]{3,4}$')
class DemoCreditcardPaymentService(PaymentProviderServiceBase):
"""A demo payment gateway service.
This one supports credit card payments.
"""
grok.implements(IPaymentGatewayService)
grok.name('demo_creditcard')
title = _(u'Credit Card (Demo Payments)')
def create_payment(self, payer, payable, payee=None):
"""Create a payment.
"""
if not IPayer.providedBy(payer):
payer = IPayer(payer)
if not IPayable.providedBy(payable):
payable = IPayable(payable)
if (payee is not None) and (not IPayee.providedBy(payee)):
payee = IPayee(payee)
payment = Payment(payer, payable, payee)
payment.gateway_service = 'demo_creditcard' # must be grok.name above
return payment
def next_step(self, payment_id):
"""Tell where to go next.
Returns (context, view_name). Both may be none.
"""
payment = get_payment(payment_id)
if payment is None:
return None, None
if payment.state == STATE_AWAITING_GATEWAY_CONFIRM:
return payment, 'demo_cc2'
return payment, 'demo_cc1'
class CreditCardStep1(IkobaEditFormPage):
grok.context(IPayment)
grok.name('demo_cc1')
# XXX: Use own permissions for payments
grok.require('waeup.Authenticated')
label = "Enter Credit Card Details"
grok.template('demo_cc_step1')
pnav = 4
def validate_form(self):
fields = ['first_name', 'last_name', 'cc_number', 'csc', 'exp_date']
cls = 'form-group'
if len(self.request.form):
cls += ' has-success'
result = dict([(x, cls) for x in fields])
if not len(self.request.form):
return True, result
err = 'form-group has-error'
if not self.first_name:
result['first_name'] = err
if not self.last_name:
result['last_name'] = err
if not RE_CC_NUMBER.match(self.cc_number):
result['cc_number'] = err
if not RE_CSC.match(self.csc):
result['csc'] = err
if err in result.values():
return False, result
return True, result
def update(self, first_name=None, last_name=None, cc_number=None,
month=None, year=None):
self.payer = IPayer(find_payer_from_payer_id(self.context.payer_id))
self.payable = IPayable(find_payable_from_payable_id(
self.context.payable_id))
form = self.request.form
self.first_name = form.get('first_name', self.payer.first_name)
self.last_name = form.get('last_name', self.payer.last_name)
self.cc_number = form.get('cc_number', '')
self.month = int(form.get('exp_month', datetime.datetime.now().month))
self.year = int(form.get('exp_year', datetime.datetime.now().year))
self.csc = form.get('csc', '')
self.amount = format_amount(
self.context.amount, self.context.currency)
self.months = ''.join([
'' % (
(x == self.month) and ' selected="selected"' or '',
x) for x in range(1, 13)])
self.years = ''.join([
'' % (
(x == self.year + 1) and ' selected="selected"' or '',
x) for x in range(self.year - 1, self.year + 15)])
self.ok, self.validations = self.validate_form()
@action(_('Authorize Payment'), warning=WARN_FINAL_SUBMIT,
style="primary")
def authorize(self, **data):
if not self.ok:
self.flash(_("Please review (red) entries below!"),
type='warning')
return
# XXX: payment really started, do lots of logging
self.context.state = STATE_AWAITING_GATEWAY_CONFIRM
notify(PaymentWaitingForGatewayEvent(self.context))
self.redirect(self.url(self.context, 'demo_cc2'))
return
@action(_('Cancel'))
def cancel(self, **data):
"""Go back to the payable (if possible) or site home.
"""
payable_id = getattr(self.context, 'payable_id', '')
payed_item = find_payable_from_payable_id(payable_id)
self.flash(_("Payment cancelled."))
if payed_item is not None:
# remove context/payment?
target = payed_item
else:
target = grok.getSite()
self.redirect(self.url(target))
class CreditCardStep2(IkobaEditFormPage):
grok.context(IPayment)
grok.name('demo_cc2')
# XXX: Use own permissions for payments
grok.require('waeup.Authenticated')
label = " "
grok.template('demo_cc_step2')
pnav = 4
def update(self):
cnt = int(self.request.form.get('cnt', '0'))
self.cnt = cnt + 1
threshold = random.choice(range(10))
self.success = False
# HERE WOULD WE REALLY ASK FOR VERIFICATION
if threshold <= cnt:
self.success = True
self.flash(_("Your payment was finished successfully."))
if self.request.form.get('SUBMIT', None):
self.renew()
def renew(self):
if not self.success:
return
payable_id = getattr(self.context, 'payable_id', '')
payed_item = find_payable_from_payable_id(payable_id)
self.context.state = STATE_PAID
self.context.payment_date = datetime.datetime.utcnow()
notify(PaymentFinishedEvent(self.context))
self.redirect(self.url(payed_item))