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

Last change on this file since 13599 was 13586, checked in by Henrik Bettermann, 9 years ago

Add Interswitch verification components.

  • Property svn:keywords set to Id
File size: 21.0 KB
Line 
1## $Id: browser.py 13586 2016-01-11 08:58:32Z henrik $
2##
3## Copyright (C) 2012 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 httplib
19import hashlib
20import grok
21from zope.interface import Interface
22from zope.component import queryAdapter
23from waeup.kofa.interfaces import CLEARED
24from kofacustom.nigeria.interswitch.browser import (
25    InterswitchPaymentRequestWebservicePageStudent,
26    InterswitchPaymentRequestWebservicePageApplicant,
27    InterswitchPaymentVerifyWebservicePageApplicant,
28    InterswitchPaymentVerifyWebservicePageStudent,
29    InterswitchPageStudent, InterswitchPageApplicant,
30    )
31from waeup.aaue.students.interfaces import ICustomStudentOnlinePayment
32from waeup.aaue.applicants.interfaces import ICustomApplicantOnlinePayment
33from waeup.aaue.interfaces import MessageFactory as _
34
35PRODUCT_ID_PT = '5040'
36PRODUCT_ID_REGULAR = '5845'
37SITE_NAME = 'aaue.waeup.org'
38PROVIDER_ACCT = '2022866811'
39PROVIDER_BANK_ID = '8'
40PROVIDER_ITEM_NAME = 'BT Education'
41INSTITUTION_NAME = 'AAU Ekpoma'
42CURRENCY = '566'
43GATEWAY_AMT = 250.0
44POST_ACTION = 'https://webpay.interswitchng.com/paydirect/pay'
45
46HOST = 'webpay.interswitchng.com'
47URL = '/paydirect/api/v1/gettransaction.json'
48HTTPS = True
49MAC = '9718FA00B0F5070B388A9896ADCED9B2FB02D30F71E12E68BDADC63F6852A3496FF97D8A0F9DA9F753B911A49BB09BB87B55FD02046BD325C74C46C0123CF023'
50
51httplib.HTTPSConnection.debuglevel = 0
52
53
54def gateway_net_amt(fee):
55    if fee > GATEWAY_AMT:
56        return fee - GATEWAY_AMT
57    return 0.0
58
59def contr_agreement_applicant(applicant):
60    if applicant.__parent__.code[:2] in ('fp', 'pt'):
61        return 'first'
62    return 'second'
63
64def contr_agreement_student(student):
65    if student.current_mode == 'found' or student.current_mode.endswith('_pt'):
66        return 'first'
67    return 'second'
68
69class CustomInterswitchPageApplicant(InterswitchPageApplicant):
70    """ View which sends a POST request to the Interswitch
71    CollegePAY payment gateway.
72
73    So far only PT application has been configured.
74    """
75    grok.context(ICustomApplicantOnlinePayment)
76    action = POST_ACTION
77    site_name = SITE_NAME
78    currency = CURRENCY
79    mac = MAC
80
81    def update(self):
82
83        error = self.init_update()
84        if error:
85            self.flash(error, type='danger')
86            self.redirect(self.url(self.context, '@@index'))
87            return
88        if contr_agreement_applicant(self.context.__parent__) == 'first':
89            self.product_id = PRODUCT_ID_PT
90            self.pay_item_id = '101'
91        else:
92            self.product_id = PRODUCT_ID_REGULAR
93            self.pay_item_id = '109'
94        xmldict = {}
95        provider_amt = 1000.0
96        xmldict['institution_acct'] = '1010835352'
97        xmldict['institution_bank_id'] = '117'
98        xmldict['detail_ref'] = self.context.p_id
99        xmldict['provider_amt'] = 100 * provider_amt
100        xmldict['provider_acct'] = PROVIDER_ACCT
101        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
102        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
103        xmldict['institution_amt'] = 100 * (
104            self.context.amount_auth - provider_amt - GATEWAY_AMT)
105        xmldict['institution_item_name'] = self.category
106        xmldict['institution_name'] = INSTITUTION_NAME
107        # Interswitch amount is not part of the xml data
108        xmltext = """<payment_item_detail>
109<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s">
110<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" />
111<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" />
112</item_details>
113</payment_item_detail>""" % xmldict
114        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
115        self.context.provider_amt = provider_amt
116        self.context.gateway_amt = GATEWAY_AMT
117
118        hashargs = (
119            self.context.p_id +
120            self.product_id +
121            self.pay_item_id +
122            str(int(self.amount_auth)) +
123            self.site_redirect_url +
124            self.mac)
125        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
126
127        return
128
129class CustomInterswitchPageStudent(InterswitchPageStudent):
130    """ View which sends a POST request to the Interswitch
131    CollegePAY payment gateway.
132    """
133    grok.context(ICustomStudentOnlinePayment)
134    action = POST_ACTION
135    site_name = SITE_NAME
136    currency = CURRENCY
137    #pay_item_id = '101'
138    mac = MAC
139
140    def update(self):
141        error = self.init_update()
142
143        ######################################
144        #error = 'Sorry, Interswitch payments are temporarily disabled.'
145        ######################################
146
147        if error:
148            self.flash(error, type='danger')
149            self.redirect(self.url(self.context, '@@index'))
150            return
151
152        student = self.student
153        p_session = self.context.p_session
154        try:
155            academic_session = grok.getSite()['configuration'][str(p_session)]
156        except KeyError:
157            self.flash(_(u'Session configuration object is not available.'),
158                       type='danger')
159            self.redirect(self.url(self.context, '@@index'))
160            return
161        if contr_agreement_student(student) == 'first':
162            self.product_id = PRODUCT_ID_PT
163        else:
164            self.product_id = PRODUCT_ID_REGULAR
165
166        # To guarantee that cleared students pay both acceptance fee
167        # and school fees, the page can only be accessed
168        # for school fee payments if acceptance/clearance fee has
169        # been successfully queried/paid beforehand. This
170        # requirement applies to students in state 'cleared' and
171        # entry_session greater than 2013 only.
172        if self.context.p_category.startswith('schoolfee') and \
173            student.state == CLEARED and \
174            student.entry_session > 2012:
175            acceptance_fee_paid = False
176            for ticket in student['payments'].values():
177                if ticket.p_state == 'paid' and \
178                    ticket.p_category.startswith('clearance'):
179                    acceptance_fee_paid = True
180                    break
181            if not acceptance_fee_paid:
182                self.flash(
183                    _('Please pay acceptance fee first.'), type="danger")
184                self.redirect(self.url(self.context, '@@index'))
185                return
186
187        xmldict = self.xmldict
188        xmltext = ""
189        # Provider data
190        xmldict['detail_ref'] = self.context.p_id
191        xmldict['provider_acct'] = PROVIDER_ACCT
192        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
193        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
194        xmldict['institution_item_name'] = self.category
195        xmldict['institution_name'] = INSTITUTION_NAME
196        provider_amt = 0.0
197
198        # Schoolfee
199        if self.context.p_category.startswith('schoolfee'):
200            if contr_agreement_student(student) == 'first':
201                # First agreement
202                provider_amt = 1900.0
203                joint_venture_amt = 1100.0
204                aaue_share_amt = 1000.0
205                student_union_due_amt = gateway_net_amt(
206                    academic_session.union_fee)
207                student_welfare_assurance_amt = gateway_net_amt(
208                    academic_session.welfare_fee)
209                xmldict['institution_bank_id'] = '7'
210                xmldict['institution_acct'] = '1014847058'
211                if student.current_mode == 'found':
212                    self.pay_item_id = '103'
213                else:
214                    self.pay_item_id = '105'
215            else:
216                # Second agreement
217                provider_amt = 1500.0
218                joint_venture_amt = 1000.0
219                aaue_share_amt = 1500.0
220                student_union_due_amt = gateway_net_amt(
221                    academic_session.union_fee)
222                student_welfare_assurance_amt = gateway_net_amt(
223                    academic_session.welfare_fee)
224                xmldict['institution_bank_id'] = '117'
225                xmldict['institution_acct'] = '1010827641'
226                self.pay_item_id = '101'
227                if student.is_postgrad:
228                    xmldict['institution_bank_id'] = '51'
229                    xmldict['institution_acct'] = '5210006575'
230                    self.pay_item_id = '111'
231
232            xmldict['provider_amt'] = 100 * provider_amt
233            xmldict['joint_venture_amt'] = 100 * joint_venture_amt
234            xmldict['aaue_share_amt'] = 100 * aaue_share_amt
235            if self.context.p_category in ('schoolfee_incl', 'schoolfee_1'):
236                # Schoolfee including additional fees
237                xmldict['student_union_due_amt'] = 100 * student_union_due_amt
238                xmldict['student_welfare_assurance_amt'] = 100 * student_welfare_assurance_amt
239                xmldict['institution_amt'] = 100 * (
240                    gateway_net_amt(self.context.amount_auth)
241                    - provider_amt
242                    - joint_venture_amt
243                    - aaue_share_amt
244                    - student_union_due_amt
245                    - student_welfare_assurance_amt)
246                xmltext = """<payment_item_detail>
247<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
248<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" />
249<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" />
250<item_detail item_id="3" item_name="Joint Venture" item_amt="%(joint_venture_amt)d" bank_id="51" acct_num="5060023759" />
251<item_detail item_id="4" item_name="AAUE Share" item_amt="%(aaue_share_amt)d" bank_id="51" acct_num="5060020947" />
252<item_detail item_id="5" item_name="Student Union" item_amt="%(student_union_due_amt)d" bank_id="123" acct_num="1006360118" />
253<item_detail item_id="6" item_name="Student Welfare Assurance" item_amt="%(student_welfare_assurance_amt)d" bank_id="31" acct_num="1006407792" />
254</item_details>
255</payment_item_detail>""" % xmldict
256            else:
257                # Schoolfee without additional fees
258                xmldict['institution_amt'] = 100 * (
259                    gateway_net_amt(self.context.amount_auth)
260                    - provider_amt
261                    - joint_venture_amt
262                    - aaue_share_amt)
263                xmltext = """<payment_item_detail>
264<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
265<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" />
266<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" />
267<item_detail item_id="3" item_name="Joint Venture" item_amt="%(joint_venture_amt)d" bank_id="51" acct_num="5060023759" />
268<item_detail item_id="4" item_name="AAUE Share" item_amt="%(aaue_share_amt)d" bank_id="51" acct_num="5060020947" />
269</item_details>
270</payment_item_detail>""" % xmldict
271
272
273        # Clearance
274        elif self.context.p_category.startswith('clearance'):
275            if contr_agreement_student(student) == 'first':
276                # First agreement
277                if student.current_mode == 'found':
278                    self.pay_item_id = '102'
279                else:
280                    self.pay_item_id = '104'
281                xmldict['institution_acct'] = '1014066976'
282                xmldict['institution_bank_id'] = '117'
283            else:
284                # Second agreement
285                self.pay_item_id = '102'
286                xmldict['institution_acct'] = '1010827641'
287                xmldict['institution_bank_id'] = '117'
288                if student.is_postgrad:
289                    xmldict['institution_bank_id'] = '51'
290                    xmldict['institution_acct'] = '5210006575'
291                    self.pay_item_id = '110'
292
293            if self.context.p_category.endswith('_incl'):
294                # Clearance including additional fees
295                gown_fee_amt = gateway_net_amt(academic_session.matric_gown_fee)
296                aaue_lf_fee_amt = gateway_net_amt(academic_session.lapel_fee)
297                xmldict['gown_fee_amt'] = 100 * gown_fee_amt
298                xmldict['aaue_lf_fee_amt'] = 100 * aaue_lf_fee_amt
299                xmldict['institution_amt'] = 100 * (
300                    gateway_net_amt(self.context.amount_auth)
301                    - gown_fee_amt
302                    - aaue_lf_fee_amt)
303                xmltext = """<payment_item_detail>
304<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
305<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" />
306<item_detail item_id="2" item_name="Matriculation Gown Fee" item_amt="%(gown_fee_amt)d" bank_id="51" acct_num="5060020947" />
307<item_detail item_id="3" item_name="AAU File-Lapel Fee" item_amt="%(aaue_lf_fee_amt)d" bank_id="51" acct_num="4010660109" />
308</item_details>
309</payment_item_detail>""" % xmldict
310
311            else:
312                # Clearance without additional fees
313                xmldict['institution_amt'] = 100 * (
314                    gateway_net_amt(self.context.amount_auth))
315                xmltext = """<payment_item_detail>
316<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
317<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" />
318</item_details>
319</payment_item_detail>""" % xmldict
320
321        # Union Dues
322        elif self.context.p_category == 'union':
323            self.pay_item_id = '103'
324            xmldict['institution_acct'] = '1006360118'
325            xmldict['institution_bank_id'] = '123'
326            xmldict['institution_amt'] = 100 * (
327                gateway_net_amt(self.context.amount_auth))
328            xmltext = """<payment_item_detail>
329<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
330<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" />
331</item_details>
332</payment_item_detail>""" % xmldict
333
334        # Lapel/File
335        elif self.context.p_category == 'lapel':
336            self.pay_item_id = '104'
337            xmldict['institution_acct'] = '4010660109'
338            xmldict['institution_bank_id'] = '51'
339            xmldict['institution_amt'] = 100 * (
340                gateway_net_amt(self.context.amount_auth))
341            xmltext = """<payment_item_detail>
342<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
343<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" />
344</item_details>
345</payment_item_detail>""" % xmldict
346
347        # Welfare Assurance
348        elif self.context.p_category == 'welfare':
349            self.pay_item_id = '105'
350            xmldict['institution_acct'] = '1006407792'
351            xmldict['institution_bank_id'] = '123'
352            xmldict['institution_amt'] = 100 * (
353                gateway_net_amt(self.context.amount_auth))
354            xmltext = """<payment_item_detail>
355<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
356<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" />
357</item_details>
358</payment_item_detail>""" % xmldict
359
360        # Matric Gown
361        elif self.context.p_category == 'matric_gown':
362            self.pay_item_id = '106'
363            xmldict['institution_acct'] = '5060023429'
364            xmldict['institution_bank_id'] = '51'
365            xmldict['institution_amt'] = 100 * (
366                gateway_net_amt(self.context.amount_auth))
367            xmltext = """<payment_item_detail>
368<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
369<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" />
370</item_details>
371</payment_item_detail>""" % xmldict
372
373        # Concessional
374        elif self.context.p_category == 'concessional':
375            self.pay_item_id = '107'
376            xmldict['institution_acct'] = '1010835352'
377            xmldict['institution_bank_id'] = '117'
378            xmldict['institution_amt'] = 100 * (
379                gateway_net_amt(self.context.amount_auth))
380            xmltext = """<payment_item_detail>
381<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
382<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" />
383</item_details>
384</payment_item_detail>""" % xmldict
385
386        # Hostel Maintenance
387        elif self.context.p_category == 'hostel_maintenance':
388            self.pay_item_id = '109'
389            xmldict['institution_acct'] = '1006406795'
390            xmldict['institution_bank_id'] = '123'
391            xmldict['institution_amt'] = 100 * (
392                gateway_net_amt(self.context.amount_auth))
393            xmltext = """<payment_item_detail>
394<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
395<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" />
396</item_details>
397</payment_item_detail>""" % xmldict
398
399        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
400        self.context.provider_amt = provider_amt
401        self.context.gateway_amt = self.amount_auth - gateway_net_amt(
402            self.amount_auth)
403        hashargs = (
404            self.context.p_id +
405            self.product_id +
406            self.pay_item_id +
407            str(int(self.amount_auth)) +
408            self.site_redirect_url +
409            self.mac)
410        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
411        return
412
413
414class CustomInterswitchPaymentRequestWebservicePageApplicant(
415    InterswitchPaymentRequestWebservicePageApplicant):
416    """Request webservice view for the CollegePAY gateway
417    """
418    grok.context(ICustomApplicantOnlinePayment)
419    gateway_host = HOST
420    gateway_url = URL
421    https = HTTPS
422    mac = MAC
423
424    @property
425    def product_id(self):
426        if contr_agreement_applicant(self.context.__parent__) == 'first':
427            return PRODUCT_ID_PT
428        return PRODUCT_ID_REGULAR
429
430class CustomInterswitchPaymentVerifyWebservicePageApplicant(
431    InterswitchPaymentVerifyWebservicePageApplicant):
432    """Payment verify view for the CollegePAY gateway
433    """
434    grok.context(ICustomApplicantOnlinePayment)
435    gateway_host = HOST
436    gateway_url = URL
437    https = HTTPS
438    mac = MAC
439
440    @property
441    def product_id(self):
442        if contr_agreement_applicant(self.context.__parent__) == 'first':
443            return PRODUCT_ID_PT
444        return PRODUCT_ID_REGULAR
445
446class CustomInterswitchPaymentRequestWebservicePageStudent(
447    InterswitchPaymentRequestWebservicePageStudent):
448    """Request webservice view for the CollegePAY gateway
449    """
450    grok.context(ICustomStudentOnlinePayment)
451    gateway_host = HOST
452    gateway_url = URL
453    https = HTTPS
454    mac = MAC
455
456    @property
457    def product_id(self):
458        if contr_agreement_student(self.context.student) == 'first':
459            return PRODUCT_ID_PT
460        return PRODUCT_ID_REGULAR
461
462class CustomInterswitchPaymentVerifyWebservicePageStudent(
463    InterswitchPaymentVerifyWebservicePageStudent):
464    """Payment verify view for the CollegePAY gateway
465    """
466    grok.context(ICustomStudentOnlinePayment)
467    gateway_host = HOST
468    gateway_url = URL
469    https = HTTPS
470    mac = MAC
471
472    @property
473    def product_id(self):
474        if contr_agreement_student(self.context.student) == 'first':
475            return PRODUCT_ID_PT
476        return PRODUCT_ID_REGULAR
Note: See TracBrowser for help on using the repository browser.