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

Last change on this file since 16827 was 16484, checked in by Henrik Bettermann, 3 years ago

Implement PAYDirect Bank Branch payment.

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