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

Last change on this file since 17609 was 17248, checked in by Henrik Bettermann, 23 months ago

Enable Paypal only for USD payments.
Enable all other gateway services only for NGN payments.

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