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

Last change on this file since 15185 was 14755, checked in by Henrik Bettermann, 7 years ago

Add a session-based 'switch' to disable Interswitch payments. If the session configuration field interswitch_enabled is missing in custom packages, Interswitch payments are enabled by default (in contrast to Remita payments which are disabled by default).

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