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

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

Don't show Interswitch button if amount_auth is 0.

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