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

Last change on this file 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
RevLine 
[16484]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
[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):
[17248]49    if payment.p_currency != 'NGN':
50        return False
[16484]51    if payment.r_company and payment.r_company != 'interswitch':
52        return False
53    try:
54        return getattr(grok.getSite()['configuration'][str(session)],
[16496]55            'interswitch_paydirect_enabled', False)
[16484]56    except KeyError:
57        session = datetime.now().year
58        try:
59            return getattr(grok.getSite()['configuration'][str(session)],
[16496]60                'interswitch_paydirect_enabled', False)
[16484]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
[16587]110    @property
111    def item_code(self):
112        return '01' # for testing
113
[16484]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:]
[16487]120        # Create a PAYDirect Booking
[16555]121        result_xml = create_paydirect_booking(
[16587]122            self.merchant_id, self.context, self.item_code, self.gateway_host,
[16555]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
[16484]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'
[16555]150        error = self.init_update()
151        if error:
152            self.flash(error, type='danger')
153            self.redirect(self.url(self.context, '@@index'))
154            return
[16484]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)
[16528]162        IPayer(self.context).payer.writeLogMessage(self, log)
[16484]163        if not success:
164            self.flash(msg, type='warning')
165            self.redirect(self.url(self.context, '@@index'))
166            return
[16528]167        write_payments_log(IPayer(self.context).id, self.context)
168        flashtype, msg, log = IPayer(self.context).doAfterPayment()
[16484]169        if log is not None:
[16528]170            IPayer(self.context).payer.writeLogMessage(self, log)
[16484]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
[16555]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'
[16484]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
[16487]203class StudentPaymentRefNumberSlipActionButton(ManageActionButton):
204    grok.order(1)
205    grok.context(INigeriaStudentOnlinePayment)
[16484]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
[16487]218class ApplicantPaymentRefNumberSlipActionButton(StudentPaymentRefNumberSlipActionButton):
219    grok.context(INigeriaApplicantOnlinePayment)
220    grok.view(PAYDirectPageApplicant)
221    grok.require('waeup.viewApplication')
222
223class StudentRefNumberSlip(UtilityView, grok.View):
[16484]224    """Deliver a PDF slip of the context.
225    """
[16487]226    grok.context(INigeriaStudentOnlinePayment)
[16484]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)
[16487]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.