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

Last change on this file since 17926 was 17926, checked in by Henrik Bettermann, 7 days ago

Add missing split_accounts declaration.

  • 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    split_accounts = None
151
152    def init_update(self):
153        if self.context.p_state == 'paid':
154            return _("Payment ticket can't be re-sent to WebCheckout.")
155        now = datetime.utcnow()
156        if self.context.creation_date.tzinfo is not None:
157            # That's bad. Please store timezone-naive datetimes only!
158            now = self.context.creation_date.tzinfo.localize(now)
159        time_delta = now - self.context.creation_date
160        if time_delta.days > 7:
161            return _("This payment ticket is too old. Please create a new ticket.")
162        if self.context.r_company and self.context.r_company != 'interswitch':
163            return _("Payment ticket has been used for another payment gateway.")
164        student = self.context.student
165        kofa_utils = getUtility(IKofaUtils)
166        student_utils = getUtility(IStudentsUtils)
167        if student_utils.samePaymentMade(student, self.context.p_category,
168            self.context.p_item, self.context.p_session):
169            return _("This type of payment has already been made.")
170        xmldict = {}
171        self.category = self.context.category
172        tz = kofa_utils.tzinfo
173        self.local_date_time = to_timezone(
174            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
175        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
176        self.student = student
177        return
178
179    def update(self):
180        if not webcheckout_module_activated(
181            self.context.student.current_session, self.context):
182            self.flash(_('Forbidden'), type='danger')
183            self.redirect(self.url(self.context, '@@index'))
184            return
185        error = self.init_update()
186        if error:
187            self.flash(error, type='danger')
188            self.redirect(self.url(self.context, '@@index'))
189        # Already now it becomes an Interswitch payment. We set the net amount
190        # and add the gateway amount.
191        if not self.context.r_company:
192            self.context.net_amt = self.context.amount_auth
193            self.context.amount_auth += self.gateway_amt
194            self.context.gateway_amt = self.gateway_amt
195            self.context.r_company = u'interswitch'
196        self.amount_auth = int(100 * self.context.amount_auth)
197        return
198
199class WebCheckoutPageApplicant(KofaPage):
200    """ View which sends a POST request to the Interswitch
201    WebCheckout payment gateway.
202    """
203    grok.context(INigeriaApplicantOnlinePayment)
204    grok.require('waeup.payApplicant')
205    grok.template('applicant_goto_webcheckout')
206    grok.name('webcheckout')
207    label = _('Submit data to Interswitch')
208    submit_button = _('Submit')
209
210    action = None
211    currency = None
212    pay_item_id = None
213    merchant_code = None
214    gateway_amt = GATEWAY_AMT
215    split_accounts = None
216
217    def init_update(self):
218        if self.context.p_state != 'unpaid':
219            return _("Payment ticket can't be re-sent to WebCheckout.")
220        if self.context.__parent__.__parent__.expired \
221            and self.context.__parent__.__parent__.strict_deadline:
222            return _("Payment ticket can't be sent to WebCheckout. "
223                     "Application period has expired.")
224        if self.context.r_company and self.context.r_company != 'interswitch':
225            return _("Payment ticket has been used for another payment gateway.")
226        tz = getUtility(IKofaUtils).tzinfo
227        time_delta = datetime.utcnow() - self.context.creation_date
228        if time_delta.days > 7:
229            return _("This payment ticket is too old. Please create a new ticket.")
230        self.applicant = self.context.__parent__
231        self.category = self.context.category
232        tz = getUtility(IKofaUtils).tzinfo
233        self.local_date_time = to_timezone(
234            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
235        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
236        return
237
238    def update(self):
239        if not webcheckout_module_activated(
240            self.context.__parent__.__parent__.year, self.context):
241            self.flash(_('Forbidden'), type='danger')
242            self.redirect(self.url(self.context, '@@index'))
243            return
244        error = self.init_update()
245        if error:
246            self.flash(error, type='danger')
247            self.redirect(self.url(self.context, '@@index'))
248        # Already now it becomes an Interswitch payment. We set the net amount
249        # and add the gateway amount.
250        if not self.context.r_company:
251            self.context.net_amt = self.context.amount_auth
252            self.context.amount_auth += self.gateway_amt
253            self.context.gateway_amt = self.gateway_amt
254            self.context.r_company = u'interswitch'
255        self.amount_auth = int(100 * self.context.amount_auth)
256        return
257
258# Webservice request views
259
260class WebCheckoutConfirmTransactionApplicant(UtilityView, grok.View):
261    """ Request webservice view for the WebCheckout gateway
262    """
263    grok.context(INigeriaApplicantOnlinePayment)
264    grok.name('confirm_transaction')
265    grok.require('waeup.payApplicant')
266
267    merchant_code = None
268    gateway_host = None
269    gateway_url = None
270    https = True
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.