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
RevLine 
[16484]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
[16487]23from xml.dom.minidom import parseString
[16484]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
[16528]28from waeup.kofa.payments.interfaces import IPayer
[16484]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
[16487]33from waeup.kofa.applicants.browser import ApplicantBaseDisplayFormPage
[16484]34from kofacustom.nigeria.interswitch.helpers import (
[16487]35    query_interswitch, write_payments_log,
36    fetch_booking_details, create_paydirect_booking)
[16484]37from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
38from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
39from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
[16487]40from kofacustom.nigeria.interswitch.tests import (
41    PAYDIRECT_URL, PAYDIRECT_HOST, MERCHANT_ID)
[16484]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)],
[16496]53            'interswitch_paydirect_enabled', False)
[16484]54    except KeyError:
55        session = datetime.now().year
56        try:
57            return getattr(grok.getSite()['configuration'][str(session)],
[16496]58                'interswitch_paydirect_enabled', False)
[16484]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:]
[16487]114        # Create a PAYDirect Booking
[16555]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
[16484]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'
[16555]144        error = self.init_update()
145        if error:
146            self.flash(error, type='danger')
147            self.redirect(self.url(self.context, '@@index'))
148            return
[16484]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)
[16528]156        IPayer(self.context).payer.writeLogMessage(self, log)
[16484]157        if not success:
158            self.flash(msg, type='warning')
159            self.redirect(self.url(self.context, '@@index'))
160            return
[16528]161        write_payments_log(IPayer(self.context).id, self.context)
162        flashtype, msg, log = IPayer(self.context).doAfterPayment()
[16484]163        if log is not None:
[16528]164            IPayer(self.context).payer.writeLogMessage(self, log)
[16484]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
[16555]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'
[16484]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
[16487]197class StudentPaymentRefNumberSlipActionButton(ManageActionButton):
198    grok.order(1)
199    grok.context(INigeriaStudentOnlinePayment)
[16484]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
[16487]212class ApplicantPaymentRefNumberSlipActionButton(StudentPaymentRefNumberSlipActionButton):
213    grok.context(INigeriaApplicantOnlinePayment)
214    grok.view(PAYDirectPageApplicant)
215    grok.require('waeup.viewApplication')
216
217class StudentRefNumberSlip(UtilityView, grok.View):
[16484]218    """Deliver a PDF slip of the context.
219    """
[16487]220    grok.context(INigeriaStudentOnlinePayment)
[16484]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)
[16487]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.