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

Last change on this file since 17227 was 17227, checked in by Henrik Bettermann, 2 years ago

Enable account splitting.

  • Property svn:executable set to *
File size: 12.9 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
270
271    def update(self):
272        if not webcheckout_module_activated(
273            self.context.__parent__.__parent__.year, self.context):
274            return
275        if self.context.p_state == 'paid':
276            self.flash(_('This ticket has already been paid.'), type='danger')
277            return
278        applicant = self.context.__parent__
279        success, msg, log = confirm_transaction(
280            self.context,
281            self.merchant_code,
282            self.gateway_host,
283            self.gateway_url,
284            self.https)
285        applicant.writeLogMessage(self, log)
286        if not success:
287            self.flash(msg, type='danger')
288            return
289        write_payments_log(applicant.applicant_id, self.context)
290        flashtype, msg, log = self.context.doAfterApplicantPayment()
291        if log is not None:
292            applicant.writeLogMessage(self, log)
293        self.flash(msg, type=flashtype)
294        return
295
296    def render(self):
297        self.redirect(self.url(self.context.__parent__, 'edit'))
298        return
299
300class WebCheckoutConfirmTransactionStudent(UtilityView, grok.View):
301    """ Request webservice view for the WebCheckout gateway
302    """
303    grok.context(INigeriaStudentOnlinePayment)
304    grok.name('confirm_transaction')
305    grok.require('waeup.payStudent')
306
307    merchant_code = None
308    gateway_host = None
309    gateway_url = None
310    https = True
311
312    def update(self):
313        if not webcheckout_module_activated(
314            self.context.student.current_session, self.context):
315            return
316        if self.context.p_state in ('paid', 'waived', 'scholarship'):
317            self.flash(_('This ticket has already been paid.'), type='danger')
318            return
319        student = self.context.student
320        success, msg, log = confirm_transaction(
321            self.context,
322            self.merchant_code,
323            self.gateway_host,
324            self.gateway_url,
325            self.https)
326        student.writeLogMessage(self, log)
327        if not success:
328            self.flash(msg, type='danger')
329            return
330        write_payments_log(student.student_id, self.context)
331        flashtype, msg, log = self.context.doAfterStudentPayment()
332        if log is not None:
333            student.writeLogMessage(self, log)
334        self.flash(msg, type=flashtype)
335        return
336
337    def render(self):
338        self.redirect(self.url(self.context, '@@index'))
339        return
340
Note: See TracBrowser for help on using the repository browser.