source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/paydirectbrowser.py @ 16575

Last change on this file since 16575 was 16555, checked in by Henrik Bettermann, 3 years ago

Add Interswitch surcharge before booking is being created.

  • Property svn:keywords set to Id
File size: 11.2 KB
Line 
1## $Id: paydirectbrowser.py 16555 2021-07-18 21:14:29Z 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    query_interswitch, write_payments_log,
36    fetch_booking_details, create_paydirect_booking)
37from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
38from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
39from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
40from kofacustom.nigeria.interswitch.tests import (
41    PAYDIRECT_URL, PAYDIRECT_HOST, MERCHANT_ID)
42from kofacustom.nigeria.interfaces import MessageFactory as _
43
44GATEWAY_AMT = 300.0
45
46grok.templatedir('browser_templates')
47
48def paydirect_module_activated(session, payment):
49    if payment.r_company and payment.r_company != 'interswitch':
50        return False
51    try:
52        return getattr(grok.getSite()['configuration'][str(session)],
53            'interswitch_paydirect_enabled', False)
54    except KeyError:
55        session = datetime.now().year
56        try:
57            return getattr(grok.getSite()['configuration'][str(session)],
58                'interswitch_paydirect_enabled', False)
59        except KeyError:
60            return False
61
62class PAYDirectActionButtonStudent(ManageActionButton):
63    grok.order(2)
64    grok.context(INigeriaOnlinePayment)
65    grok.view(OPDPStudent)
66    grok.require('waeup.payStudent')
67    icon = 'actionicon_pay.png'
68    text = _('Pay via Interswitch PAYDirect')
69    target = 'paydirect'
70
71    @property
72    def target_url(self):
73        if not paydirect_module_activated(
74            self.context.student.current_session, self.context):
75            return ''
76        if self.context.p_state == 'paid':
77            return ''
78        return self.view.url(self.view.context, self.target)
79
80class PAYDirectActionButtonApplicant(PAYDirectActionButtonStudent):
81    grok.view(OPDPApplicant)
82    grok.require('waeup.payApplicant')
83
84    @property
85    def target_url(self):
86        if not paydirect_module_activated(
87            self.context.__parent__.__parent__.year, self.context):
88            return ''
89        if self.context.p_state != 'unpaid':
90            return ''
91        return self.view.url(self.view.context, self.target)
92
93class PAYDirectPageStudent(KofaFormPage):
94    """Inform student how to proceed
95    """
96    grok.context(INigeriaStudentOnlinePayment)
97    grok.template('paydirect')
98    grok.name('paydirect')
99    grok.require('waeup.payStudent')
100    label = _('Requery Interswitch PAYDirect History')
101
102    gateway_amt = GATEWAY_AMT
103    merchant_id = MERCHANT_ID
104    gateway_url = PAYDIRECT_URL
105    gateway_host = PAYDIRECT_HOST
106    https = True
107
108    def init_update(self):
109        if self.context.p_state == 'paid':
110            return _("Payment ticket can't be used again.")
111        if self.context.r_company and self.context.r_company != 'interswitch':
112            return _("Payment ticket has been used for another payment gateway.")
113        self.ref_number = self.merchant_id + self.context.p_id[1:]
114        # Create a PAYDirect Booking
115        result_xml = create_paydirect_booking(
116            self.merchant_id, self.context, self.gateway_host,
117            self.gateway_url, True)
118        if result_xml.startswith('Connection error'):
119            IPayer(self.context).payer.writeLogMessage(self, result_xml)
120            return result_xml
121        doc=parseString(result_xml)
122        if not doc.getElementsByTagName('ResponseCode'):
123            IPayer(self.context).payer.writeLogMessage(self, 'invalid callback')
124            return _('Invalid callback from Interswitch')
125        rc = doc.getElementsByTagName('ResponseCode')[0].firstChild.data
126        IPayer(self.context).payer.writeLogMessage(self, 'response code: %s' % rc)
127        if rc not in ('100', '108'):
128            return 'Error response code from Interswitch: %s' % rc
129        return
130
131    def update(self):
132        if not paydirect_module_activated(
133            self.context.student.current_session, self.context):
134            self.flash(_('Forbidden'), type='danger')
135            self.redirect(self.url(self.context, '@@index'))
136            return
137        # Already now it becomes an Interswitch payment. We set the net amount
138        # and add the gateway amount.
139        if not self.context.r_company:
140            self.context.net_amt = self.context.amount_auth
141            self.context.amount_auth += self.gateway_amt
142            self.context.gateway_amt = self.gateway_amt
143            self.context.r_company = u'interswitch'
144        error = self.init_update()
145        if error:
146            self.flash(error, type='danger')
147            self.redirect(self.url(self.context, '@@index'))
148            return
149        return
150
151    @action('Requery now', style='primary')
152    def fetch(self):
153        success, msg, log = fetch_booking_details(
154            self.context, self.merchant_id, self.gateway_host,
155            self.gateway_url, self.https)
156        IPayer(self.context).payer.writeLogMessage(self, log)
157        if not success:
158            self.flash(msg, type='warning')
159            self.redirect(self.url(self.context, '@@index'))
160            return
161        write_payments_log(IPayer(self.context).id, self.context)
162        flashtype, msg, log = IPayer(self.context).doAfterPayment()
163        if log is not None:
164            IPayer(self.context).payer.writeLogMessage(self, log)
165        self.flash(msg, type=flashtype)
166        return
167
168class PAYDirectPageApplicant(PAYDirectPageStudent):
169    """ Inform applicant how to proceed.
170    """
171    grok.context(INigeriaApplicantOnlinePayment)
172    grok.require('waeup.payApplicant')
173
174    def update(self):
175        if not paydirect_module_activated(
176            self.context.__parent__.__parent__.year, self.context):
177            self.flash(_('Forbidden'), type='danger')
178            self.redirect(self.url(self.context, '@@index'))
179            return
180        # Already now it becomes an Interswitch payment. We set the net amount
181        # and add the gateway amount.
182        if not self.context.r_company:
183            self.context.net_amt = self.context.amount_auth
184            self.context.amount_auth += self.gateway_amt
185            self.context.gateway_amt = self.gateway_amt
186            self.context.r_company = u'interswitch'
187        error = self.init_update()
188        if error:
189            self.flash(error, type='danger')
190            self.redirect(self.url(self.context, '@@index'))
191        if self.context.__parent__.__parent__.expired \
192            and self.context.__parent__.__parent__.strict_deadline:
193            return _("Payment ticket can't be used. "
194                     "Application period has expired.")
195        return
196
197class StudentPaymentRefNumberSlipActionButton(ManageActionButton):
198    grok.order(1)
199    grok.context(INigeriaStudentOnlinePayment)
200    grok.view(PAYDirectPageStudent)
201    grok.require('waeup.viewStudent')
202    icon = 'actionicon_pdf.png'
203    text = _('Download reference number slip')
204    target = 'refnumberslip.pdf'
205
206    @property
207    def target_url(self):
208        if self.context.p_state == 'paid':
209            return ''
210        return self.view.url(self.view.context, self.target)
211
212class ApplicantPaymentRefNumberSlipActionButton(StudentPaymentRefNumberSlipActionButton):
213    grok.context(INigeriaApplicantOnlinePayment)
214    grok.view(PAYDirectPageApplicant)
215    grok.require('waeup.viewApplication')
216
217class StudentRefNumberSlip(UtilityView, grok.View):
218    """Deliver a PDF slip of the context.
219    """
220    grok.context(INigeriaStudentOnlinePayment)
221    grok.name('refnumberslip.pdf')
222    grok.require('waeup.viewStudent')
223    form_fields = grok.AutoFields(INigeriaOnlinePayment).select(
224        'amount_auth', 'p_category')
225    prefix = 'form'
226    omit_fields = (
227        'password', 'suspended', 'phone', 'date_of_birth',
228        'adm_code', 'sex', 'suspended_comment', 'current_level',
229        'flash_notice', 'parents_email', 'current_mode',
230        'entry_session', 'reg_number')
231    merchant_id = MERCHANT_ID
232
233    @property
234    def refnumber(self):
235        return self.merchant_id + self.context.p_id[1:]
236
237    @property
238    def label(self):
239        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
240        return translate(_('Interswitch PAYDirect Payment'),
241            'waeup.kofa', target_language=portal_language) \
242            + ' %s' % self.refnumber
243
244    @property
245    def note(self):
246        return """<br /><br />
247Go to your bank and make your PAYDirect payment with the reference number <strong>%s</strong>.
248""" % self.refnumber
249    def render(self):
250        if self.context.p_state == 'paid':
251            self.flash('Payment has already been made.')
252            self.redirect(self.url(self.context))
253            return
254        studentview = StudentBasePDFFormPage(self.context.student,
255            self.request, self.omit_fields)
256        students_utils = getUtility(IStudentsUtils)
257        return students_utils.renderPDF(self, 'refnumberslip.pdf',
258            self.context.student, studentview, note=self.note,
259            omit_fields=self.omit_fields)
260
261class ApplicantRefNumberSlip(StudentRefNumberSlip):
262    """Deliver a PDF slip of the context.
263    """
264    grok.context(INigeriaApplicantOnlinePayment)
265    grok.require('waeup.viewApplication')
266
267    @property
268    def note(self):
269        return """<br /><br />
270Go to your bank and make your PAYDirect payment with the reference number <strong>%s</strong>.
271""" % self.refnumber
272    def render(self):
273        if self.context.p_state == 'paid':
274            self.flash('Payment has already been made.')
275            self.redirect(self.url(self.context))
276            return
277        applicantview = ApplicantBaseDisplayFormPage(self.context.__parent__,
278            self.request)
279        students_utils = getUtility(IStudentsUtils)
280        return students_utils.renderPDF(self, 'refnumberslip.pdf',
281            self.context.__parent__, applicantview, note=self.note,
282            omit_fields=self.omit_fields)
Note: See TracBrowser for help on using the repository browser.