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

Last change on this file since 17234 was 17234, checked in by Henrik Bettermann, 21 months ago

Catch TypeError? #2.

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