source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/webcheckoutbrowser.py @ 17260

Last change on this file since 17260 was 17248, checked in by Henrik Bettermann, 23 months ago

Enable Paypal only for USD payments.
Enable all other gateway services only for NGN payments.

  • Property svn:executable set to *
File size: 13.0 KB
Line 
1## $Id: webcheckoutbrowser.py 16587 2021-08-31 06:28:35Z henrik $
2##
3## Copyright (C) 2021 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.i18n import translate
21from zope.component import getUtility
22from zope.security import checkPermission
23from xml.dom.minidom import parseString
24from waeup.kofa.interfaces import IKofaUtils
25from waeup.kofa.utils.helpers import to_timezone
26from waeup.kofa.browser.layout import UtilityView, KofaPage, KofaFormPage, action
27from waeup.kofa.browser.viewlets import ManageActionButton
28from waeup.kofa.payments.interfaces import IPayer
29from waeup.kofa.students.interfaces import IStudentsUtils
30from waeup.kofa.students.browser import OnlinePaymentDisplayFormPage as OPDPStudent
31from waeup.kofa.students.browser import StudentBasePDFFormPage
32from waeup.kofa.applicants.browser import OnlinePaymentDisplayFormPage as OPDPApplicant
33from waeup.kofa.applicants.browser import ApplicantBaseDisplayFormPage
34from kofacustom.nigeria.interswitch.helpers import (
35    write_payments_log, confirm_transaction)
36from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
37from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
38from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
39from kofacustom.nigeria.interswitch.tests import (
40    MERCHANT_ID)
41from kofacustom.nigeria.interfaces import MessageFactory as _
42
43GATEWAY_AMT = 0.0
44
45grok.templatedir('browser_templates')
46
47def webcheckout_module_activated(session, payment):
48    if payment.p_currency != 'NGN':
49        return False
50    if payment.r_company and payment.r_company != 'interswitch':
51        return False
52    try:
53        return getattr(grok.getSite()['configuration'][str(session)],
54            'interswitch_webcheckout_enabled', False)
55    except KeyError:
56        session = datetime.now().year
57        try:
58            return getattr(grok.getSite()['configuration'][str(session)],
59                'interswitch_webcheckout_enabled', False)
60        except KeyError:
61            return False
62
63# Buttons
64
65class WebcheckoutActionButtonStudent(ManageActionButton):
66    grok.context(INigeriaOnlinePayment)
67    grok.view(OPDPStudent)
68    grok.require('waeup.payStudent')
69    grok.order(10)
70    icon = 'actionicon_pay.png'
71    text = _('Pay via Interswitch WebCheckout')
72    target = 'webcheckout'
73
74    @property
75    def target_url(self):
76        if not webcheckout_module_activated(
77            self.context.student.current_session, self.context):
78            return ''
79        if self.context.p_state == 'paid':
80            return ''
81        return self.view.url(self.view.context, self.target)
82
83class WebcheckoutActionButtonApplicant(WebcheckoutActionButtonStudent):
84    grok.view(OPDPApplicant)
85    grok.require('waeup.payApplicant')
86
87    @property
88    def target_url(self):
89        if not webcheckout_module_activated(
90            self.context.__parent__.__parent__.year, self.context):
91            return ''
92        if self.context.p_state != 'unpaid':
93            return ''
94        return self.view.url(self.view.context, self.target)
95
96class WebCheckoutConfirmTransactionActionButtonStudent(ManageActionButton):
97    grok.order(11)
98    grok.context(INigeriaOnlinePayment)
99    grok.view(OPDPStudent)
100    grok.require('waeup.payStudent')
101    icon = 'actionicon_call.png'
102    text = _('Confirm WebCheckout Transaction')
103    target = 'confirm_transaction'
104
105    @property
106    def target_url(self):
107        if not webcheckout_module_activated(
108            self.context.student.current_session, self.context):
109            return ''
110        if self.context.amount_auth == 0:
111            return ''
112        if self.context.p_state in ('paid', 'waived', 'scholarship'):
113            return ''
114        return self.view.url(self.view.context, self.target)
115
116class WebCheckoutConfirmTransactionActionButtonApplicant(
117    WebCheckoutConfirmTransactionActionButtonStudent):
118    grok.view(OPDPApplicant)
119    grok.require('waeup.payApplicant')
120
121    @property
122    def target_url(self):
123        if not webcheckout_module_activated(
124            self.context.__parent__.__parent__.year, self.context):
125            return ''
126        if self.context.amount_auth == 0:
127            return ''
128        if self.context.p_state in ('paid', 'waived', 'scholarship'):
129            return ''
130        return self.view.url(self.view.context, self.target)
131
132# Forwarding pages
133
134class WebCheckoutPageStudent(KofaPage):
135    """ View which sends a POST request to the Interswitch
136    WebCheckout payment gateway.
137    """
138    grok.context(INigeriaOnlinePayment)
139    grok.name('webcheckout')
140    grok.template('student_goto_webcheckout')
141    grok.require('waeup.payStudent')
142    label = _('Submit data to Interswitch')
143    submit_button = _('Submit')
144
145    action = None
146    currency = None
147    pay_item_id = None
148    merchant_code = None
149    gateway_amt = GATEWAY_AMT
150
151    def init_update(self):
152        if self.context.p_state == 'paid':
153            return _("Payment ticket can't be re-sent to WebCheckout.")
154        now = datetime.utcnow()
155        if self.context.creation_date.tzinfo is not None:
156            # That's bad. Please store timezone-naive datetimes only!
157            now = self.context.creation_date.tzinfo.localize(now)
158        time_delta = now - self.context.creation_date
159        if time_delta.days > 7:
160            return _("This payment ticket is too old. Please create a new ticket.")
161        if self.context.r_company and self.context.r_company != 'interswitch':
162            return _("Payment ticket has been used for another payment gateway.")
163        student = self.context.student
164        kofa_utils = getUtility(IKofaUtils)
165        student_utils = getUtility(IStudentsUtils)
166        if student_utils.samePaymentMade(student, self.context.p_category,
167            self.context.p_item, self.context.p_session):
168            return _("This type of payment has already been made.")
169        xmldict = {}
170        self.category = self.context.category
171        tz = kofa_utils.tzinfo
172        self.local_date_time = to_timezone(
173            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
174        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
175        self.student = student
176        return
177
178    def update(self):
179        if not webcheckout_module_activated(
180            self.context.student.current_session, self.context):
181            self.flash(_('Forbidden'), type='danger')
182            self.redirect(self.url(self.context, '@@index'))
183            return
184        error = self.init_update()
185        if error:
186            self.flash(error, type='danger')
187            self.redirect(self.url(self.context, '@@index'))
188        # Already now it becomes an Interswitch payment. We set the net amount
189        # and add the gateway amount.
190        if not self.context.r_company:
191            self.context.net_amt = self.context.amount_auth
192            self.context.amount_auth += self.gateway_amt
193            self.context.gateway_amt = self.gateway_amt
194            self.context.r_company = u'interswitch'
195        self.amount_auth = int(100 * self.context.amount_auth)
196        return
197
198class WebCheckoutPageApplicant(KofaPage):
199    """ View which sends a POST request to the Interswitch
200    WebCheckout payment gateway.
201    """
202    grok.context(INigeriaApplicantOnlinePayment)
203    grok.require('waeup.payApplicant')
204    grok.template('applicant_goto_webcheckout')
205    grok.name('webcheckout')
206    label = _('Submit data to Interswitch')
207    submit_button = _('Submit')
208
209    action = None
210    currency = None
211    pay_item_id = None
212    merchant_code = None
213    gateway_amt = GATEWAY_AMT
214    split_accounts = None
215
216    def init_update(self):
217        if self.context.p_state != 'unpaid':
218            return _("Payment ticket can't be re-sent to WebCheckout.")
219        if self.context.__parent__.__parent__.expired \
220            and self.context.__parent__.__parent__.strict_deadline:
221            return _("Payment ticket can't be sent to WebCheckout. "
222                     "Application period has expired.")
223        if self.context.r_company and self.context.r_company != 'interswitch':
224            return _("Payment ticket has been used for another payment gateway.")
225        tz = getUtility(IKofaUtils).tzinfo
226        time_delta = datetime.utcnow() - self.context.creation_date
227        if time_delta.days > 7:
228            return _("This payment ticket is too old. Please create a new ticket.")
229        self.applicant = self.context.__parent__
230        self.category = self.context.category
231        tz = getUtility(IKofaUtils).tzinfo
232        self.local_date_time = to_timezone(
233            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
234        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
235        return
236
237    def update(self):
238        if not webcheckout_module_activated(
239            self.context.__parent__.__parent__.year, self.context):
240            self.flash(_('Forbidden'), type='danger')
241            self.redirect(self.url(self.context, '@@index'))
242            return
243        error = self.init_update()
244        if error:
245            self.flash(error, type='danger')
246            self.redirect(self.url(self.context, '@@index'))
247        # Already now it becomes an Interswitch payment. We set the net amount
248        # and add the gateway amount.
249        if not self.context.r_company:
250            self.context.net_amt = self.context.amount_auth
251            self.context.amount_auth += self.gateway_amt
252            self.context.gateway_amt = self.gateway_amt
253            self.context.r_company = u'interswitch'
254        self.amount_auth = int(100 * self.context.amount_auth)
255        return
256
257# Webservice request views
258
259class WebCheckoutConfirmTransactionApplicant(UtilityView, grok.View):
260    """ Request webservice view for the WebCheckout gateway
261    """
262    grok.context(INigeriaApplicantOnlinePayment)
263    grok.name('confirm_transaction')
264    grok.require('waeup.payApplicant')
265
266    merchant_code = None
267    gateway_host = None
268    gateway_url = None
269    https = True
270    split_accounts = None
271    mac = ''
272
273
274    def update(self):
275        if not webcheckout_module_activated(
276            self.context.__parent__.__parent__.year, self.context):
277            return
278        if self.context.p_state == 'paid':
279            self.flash(_('This ticket has already been paid.'), type='danger')
280            return
281        applicant = self.context.__parent__
282        success, msg, log = confirm_transaction(
283            self.context,
284            self.merchant_code,
285            self.gateway_host,
286            self.gateway_url,
287            self.https,
288            self.mac)
289        applicant.writeLogMessage(self, log)
290        if not success:
291            self.flash(msg, type='danger')
292            return
293        write_payments_log(applicant.applicant_id, self.context)
294        flashtype, msg, log = self.context.doAfterApplicantPayment()
295        if log is not None:
296            applicant.writeLogMessage(self, log)
297        self.flash(msg, type=flashtype)
298        return
299
300    def render(self):
301        self.redirect(self.url(self.context.__parent__, 'edit'))
302        return
303
304class WebCheckoutConfirmTransactionStudent(UtilityView, grok.View):
305    """ Request webservice view for the WebCheckout gateway
306    """
307    grok.context(INigeriaStudentOnlinePayment)
308    grok.name('confirm_transaction')
309    grok.require('waeup.payStudent')
310
311    merchant_code = None
312    gateway_host = None
313    gateway_url = None
314    https = True
315    mac = ''
316
317    def update(self):
318        if not webcheckout_module_activated(
319            self.context.student.current_session, self.context):
320            return
321        if self.context.p_state in ('paid', 'waived', 'scholarship'):
322            self.flash(_('This ticket has already been paid.'), type='danger')
323            return
324        student = self.context.student
325        success, msg, log = confirm_transaction(
326            self.context,
327            self.merchant_code,
328            self.gateway_host,
329            self.gateway_url,
330            self.https,
331            self.mac)
332        student.writeLogMessage(self, log)
333        if not success:
334            self.flash(msg, type='danger')
335            return
336        write_payments_log(student.student_id, self.context)
337        flashtype, msg, log = self.context.doAfterStudentPayment()
338        if log is not None:
339            student.writeLogMessage(self, log)
340        self.flash(msg, type=flashtype)
341        return
342
343    def render(self):
344        self.redirect(self.url(self.context, '@@index'))
345        return
346
Note: See TracBrowser for help on using the repository browser.