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([ '%s' % ( (x == self.month) and ' selected="selected"' or '', x) for x in range(1, 13)]) self.years = ''.join([ '%s' % ( (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))