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

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

Implement PAYDirect Bank Branch payment.

  • Property svn:keywords set to Id
File size: 16.4 KB
Line 
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
19from datetime import datetime, timedelta
20from zope.component import getUtility
21from zope.security import checkPermission
22from waeup.kofa.interfaces import IKofaUtils
23from waeup.kofa.utils.helpers import to_timezone
24from waeup.kofa.browser.layout import UtilityView, KofaPage, KofaFormPage, action
25from waeup.kofa.browser.viewlets import ManageActionButton
26from waeup.kofa.students.interfaces import IStudentsUtils
27from waeup.kofa.students.browser import OnlinePaymentDisplayFormPage as OPDPStudent
28from waeup.kofa.applicants.browser import OnlinePaymentDisplayFormPage as OPDPApplicant
29from kofacustom.nigeria.interswitch.helpers import (
30    query_interswitch, write_payments_log, fetch_booking_details)
31from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
32from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
33from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
34from kofacustom.nigeria.interswitch.tests import PAYDIRECT_URL, PAYDIRECT_HOST, MERCHANT_ID
35from kofacustom.nigeria.interfaces import MessageFactory as _
36
37GATEWAY_AMT = 300.0
38
39# Buttons
40
41def module_activated(session, payment):
42    if payment.r_company and payment.r_company != 'interswitch':
43        return False
44    try:
45        return getattr(grok.getSite()['configuration'][str(session)],
46            'interswitch_enabled', True)
47    except KeyError:
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
54
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'
61    text = _('Pay via Interswitch CollegePAY')
62    target = 'goto_interswitch'
63
64    @property
65    def target_url(self):
66        if not module_activated(
67            self.context.student.current_session, self.context):
68            return ''
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
77    @property
78    def target_url(self):
79        if not module_activated(
80            self.context.__parent__.__parent__.year, self.context):
81            return ''
82        if self.context.p_state != 'unpaid':
83            return ''
84        return self.view.url(self.view.context, self.target)
85
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'
92    text = _('Requery CollegePAY History')
93    target = 'request_webservice'
94
95    @property
96    def target_url(self):
97        if not module_activated(
98            self.context.student.current_session, self.context):
99            return ''
100        if self.context.p_state in ('paid', 'waived', 'scholarship'):
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
109    @property
110    def target_url(self):
111        if not module_activated(
112            self.context.__parent__.__parent__.year, self.context):
113            return ''
114        if self.context.p_state in ('paid', 'waived', 'scholarship'):
115            return ''
116        return self.view.url(self.view.context, self.target)
117
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):
129        if not module_activated(
130            self.context.student.current_session, self.context):
131            return ''
132        if self.context.p_state != 'paid' \
133            or self.context.r_company != u'interswitch':
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
142    @property
143    def target_url(self):
144        if not module_activated(
145            self.context.__parent__.__parent__.year, self.context):
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)
151
152# Webservice request views
153
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
164    https = True
165    mac = None
166
167    def update(self):
168        if not module_activated(
169            self.context.student.current_session, self.context):
170            return
171        if self.context.p_state in ('paid', 'waived', 'scholarship'):
172            self.flash(_('This ticket has already been paid.'), type='danger')
173            return
174        student = self.context.student
175        success, msg, log = query_interswitch(
176            self.context, self.product_id,
177            self.gateway_host, self.gateway_url,
178            self.https, self.mac, False)
179        student.writeLogMessage(self, log)
180        if not success:
181            self.flash(msg, type='danger')
182            return
183        write_payments_log(student.student_id, self.context)
184        flashtype, msg, log = self.context.doAfterStudentPayment()
185        if log is not None:
186            student.writeLogMessage(self, log)
187        self.flash(msg, type=flashtype)
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
204    https = True
205    mac = None
206
207    def update(self):
208        if not module_activated(
209            self.context.__parent__.__parent__.year, self.context):
210            return
211        if self.context.p_state == 'paid':
212            self.flash(_('This ticket has already been paid.'), type='danger')
213            return
214        applicant = self.context.__parent__
215        success, msg, log = query_interswitch(
216            self.context, self.product_id,
217            self.gateway_host, self.gateway_url,
218            self.https, self.mac, False)
219        applicant.writeLogMessage(self, log)
220        if not success:
221            self.flash(msg, type='danger')
222            return
223        write_payments_log(applicant.applicant_id, self.context)
224        flashtype, msg, log = self.context.doAfterApplicantPayment()
225        if log is not None:
226            applicant.writeLogMessage(self, log)
227        self.flash(msg, type=flashtype)
228        return
229
230    def render(self):
231        self.redirect(self.url(self.context.__parent__, 'edit'))
232        return
233
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):
248        if not module_activated(
249            self.context.student.current_session, self.context):
250            return
251        if self.context.p_state  != 'paid' \
252            or self.context.r_company != u'interswitch':
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,
259            self.https, self.mac, True)
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):
285        if not module_activated(
286            self.context.__parent__.__parent__.year, self.context):
287            return
288        if self.context.p_state != 'paid' \
289            or self.context.r_company != u'interswitch':
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,
296            self.https, self.mac, True)
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
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')
318    label = _('Submit data to CollegePAY')
319    submit_button = _('Submit')
320
321    action = None
322    site_name = None
323    currency = None
324    pay_item_id = None
325    product_id = None
326    xml_data = None
327    hashvalue = None
328    gateway_amt = GATEWAY_AMT
329
330    def init_update(self):
331        if self.context.p_state == 'paid':
332            return _("Payment ticket can't be re-sent to CollegePAY.")
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
338        if time_delta.days > 7:
339            return _("This payment ticket is too old. Please create a new ticket.")
340        if self.context.r_company and self.context.r_company != 'interswitch':
341            return _("Payment ticket has been used for another payment gateway.")
342        student = self.context.student
343        certificate = getattr(student['studycourse'],'certificate',None)
344        if certificate is None:
345            return _("Study course data are incomplete.")
346        kofa_utils = getUtility(IKofaUtils)
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.")
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
358        self.category = self.context.category
359        tz = kofa_utils.tzinfo
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')
363        self.student = student
364        self.xmldict = xmldict
365        return
366
367    def update(self):
368        if not module_activated(
369            self.context.student.current_session, self.context):
370            self.flash(_('Forbidden'), type='danger')
371            self.redirect(self.url(self.context, '@@index'))
372            return
373        error = self.init_update()
374        if error:
375            self.flash(error, type='danger')
376            self.redirect(self.url(self.context, '@@index'))
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)
385        return
386
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')
395    label = _('Submit data to CollegePAY')
396    submit_button = _('Submit')
397
398    action = None
399    site_name = None
400    currency = None
401    pay_item_id = None
402    product_id = None
403    xml_data = None
404    hashvalue = None
405    gateway_amt = GATEWAY_AMT
406
407    def init_update(self):
408        if self.context.p_state != 'unpaid':
409            return _("Payment ticket can't be re-sent to CollegePAY.")
410        if self.context.__parent__.__parent__.expired \
411            and self.context.__parent__.__parent__.strict_deadline:
412            return _("Payment ticket can't be sent to CollegePAY. "
413                     "Application period has expired.")
414        if self.context.r_company and self.context.r_company != 'interswitch':
415            return _("Payment ticket has been used for another payment gateway.")
416        tz = getUtility(IKofaUtils).tzinfo
417        time_delta = datetime.utcnow() - self.context.creation_date
418        if time_delta.days > 7:
419            return _("This payment ticket is too old. Please create a new ticket.")
420        self.applicant = self.context.__parent__
421        self.category = self.context.category
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')
426        return
427
428    def update(self):
429        if not module_activated(
430            self.context.__parent__.__parent__.year, self.context):
431            self.flash(_('Forbidden'), type='danger')
432            self.redirect(self.url(self.context, '@@index'))
433            return
434        error = self.init_update()
435        if error:
436            self.flash(error, type='danger')
437            self.redirect(self.url(self.context, '@@index'))
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)
446        return
447
Note: See TracBrowser for help on using the repository browser.