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

Last change on this file since 15755 was 15755, checked in by Henrik Bettermann, 5 years ago

Prepare all payment gateway modules for net amount fee configuration. In the future, provider and gateway surcharges will be determined and added just before the data are being send to the gateways for the first time.

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