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

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

Use IPayer adapter.

  • Property svn:keywords set to Id
File size: 11.3 KB
Line 
1## $Id: paydirectbrowser.py 16528 2021-07-06 05:52:21Z 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        if not self.context.r_company:
116            result_xml = create_paydirect_booking(
117                self.merchant_id, self.context, self.gateway_host,
118                self.gateway_url, True)
119            if result_xml.startswith('Connection error'):
120                IPayer(self.context).payer.writeLogMessage(self, result_xml)
121                return result_xml
122            doc=parseString(result_xml)
123            if not doc.getElementsByTagName('ResponseCode'):
124                IPayer(self.context).payer.writeLogMessage(self, 'invalid callback')
125                return _('Invalid callback from Interswitch')
126            rc = doc.getElementsByTagName('ResponseCode')[0].firstChild.data
127            IPayer(self.context).payer.writeLogMessage(self, 'response code: %s' % rc)
128            if rc not in ('100', '108'):
129                return 'Error response code from Interswitch: %s' % rc
130        return
131
132    def update(self):
133        if not paydirect_module_activated(
134            self.context.student.current_session, self.context):
135            self.flash(_('Forbidden'), type='danger')
136            self.redirect(self.url(self.context, '@@index'))
137            return
138        error = self.init_update()
139        if error:
140            self.flash(error, type='danger')
141            self.redirect(self.url(self.context, '@@index'))
142            return
143        # Already now it becomes an Interswitch payment. We set the net amount
144        # and add the gateway amount.
145        if not self.context.r_company:
146            self.context.net_amt = self.context.amount_auth
147            self.context.amount_auth += self.gateway_amt
148            self.context.gateway_amt = self.gateway_amt
149            self.context.r_company = u'interswitch'
150        return
151
152    @action('Requery now', style='primary')
153    def fetch(self):
154        success, msg, log = fetch_booking_details(
155            self.context, self.merchant_id, self.gateway_host,
156            self.gateway_url, self.https)
157        IPayer(self.context).payer.writeLogMessage(self, log)
158        if not success:
159            self.flash(msg, type='warning')
160            self.redirect(self.url(self.context, '@@index'))
161            return
162        write_payments_log(IPayer(self.context).id, self.context)
163        flashtype, msg, log = IPayer(self.context).doAfterPayment()
164        if log is not None:
165            IPayer(self.context).payer.writeLogMessage(self, log)
166        self.flash(msg, type=flashtype)
167        return
168
169class PAYDirectPageApplicant(PAYDirectPageStudent):
170    """ Inform applicant how to proceed.
171    """
172    grok.context(INigeriaApplicantOnlinePayment)
173    grok.require('waeup.payApplicant')
174
175    def update(self):
176        if not paydirect_module_activated(
177            self.context.__parent__.__parent__.year, self.context):
178            self.flash(_('Forbidden'), type='danger')
179            self.redirect(self.url(self.context, '@@index'))
180            return
181        error = self.init_update()
182        if error:
183            self.flash(error, type='danger')
184            self.redirect(self.url(self.context, '@@index'))
185        if self.context.__parent__.__parent__.expired \
186            and self.context.__parent__.__parent__.strict_deadline:
187            return _("Payment ticket can't be used. "
188                     "Application period has expired.")
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        return
197
198class StudentPaymentRefNumberSlipActionButton(ManageActionButton):
199    grok.order(1)
200    grok.context(INigeriaStudentOnlinePayment)
201    grok.view(PAYDirectPageStudent)
202    grok.require('waeup.viewStudent')
203    icon = 'actionicon_pdf.png'
204    text = _('Download reference number slip')
205    target = 'refnumberslip.pdf'
206
207    @property
208    def target_url(self):
209        if self.context.p_state == 'paid':
210            return ''
211        return self.view.url(self.view.context, self.target)
212
213class ApplicantPaymentRefNumberSlipActionButton(StudentPaymentRefNumberSlipActionButton):
214    grok.context(INigeriaApplicantOnlinePayment)
215    grok.view(PAYDirectPageApplicant)
216    grok.require('waeup.viewApplication')
217
218class StudentRefNumberSlip(UtilityView, grok.View):
219    """Deliver a PDF slip of the context.
220    """
221    grok.context(INigeriaStudentOnlinePayment)
222    grok.name('refnumberslip.pdf')
223    grok.require('waeup.viewStudent')
224    form_fields = grok.AutoFields(INigeriaOnlinePayment).select(
225        'amount_auth', 'p_category')
226    prefix = 'form'
227    omit_fields = (
228        'password', 'suspended', 'phone', 'date_of_birth',
229        'adm_code', 'sex', 'suspended_comment', 'current_level',
230        'flash_notice', 'parents_email', 'current_mode',
231        'entry_session', 'reg_number')
232    merchant_id = MERCHANT_ID
233
234    @property
235    def refnumber(self):
236        return self.merchant_id + self.context.p_id[1:]
237
238    @property
239    def label(self):
240        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
241        return translate(_('Interswitch PAYDirect Payment'),
242            'waeup.kofa', target_language=portal_language) \
243            + ' %s' % self.refnumber
244
245    @property
246    def note(self):
247        return """<br /><br />
248Go to your bank and make your PAYDirect payment with the reference number <strong>%s</strong>.
249""" % self.refnumber
250    def render(self):
251        if self.context.p_state == 'paid':
252            self.flash('Payment has already been made.')
253            self.redirect(self.url(self.context))
254            return
255        studentview = StudentBasePDFFormPage(self.context.student,
256            self.request, self.omit_fields)
257        students_utils = getUtility(IStudentsUtils)
258        return students_utils.renderPDF(self, 'refnumberslip.pdf',
259            self.context.student, studentview, note=self.note,
260            omit_fields=self.omit_fields)
261
262class ApplicantRefNumberSlip(StudentRefNumberSlip):
263    """Deliver a PDF slip of the context.
264    """
265    grok.context(INigeriaApplicantOnlinePayment)
266    grok.require('waeup.viewApplication')
267
268    @property
269    def note(self):
270        return """<br /><br />
271Go to your bank and make your PAYDirect payment with the reference number <strong>%s</strong>.
272""" % self.refnumber
273    def render(self):
274        if self.context.p_state == 'paid':
275            self.flash('Payment has already been made.')
276            self.redirect(self.url(self.context))
277            return
278        applicantview = ApplicantBaseDisplayFormPage(self.context.__parent__,
279            self.request)
280        students_utils = getUtility(IStudentsUtils)
281        return students_utils.renderPDF(self, 'refnumberslip.pdf',
282            self.context.__parent__, applicantview, note=self.note,
283            omit_fields=self.omit_fields)
Note: See TracBrowser for help on using the repository browser.