source: main/waeup.aaue/trunk/src/waeup/aaue/interswitch/browser.py @ 17442

Last change on this file since 17442 was 17442, checked in by Henrik Bettermann, 15 months ago

Simplify payment slip.

  • Property svn:executable set to *
File size: 17.7 KB
Line 
1    # -*- coding: utf-8 -*-
2## $Id: browser.py 17429 2023-06-05 04:34:30Z henrik $
3##
4## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
5## This program is free software; you can redistribute it and/or modify
6## it under the terms of the GNU General Public License as published by
7## the Free Software Foundation; either version 2 of the License, or
8## (at your option) any later version.
9##
10## This program is distributed in the hope that it will be useful,
11## but WITHOUT ANY WARRANTY; without even the implied warranty ofself.context.amount_auth
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13## GNU General Public License for more details.
14##
15## You should have received a copy of the GNU General Public License
16## along with this program; if not, write to the Free Software
17## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18##
19import httplib
20import hashlib
21import grok
22import os
23import csv
24from xml.dom import minidom
25from zope.interface import Interface
26from zope.component import queryAdapter
27from waeup.kofa.interfaces import CLEARED
28from kofacustom.nigeria.interswitch.browser import (
29    InterswitchPaymentRequestWebservicePageStudent,
30    InterswitchPaymentRequestWebservicePageApplicant,
31    InterswitchPaymentVerifyWebservicePageApplicant,
32    InterswitchPaymentVerifyWebservicePageStudent,
33    InterswitchPageStudent, InterswitchPageApplicant,
34    module_activated,
35    )
36from waeup.aaue.students.interfaces import ICustomStudentOnlinePayment
37from waeup.aaue.applicants.interfaces import ICustomApplicantOnlinePayment
38from waeup.aaue.interfaces import MessageFactory as _
39
40PRODUCT_ID = '5845'
41SITE_NAME = 'aaue.waeup.org'
42PROVIDER_ACCT = '0200244434'
43PROVIDER_BANK_ID = '11'
44PROVIDER_ITEM_NAME = 'WAeAC Portal Fee'
45INSTITUTION_NAME = 'AAU Ekpoma'
46CURRENCY = '566'
47GATEWAY_AMT = 200.0
48POST_ACTION = 'https://webpay.interswitchng.com/paydirect/pay'
49
50HOST = 'webpay.interswitchng.com'
51URL = '/paydirect/api/v1/gettransaction.json'
52HTTPS = True
53MAC = '9718FA00B0F5070B388A9896ADCED9B2FB02D30F71E12E68BDADC63F6852A3496FF97D8A0F9DA9F753B911A49BB09BB87B55FD02046BD325C74C46C0123CF023'
54
55httplib.HTTPSConnection.debuglevel = 0
56
57BANK_ACCOUNTS = {
58    'edohis':   ('1222577132', '117'),
59    'union':    ('1019763348', '7'),
60    'sport':    ('1021941220', '7'),
61    'access':   ('1012688013', '123'),
62    'notebook': ('4011210501', '51'),
63    'library':  ('2000122995', '8'),
64    'fac1':     ('1022438743', '7'),
65    'fac2':     ('2000249757', '8'),
66    'fac3':     ('1012678566', '123'),
67    'acceptance': ('2000249757', '8'),
68    'matricgown': ('2000249757', '8'),
69    'lapel':      ('2000249757', '8'),
70    }
71
72FEE_NAMES = {
73    'edohis':     'Edo State Health Insurance Scheme',
74    'union':      'Student Union Dues',
75    'sport':      'Sport Development Fee',
76    'access':     'Access Card Fee',
77    'notebook':   'Branded Notebook',
78    'library':    'Library Development Fee',
79    'tuition':    'Tuition',
80    'acceptance': 'Acceptance Fee',
81    'matricgown': 'Matriculation Gown Fee',
82    'lapel':      'File/Lapel Fee',
83    'lmsplus':    'LMS Plus Fee',
84    'nuga':       'NUGA Fee',
85    }
86
87schoolfees_path = os.path.join(
88    os.path.dirname(__file__), '../students/schoolfees.csv')
89reader = csv.DictReader(open(schoolfees_path, 'rb'))
90SCHOOLFEES = {item['code']:item for item in reader}
91
92acceptancefees_path = os.path.join(
93    os.path.dirname(__file__), '../students/acceptancefees.csv')
94reader = csv.DictReader(open(acceptancefees_path, 'rb'))
95ACCEPTANCEFEES = {item['code']:item for item in reader}
96
97class CustomInterswitchPageApplicant(InterswitchPageApplicant):
98    """ View which sends a POST request to the Interswitch
99    CollegePAY payment gateway.
100
101    So far only PT application has been configured.
102    """
103    grok.context(ICustomApplicantOnlinePayment)
104    action = POST_ACTION
105    site_name = SITE_NAME
106    currency = CURRENCY
107    provider_bank_id = PROVIDER_BANK_ID
108    provider_acct = PROVIDER_ACCT
109    pay_item_id = '101'
110    product_id = PRODUCT_ID
111
112    def update(self):
113        if not module_activated(
114            self.context.__parent__.__parent__.year, self.context):
115            self.flash(_('Forbidden'), type='danger')
116            self.redirect(self.url(self.context, '@@index'))
117            return
118        error = self.init_update()
119        if error:
120            self.flash(error, type='danger')
121            self.redirect(self.url(self.context, '@@index'))
122            return
123        # Already now it becomes an Interswitch payment. We set the net amount
124        # and add the gateway amount.
125        if not self.context.r_company:
126            self.context.net_amt = self.context.amount_auth
127            self.context.amount_auth += GATEWAY_AMT
128            self.context.gateway_amt = GATEWAY_AMT
129            self.context.r_company = u'interswitch'
130        xmldict = {}
131        provider_amt = 2000.0
132        if self.applicant.applicant_id.startswith('trans'):
133            provider_amt = 3000.0
134        xmldict['institution_acct'] = '00000000'
135        xmldict['institution_bank_id'] = '00'
136        xmldict['detail_ref'] = self.context.p_id
137        xmldict['provider_amt'] = 100 * provider_amt
138        xmldict['provider_acct'] = PROVIDER_ACCT
139        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
140        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
141        xmldict['institution_item_name'] = self.context.category
142        xmldict['institution_name'] = INSTITUTION_NAME
143        xmldict['institution_amt'] = 100 * self.context.net_amt
144        if not self.context.provider_amt:
145            self.context.provider_amt = provider_amt
146            self.context.amount_auth += provider_amt
147        xmltext = """<payment_item_detail>
148<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s">
149<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
150<item_detail item_id="2" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
151</item_details>
152</payment_item_detail>""" % xmldict
153        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
154        xmlitems = ''
155        xmldoc = minidom.parseString(xmltext)
156        itemlist = xmldoc.getElementsByTagName('item_detail')
157        for s in itemlist:
158            xmlitems += "%s (%s %s, %s (%s)),  " % (
159                s.attributes['item_name'].value,
160                u'\u20a6',
161                int(s.attributes['item_amt'].value)/100,
162                s.attributes['acct_num'].value,
163                s.attributes['bank_id'].value,
164                )
165        self.context.p_split_data = xmlitems
166        self.amount_auth = int(100 * self.context.amount_auth)
167        hashargs = (
168            self.context.p_id +
169            PRODUCT_ID +
170            self.pay_item_id +
171            str(int(self.amount_auth)) +
172            self.site_redirect_url +
173            MAC)
174        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
175        return
176
177class CustomInterswitchPageStudent(InterswitchPageStudent):
178    """ View which sends a POST request to the Interswitch
179    CollegePAY payment gateway.
180    """
181    grok.context(ICustomStudentOnlinePayment)
182    action = POST_ACTION
183    site_name = SITE_NAME
184    currency = CURRENCY
185    pay_item_id = '101'
186    product_id = PRODUCT_ID
187
188    def update(self):
189        if not module_activated(
190            self.context.student.current_session, self.context):
191            self.flash(_('Forbidden'), type='danger')
192            self.redirect(self.url(self.context, '@@index'))
193            return
194        error = self.init_update()
195        if error:
196            self.flash(error, type='danger')
197            self.redirect(self.url(self.context, '@@index'))
198            return
199        student = self.student
200        category = self.context.p_category
201        # To guarantee that cleared students pay both acceptance fee
202        # and school fees, the page can only be accessed
203        # for school fee payments if acceptance/clearance fee has
204        # been successfully queried/paid beforehand. This
205        # requirement applies to students in state 'cleared' and
206        # entry_session greater than 2012 only.
207        if self.context.p_category.startswith('schoolfee') and \
208            student.state == CLEARED and \
209            student.entry_session > 2012:
210            acceptance_fee_paid = False
211            for ticket in student['payments'].values():
212                if ticket.p_state == 'paid' and \
213                    ticket.p_category.startswith('clearance'):
214                    acceptance_fee_paid = True
215                    break
216            if not acceptance_fee_paid:
217                self.flash(
218                    _('Please pay acceptance fee first.'), type="danger")
219                self.redirect(self.url(self.context, '@@index'))
220                return
221        # Already now it becomes an Interswitch payment. We set the net amount
222        # and add the gateway amount.
223        if not self.context.r_company:
224            self.context.net_amt = self.context.amount_auth
225            self.context.amount_auth += GATEWAY_AMT
226            self.context.gateway_amt = GATEWAY_AMT
227            self.context.r_company = u'interswitch'
228        xmldict = self.xmldict
229        # Provider data
230        xmldict['detail_ref'] = self.context.p_id
231        xmldict['provider_acct'] = PROVIDER_ACCT
232        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
233        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
234        # Institution data
235        xmldict['institution_acct'] = '00000000'
236        xmldict['institution_bank_id'] = '00'
237        provider_amt = 0.0
238        if category.startswith('clearance'):
239            provider_amt = 1500.0
240        elif category.startswith('hostel_maintenance'):
241            provider_amt = 1000.0
242        elif category in ('schoolfee', 'schoolfee_1', 'schoolfee_incl'):
243            provider_amt = 2500.0
244        xmldict['provider_amt'] = 100 * provider_amt
245        xmldict['institution_item_name'] = self.context.category
246        xmldict['institution_name'] = INSTITUTION_NAME
247        xmldict['institution_amt'] = 100 * self.context.net_amt
248        if not self.context.provider_amt:
249            self.context.provider_amt = provider_amt
250            self.context.amount_auth += provider_amt
251        xmltext = ''
252
253        # School fee
254        if category.startswith('schoolfee'):
255            # collect additional fees
256            if self.context.p_category in ('schoolfee_1', 'schoolfee_incl'):
257                xmltext = """<payment_item_detail>
258<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">""" % xmldict
259                item_id = 1
260                sorted_items = SCHOOLFEES[student.certcode].items()
261                sorted_items.insert(0, sorted_items.pop(4))
262                for item in sorted_items:
263                    try:
264                        item_amt = 100 * int(item[1])
265                        if self.context.p_category == 'schoolfee_1' and item[0] == 'tuition':
266                            item_amt /= 2
267                        acct_num = ''
268                        bank_id = ''
269                        item_name = ''
270                        # Find appropriate bank
271                        try:
272                            bank = BANK_ACCOUNTS[item[0]]
273                        except: # transfer to faculty account
274                            if student.faccode in ('FAG', 'FAT', 'FBM', 'FMLS', 'fac1'):
275                                bank = BANK_ACCOUNTS['fac1']
276                            elif student.faccode in ('FCS', 'FED', 'FES', 'FET'):
277                                bank = BANK_ACCOUNTS['fac2']
278                            elif student.faccode in ('FLS', 'FLW', 'FMS', 'FPS', 'FSS'):
279                                bank = BANK_ACCOUNTS['fac3']
280                        acct_num = bank[0]
281                        bank_id = bank[1]
282                        item_name = FEE_NAMES[item[0]]
283                        xmltext += """
284<item_detail item_id="%s" item_name="%s" item_amt="%d" bank_id="%s" acct_num="%s" />""" % (item_id, item_name, item_amt, bank_id, acct_num)
285                        item_id += 1
286                    except:
287                        pass
288                xmldict['item_id'] = item_id
289                xmltext += """
290<item_detail item_id="%(item_id)d" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
291</item_details>
292</payment_item_detail>""" % xmldict
293            # no additional charges, determine faculty bank only
294            else:
295                if student.faccode in ('FAG', 'FAT', 'FBM', 'FMLS', 'fac1'):
296                    bank = BANK_ACCOUNTS['fac1']
297                elif student.faccode in ('FCS', 'FED', 'FES', 'FET'):
298                    bank = BANK_ACCOUNTS['fac2']
299                elif student.faccode in ('FLS', 'FLW', 'FMS', 'FPS', 'FSS'):
300                    bank = BANK_ACCOUNTS['fac3']
301                xmldict['institution_acct'] = bank[0]
302                xmldict['institution_bank_id'] = bank[1]
303
304
305        # Clearance (acceptance) fee
306
307        if category.startswith('clearance'):
308            # collect additional fees
309            if self.context.p_category == 'clearance_incl':
310                xmltext = """<payment_item_detail>
311<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">""" % xmldict
312                item_id = 1
313                for item in ACCEPTANCEFEES[student.certcode].items():
314                    try:
315                        item_amt = 100 * int(item[1])
316                        bank = BANK_ACCOUNTS[item[0]]
317                        acct_num = bank[0]
318                        bank_id = bank[1]
319                        item_name = FEE_NAMES[item[0]]
320                        xmltext += """
321<item_detail item_id="%s" item_name="%s" item_amt="%d" bank_id="%s" acct_num="%s" />""" % (item_id, item_name, item_amt, bank_id, acct_num)
322                        item_id += 1
323                    except:
324                        pass
325                xmldict['item_id'] = item_id
326                xmltext += """
327<item_detail item_id="%(item_id)d" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
328</item_details>
329</payment_item_detail>""" % xmldict
330            # no additional charges, determine faculty bank only
331            else:
332                bank = BANK_ACCOUNTS['acceptance']
333                xmldict['institution_acct'] = bank[0]
334                xmldict['institution_bank_id'] = bank[1]
335
336        # Other fees
337
338
339        if not xmltext and provider_amt == 0:
340            xmltext = """<payment_item_detail>
341<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
342<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
343</item_details>
344</payment_item_detail>""" % xmldict
345        elif not xmltext:
346            xmltext = """<payment_item_detail>
347<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
348<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
349<item_detail item_id="2" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
350</item_details>
351</payment_item_detail>""" % xmldict
352        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
353        xmlitems = ''
354        xmldoc = minidom.parseString(xmltext)
355        itemlist = xmldoc.getElementsByTagName('item_detail')
356        for s in itemlist:
357            xmlitems += "%s (%s %s, %s (%s)),  " % (
358                s.attributes['item_name'].value,
359                u'\u20a6',
360                int(s.attributes['item_amt'].value)/100,
361                s.attributes['acct_num'].value,
362                s.attributes['bank_id'].value,
363                )
364        self.context.p_split_data = xmlitems
365        self.context.provider_amt = provider_amt
366        self.amount_auth = int(100 * self.context.amount_auth)
367        hashargs = (
368            self.context.p_id +
369            PRODUCT_ID +
370            self.pay_item_id +
371            str(int(self.amount_auth)) +
372            self.site_redirect_url +
373            MAC)
374        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
375        return
376
377
378class CustomInterswitchPaymentRequestWebservicePageApplicant(
379    InterswitchPaymentRequestWebservicePageApplicant):
380    """Request webservice view for the CollegePAY gateway
381    """
382    grok.context(ICustomApplicantOnlinePayment)
383    gateway_host = HOST
384    gateway_url = URL
385    https = HTTPS
386
387class CustomInterswitchPaymentVerifyWebservicePageApplicant(
388    InterswitchPaymentVerifyWebservicePageApplicant):
389    """Payment verify view for the CollegePAY gateway
390    """
391    grok.context(ICustomApplicantOnlinePayment)
392    gateway_host = HOST
393    gateway_url = URL
394    https = HTTPS
395    mac = MAC
396    product_id = PRODUCT_ID
397
398class CustomInterswitchPaymentRequestWebservicePageStudent(
399    InterswitchPaymentRequestWebservicePageStudent):
400    """Request webservice view for the CollegePAY gateway
401    """
402    grok.context(ICustomStudentOnlinePayment)
403    gateway_host = HOST
404    gateway_url = URL
405    https = HTTPS
406    mac = MAC
407    product_id = PRODUCT_ID
408
409class CustomInterswitchPaymentVerifyWebservicePageStudent(
410    InterswitchPaymentVerifyWebservicePageStudent):
411    """Payment verify view for the CollegePAY gateway
412    """
413    grok.context(ICustomStudentOnlinePayment)
414    gateway_host = HOST
415    gateway_url = URL
416    https = HTTPS
417    mac = MAC
418    product_id = PRODUCT_ID
Note: See TracBrowser for help on using the repository browser.