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

Last change on this file since 17434 was 17434, checked in by Henrik Bettermann, 17 months ago

Acceptance fee must be paid before school fee payment.

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