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

Last change on this file since 17455 was 17455, checked in by Henrik Bettermann, 19 months ago

Use correct filenames.

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