source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py @ 17421

Last change on this file since 17421 was 17248, checked in by Henrik Bettermann, 23 months ago

Enable Paypal only for USD payments.
Enable all other gateway services only for NGN payments.

  • Property svn:keywords set to Id
File size: 16.7 KB
RevLine 
[9781]1## $Id: browser.py 17248 2022-12-28 15:26:56Z henrik $
2##
3## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
[12973]19from datetime import datetime, timedelta
[11630]20from zope.component import getUtility
[13578]21from zope.security import checkPermission
[11630]22from waeup.kofa.interfaces import IKofaUtils
23from waeup.kofa.utils.helpers import to_timezone
[16484]24from waeup.kofa.browser.layout import UtilityView, KofaPage, KofaFormPage, action
[9781]25from waeup.kofa.browser.viewlets import ManageActionButton
[11642]26from waeup.kofa.students.interfaces import IStudentsUtils
[9781]27from waeup.kofa.students.browser import OnlinePaymentDisplayFormPage as OPDPStudent
28from waeup.kofa.applicants.browser import OnlinePaymentDisplayFormPage as OPDPApplicant
29from kofacustom.nigeria.interswitch.helpers import (
[16484]30    query_interswitch, write_payments_log, fetch_booking_details)
[9781]31from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
32from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
33from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
[16484]34from kofacustom.nigeria.interswitch.tests import PAYDIRECT_URL, PAYDIRECT_HOST, MERCHANT_ID
[9781]35from kofacustom.nigeria.interfaces import MessageFactory as _
36
[15755]37GATEWAY_AMT = 300.0
38
[13581]39# Buttons
40
[15770]41def module_activated(session, payment):
[17248]42    if payment.p_currency != 'NGN':
43        return False
[15770]44    if payment.r_company and payment.r_company != 'interswitch':
45        return False
[14755]46    try:
47        return getattr(grok.getSite()['configuration'][str(session)],
48            'interswitch_enabled', True)
49    except KeyError:
[16246]50        session = datetime.now().year
51        try:
52            return getattr(grok.getSite()['configuration'][str(session)],
53                'interswitch_enabled', True)
54        except KeyError:
55            return False
[14755]56
[9781]57class InterswitchActionButtonStudent(ManageActionButton):
58    grok.order(1)
59    grok.context(INigeriaOnlinePayment)
60    grok.view(OPDPStudent)
61    grok.require('waeup.payStudent')
62    icon = 'actionicon_pay.png'
[16484]63    text = _('Pay via Interswitch CollegePAY')
[9781]64    target = 'goto_interswitch'
65
66    @property
67    def target_url(self):
[15770]68        if not module_activated(
69            self.context.student.current_session, self.context):
[14755]70            return ''
[9781]71        if self.context.p_state != 'unpaid':
72            return ''
[16880]73        if self.context.amount_auth == 0:
74            return ''
[9781]75        return self.view.url(self.view.context, self.target)
76
77class InterswitchActionButtonApplicant(InterswitchActionButtonStudent):
78    grok.view(OPDPApplicant)
79    grok.require('waeup.payApplicant')
80
[14755]81    @property
82    def target_url(self):
[15770]83        if not module_activated(
84            self.context.__parent__.__parent__.year, self.context):
[14755]85            return ''
86        if self.context.p_state != 'unpaid':
87            return ''
[16880]88        if self.context.amount_auth == 0:
89            return ''
[14755]90        return self.view.url(self.view.context, self.target)
91
[9781]92class InterswitchRequestWebserviceActionButtonStudent(ManageActionButton):
93    grok.order(2)
94    grok.context(INigeriaOnlinePayment)
95    grok.view(OPDPStudent)
96    grok.require('waeup.payStudent')
97    icon = 'actionicon_call.png'
[16484]98    text = _('Requery CollegePAY History')
[9781]99    target = 'request_webservice'
100
101    @property
102    def target_url(self):
[15770]103        if not module_activated(
104            self.context.student.current_session, self.context):
[14755]105            return ''
[16881]106        if self.context.amount_auth == 0:
107            return ''
[15842]108        if self.context.p_state in ('paid', 'waived', 'scholarship'):
[9781]109            return ''
110        return self.view.url(self.view.context, self.target)
111
112class InterswitchRequestWebserviceActionButtonApplicant(
113    InterswitchRequestWebserviceActionButtonStudent):
114    grok.view(OPDPApplicant)
115    grok.require('waeup.payApplicant')
116
[14755]117    @property
118    def target_url(self):
[15770]119        if not module_activated(
120            self.context.__parent__.__parent__.year, self.context):
[14755]121            return ''
[16881]122        if self.context.amount_auth == 0:
123            return ''
[15842]124        if self.context.p_state in ('paid', 'waived', 'scholarship'):
[14755]125            return ''
126        return self.view.url(self.view.context, self.target)
127
[13581]128class InterswitchVerifyWebserviceActionButtonStudent(ManageActionButton):
129    grok.order(3)
130    grok.context(INigeriaOnlinePayment)
131    grok.view(OPDPStudent)
132    grok.require('waeup.manageStudent')
133    icon = 'actionicon_call.png'
134    text = _('Verify Payment')
135    target = 'verify_payment'
136
137    @property
138    def target_url(self):
[15770]139        if not module_activated(
140            self.context.student.current_session, self.context):
[14755]141            return ''
[13585]142        if self.context.p_state != 'paid' \
143            or self.context.r_company != u'interswitch':
[13581]144            return ''
145        return self.view.url(self.view.context, self.target)
146
147class InterswitchVerifyWebserviceActionButtonApplicant(
148    InterswitchVerifyWebserviceActionButtonStudent):
149    grok.view(OPDPApplicant)
150    grok.require('waeup.manageApplication')
151
[14755]152    @property
153    def target_url(self):
[15770]154        if not module_activated(
155            self.context.__parent__.__parent__.year, self.context):
[14755]156            return ''
157        if self.context.p_state != 'paid' \
158            or self.context.r_company != u'interswitch':
159            return ''
160        return self.view.url(self.view.context, self.target)
[13581]161
162# Webservice request views
163
[9781]164class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
165    """ Request webservice view for the CollegePAY gateway
166    """
167    grok.context(INigeriaStudentOnlinePayment)
168    grok.name('request_webservice')
169    grok.require('waeup.payStudent')
170
171    product_id = None
172    gateway_host = None
173    gateway_url = None
[13044]174    https = True
[13387]175    mac = None
[9781]176
177    def update(self):
[15770]178        if not module_activated(
179            self.context.student.current_session, self.context):
[14755]180            return
[15842]181        if self.context.p_state in ('paid', 'waived', 'scholarship'):
[13579]182            self.flash(_('This ticket has already been paid.'), type='danger')
[9781]183            return
184        student = self.context.student
185        success, msg, log = query_interswitch(
[11915]186            self.context, self.product_id,
187            self.gateway_host, self.gateway_url,
[13584]188            self.https, self.mac, False)
[9781]189        student.writeLogMessage(self, log)
190        if not success:
[11579]191            self.flash(msg, type='danger')
[9781]192            return
193        write_payments_log(student.student_id, self.context)
[11581]194        flashtype, msg, log = self.context.doAfterStudentPayment()
[9781]195        if log is not None:
196            student.writeLogMessage(self, log)
[11581]197        self.flash(msg, type=flashtype)
[9781]198        return
199
200    def render(self):
201        self.redirect(self.url(self.context, '@@index'))
202        return
203
204class InterswitchPaymentRequestWebservicePageApplicant(UtilityView, grok.View):
205    """ Request webservice view for the CollegePAY gateway
206    """
207    grok.context(INigeriaApplicantOnlinePayment)
208    grok.name('request_webservice')
209    grok.require('waeup.payApplicant')
210
211    product_id = None
212    gateway_host = None
213    gateway_url = None
[13044]214    https = True
[13387]215    mac = None
[9781]216
217    def update(self):
[15770]218        if not module_activated(
219            self.context.__parent__.__parent__.year, self.context):
[14755]220            return
[13580]221        if self.context.p_state == 'paid':
[11642]222            self.flash(_('This ticket has already been paid.'), type='danger')
[9781]223            return
224        applicant = self.context.__parent__
225        success, msg, log = query_interswitch(
[11915]226            self.context, self.product_id,
227            self.gateway_host, self.gateway_url,
[13584]228            self.https, self.mac, False)
[9781]229        applicant.writeLogMessage(self, log)
230        if not success:
[11579]231            self.flash(msg, type='danger')
[9781]232            return
233        write_payments_log(applicant.applicant_id, self.context)
[11581]234        flashtype, msg, log = self.context.doAfterApplicantPayment()
[9781]235        if log is not None:
236            applicant.writeLogMessage(self, log)
[11581]237        self.flash(msg, type=flashtype)
[9781]238        return
239
240    def render(self):
[15529]241        self.redirect(self.url(self.context.__parent__, 'edit'))
[9781]242        return
[11630]243
[13581]244class InterswitchPaymentVerifyWebservicePageStudent(UtilityView, grok.View):
245    """ Verify payment view for the CollegePAY gateway
246    """
247    grok.context(INigeriaStudentOnlinePayment)
248    grok.name('verify_payment')
249    grok.require('waeup.manageStudent')
250
251    product_id = None
252    gateway_host = None
253    gateway_url = None
254    https = True
255    mac = None
256
257    def update(self):
[15770]258        if not module_activated(
259            self.context.student.current_session, self.context):
[14755]260            return
[13585]261        if self.context.p_state  != 'paid' \
262            or self.context.r_company != u'interswitch':
[13581]263            self.flash(_('This ticket has not been paid.'), type='danger')
264            return
265        student = self.context.student
266        success, msg, log = query_interswitch(
267            self.context, self.product_id,
268            self.gateway_host, self.gateway_url,
[13584]269            self.https, self.mac, True)
[13581]270        student.writeLogMessage(self, log)
271        if not success:
272            self.flash(msg, type='danger')
273            return
274        self.flash(msg)
275        return
276
277    def render(self):
278        self.redirect(self.url(self.context, '@@index'))
279        return
280
281class InterswitchPaymentVerifyWebservicePageApplicant(UtilityView, grok.View):
282    """ Verify payment view for the CollegePAY gateway
283    """
284    grok.context(INigeriaApplicantOnlinePayment)
285    grok.name('verify_payment')
286    grok.require('waeup.manageApplication')
287
288    product_id = None
289    gateway_host = None
290    gateway_url = None
291    https = True
292    mac = None
293
294    def update(self):
[15770]295        if not module_activated(
296            self.context.__parent__.__parent__.year, self.context):
[14755]297            return
[13585]298        if self.context.p_state != 'paid' \
299            or self.context.r_company != u'interswitch':
[13581]300            self.flash(_('This ticket has not been paid.'), type='danger')
301            return
302        applicant = self.context.__parent__
303        success, msg, log = query_interswitch(
304            self.context, self.product_id,
305            self.gateway_host, self.gateway_url,
[13584]306            self.https, self.mac, True)
[13581]307        applicant.writeLogMessage(self, log)
308        if not success:
309            self.flash(msg, type='danger')
310            return
311        self.flash(msg)
312        return
313
314    def render(self):
315        self.redirect(self.url(self.context, '@@index'))
316        return
317
318# Forwarding pages
319
[11630]320class InterswitchPageStudent(KofaPage):
321    """ View which sends a POST request to the Interswitch
322    CollegePAY payment gateway.
323    """
324    grok.context(INigeriaOnlinePayment)
325    grok.name('goto_interswitch')
326    grok.template('student_goto_interswitch')
327    grok.require('waeup.payStudent')
[16484]328    label = _('Submit data to CollegePAY')
[11630]329    submit_button = _('Submit')
330
[11639]331    action = None
332    site_name = None
333    currency = None
334    pay_item_id = None
335    product_id = None
336    xml_data = None
[12477]337    hashvalue = None
[15755]338    gateway_amt = GATEWAY_AMT
[11639]339
[11642]340    def init_update(self):
[11630]341        if self.context.p_state == 'paid':
[11642]342            return _("Payment ticket can't be re-sent to CollegePAY.")
[13478]343        now = datetime.utcnow()
344        if self.context.creation_date.tzinfo is not None:
345            # That's bad. Please store timezone-naive datetimes only!
346            now = self.context.creation_date.tzinfo.localize(now)
347        time_delta = now - self.context.creation_date
[13015]348        if time_delta.days > 7:
[12973]349            return _("This payment ticket is too old. Please create a new ticket.")
[15755]350        if self.context.r_company and self.context.r_company != 'interswitch':
351            return _("Payment ticket has been used for another payment gateway.")
[11642]352        student = self.context.student
[11630]353        certificate = getattr(student['studycourse'],'certificate',None)
354        if certificate is None:
[11642]355            return _("Study course data are incomplete.")
[11639]356        kofa_utils = getUtility(IKofaUtils)
[11642]357        student_utils = getUtility(IStudentsUtils)
358        if student_utils.samePaymentMade(student, self.context.p_category,
359            self.context.p_item, self.context.p_session):
360            return _("This type of payment has already been made.")
[11630]361        xmldict = {}
362        if certificate is not None:
363            xmldict['department'] = certificate.__parent__.__parent__.code
364            xmldict['faculty'] = certificate.__parent__.__parent__.__parent__.code
365        else:
366            xmldict['department'] = None
367            xmldict['faculty'] = None
[12509]368        self.category = self.context.category
[11639]369        tz = kofa_utils.tzinfo
[11630]370        self.local_date_time = to_timezone(
371            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
372        self.site_redirect_url = self.url(self.context, 'request_webservice')
[11642]373        self.student = student
374        self.xmldict = xmldict
375        return
[11630]376
[11642]377    def update(self):
[15770]378        if not module_activated(
379            self.context.student.current_session, self.context):
[15974]380            self.flash(_('Forbidden'), type='danger')
381            self.redirect(self.url(self.context, '@@index'))
[14755]382            return
[11642]383        error = self.init_update()
384        if error:
385            self.flash(error, type='danger')
386            self.redirect(self.url(self.context, '@@index'))
[15755]387        # Already now it becomes an Interswitch payment. We set the net amount
388        # and add the gateway amount.
389        if not self.context.r_company:
390            self.context.net_amt = self.context.amount_auth
391            self.context.amount_auth += self.gateway_amt
392            self.context.gateway_amt = self.gateway_amt
393            self.context.r_company = u'interswitch'
394        self.amount_auth = int(100 * self.context.amount_auth)
[11642]395        return
396
[11630]397class InterswitchPageApplicant(KofaPage):
398    """ View which sends a POST request to the Interswitch
399    CollegePAY payment gateway.
400    """
401    grok.context(INigeriaApplicantOnlinePayment)
402    grok.require('waeup.payApplicant')
403    grok.template('applicant_goto_interswitch')
404    grok.name('goto_interswitch')
[16484]405    label = _('Submit data to CollegePAY')
[11630]406    submit_button = _('Submit')
407
[11639]408    action = None
409    site_name = None
410    currency = None
411    pay_item_id = None
412    product_id = None
413    xml_data = None
[15755]414    hashvalue = None
415    gateway_amt = GATEWAY_AMT
[11639]416
[11642]417    def init_update(self):
[11630]418        if self.context.p_state != 'unpaid':
[11642]419            return _("Payment ticket can't be re-sent to CollegePAY.")
[11630]420        if self.context.__parent__.__parent__.expired \
421            and self.context.__parent__.__parent__.strict_deadline:
[16247]422            return _("Payment ticket can't be sent to CollegePAY. "
[11642]423                     "Application period has expired.")
[15755]424        if self.context.r_company and self.context.r_company != 'interswitch':
425            return _("Payment ticket has been used for another payment gateway.")
[12973]426        tz = getUtility(IKofaUtils).tzinfo
427        time_delta = datetime.utcnow() - self.context.creation_date
[13015]428        if time_delta.days > 7:
[12973]429            return _("This payment ticket is too old. Please create a new ticket.")
[11630]430        self.applicant = self.context.__parent__
[12509]431        self.category = self.context.category
[11630]432        tz = getUtility(IKofaUtils).tzinfo
433        self.local_date_time = to_timezone(
434            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
435        self.site_redirect_url = self.url(self.context, 'request_webservice')
[11642]436        return
437
438    def update(self):
[15770]439        if not module_activated(
440            self.context.__parent__.__parent__.year, self.context):
[15974]441            self.flash(_('Forbidden'), type='danger')
442            self.redirect(self.url(self.context, '@@index'))
[14755]443            return
[11642]444        error = self.init_update()
445        if error:
446            self.flash(error, type='danger')
447            self.redirect(self.url(self.context, '@@index'))
[15755]448        # Already now it becomes an Interswitch payment. We set the net amount
449        # and add the gateway amount.
450        if not self.context.r_company:
451            self.context.net_amt = self.context.amount_auth
452            self.context.amount_auth += self.gateway_amt
453            self.context.gateway_amt = self.gateway_amt
454            self.context.r_company = u'interswitch'
455        self.amount_auth = int(100 * self.context.amount_auth)
[13478]456        return
[16484]457
Note: See TracBrowser for help on using the repository browser.