# -*- coding: utf-8 -*- ## $Id: browser.py 17429 2023-06-05 04:34:30Z henrik $ ## ## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty ofself.context.amount_auth ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## import httplib import hashlib import grok import os import csv from xml.dom import minidom from zope.interface import Interface from zope.component import queryAdapter from waeup.kofa.interfaces import CLEARED from kofacustom.nigeria.interswitch.browser import ( InterswitchPaymentRequestWebservicePageStudent, InterswitchPaymentRequestWebservicePageApplicant, InterswitchPaymentVerifyWebservicePageApplicant, InterswitchPaymentVerifyWebservicePageStudent, InterswitchPageStudent, InterswitchPageApplicant, module_activated, ) from waeup.aaue.students.interfaces import ICustomStudentOnlinePayment from waeup.aaue.applicants.interfaces import ICustomApplicantOnlinePayment from waeup.aaue.students.utils import SFEECHANGES from waeup.aaue.interfaces import MessageFactory as _ PRODUCT_ID = '5845' SITE_NAME = 'aaue.waeup.org' PROVIDER_ACCT = '0200244434' PROVIDER_BANK_ID = '11' PROVIDER_ITEM_NAME = 'WAeAC Portal Fee' INSTITUTION_NAME = 'AAU Ekpoma' CURRENCY = '566' GATEWAY_AMT = 200.0 POST_ACTION = 'https://webpay.interswitchng.com/paydirect/pay' HOST = 'webpay.interswitchng.com' URL = '/paydirect/api/v1/gettransaction.json' HTTPS = True MAC = '9718FA00B0F5070B388A9896ADCED9B2FB02D30F71E12E68BDADC63F6852A3496FF97D8A0F9DA9F753B911A49BB09BB87B55FD02046BD325C74C46C0123CF023' httplib.HTTPSConnection.debuglevel = 0 BANK_ACCOUNTS = { 'edohis': ('1222577132', '117'), 'union': ('1019763348', '7'), 'union_pt': ('0051005007', '31'), 'sport': ('1021941220', '7'), 'access': ('1012688013', '123'), 'notebook': ('4011210501', '51'), 'accredit': ('5060023412', '51'), 'library': ('2000122995', '8'), 'fac1': ('1022438743', '7'), 'fac2': ('2000249757', '8'), 'fac3': ('1012678566', '123'), 'matricgown':('2000249757', '8'), 'lapel': ('2000249757', '8'), 'lmsplus': ('5060023429', '51'), 'acceptance': ('2000249757', '8'), 'parttime': ('1012678566', '123'), 'ijmb': ('1012278272', '123'), 'hostel_maintenance': ('1006406795', '123'), 'bed_allocation': ('1006406795', '123'), 'late_registration': ('5210006575', '51'), 'ent_combined': ('6220029828', '51'), 'ent_registration_0': ('6220029828', '51'), 'ent_registration_1': ('6220029828', '51'), 'ent_registration_2': ('6220029828', '51'), 'ent_text_book_0': ('6220029828', '51'), 'ent_text_book_1': ('6220029828', '51'), 'ent_text_book_2': ('6220029828', '51'), 'gst_registration_1': ('1010893123', '117'), 'gst_registration_2': ('1010893123', '117'), 'gst_text_book_0': ('1010893123', '117'), 'gst_text_book_1': ('1010893123', '117'), 'gst_text_book_2': ('1010893123', '117'), 'gst_text_book_3': ('1010893123', '117'), 'postgrad': ('1010827641', '117'), } FEE_NAMES = { 'edohis': 'Edo State Health Insurance Scheme', 'union': 'Student Union Dues', 'union_pt': 'Student Union Dues Part-Time', 'sport': 'Sport Development Fee', 'access': 'Access Card Fee', 'notebook': 'Branded Notebook', 'accredit': 'Accreditation Fee', 'library': 'Library Development Fee', 'tuition': 'Tuition', 'acceptance': 'Acceptance Fee', 'matricgown': 'Matriculation Gown Fee', 'lapel': 'File/Lapel Fee', 'lmsplus': 'LMS Plus Fee', 'nuga': 'NUGA Fee', } SCHOOLFEES = dict() for year in SFEECHANGES: schoolfees_path = os.path.join( os.path.dirname(__file__), '../students/schoolfees_%s.csv' %year) reader = csv.DictReader(open(schoolfees_path, 'rb')) SCHOOLFEES[year] = {item['code']:item for item in reader} acceptancefees_path = os.path.join( os.path.dirname(__file__), '../students/acceptancefees.csv') reader = csv.DictReader(open(acceptancefees_path, 'rb')) ACCEPTANCEFEES = {item['code']:item for item in reader} class CustomInterswitchPageApplicant(InterswitchPageApplicant): """ View which sends a POST request to the Interswitch CollegePAY payment gateway. So far only PT application has been configured. """ grok.context(ICustomApplicantOnlinePayment) action = POST_ACTION site_name = SITE_NAME currency = CURRENCY provider_bank_id = PROVIDER_BANK_ID provider_acct = PROVIDER_ACCT pay_item_id = '101' product_id = PRODUCT_ID def update(self): if not module_activated( self.context.__parent__.__parent__.year, self.context): self.flash(_('Forbidden'), type='danger') self.redirect(self.url(self.context, '@@index')) return error = self.init_update() if error: self.flash(error, type='danger') self.redirect(self.url(self.context, '@@index')) return # Already now it becomes an Interswitch payment. We set the net amount # and add the gateway amount. if not self.context.r_company: self.context.net_amt = self.context.amount_auth self.context.amount_auth += GATEWAY_AMT self.context.gateway_amt = GATEWAY_AMT self.context.r_company = u'interswitch' xmldict = {} provider_amt = 2000.0 if self.applicant.__parent__.code in ('ver2019', 'send2019'): provider_amt = 0.0 elif self.applicant.__parent__.code.startswith('cert'): provider_amt = 3000.0 elif self.applicant.__parent__.code.startswith('trans'): provider_amt = 3000.0 xmldict['institution_acct'] = '2000249757' xmldict['institution_bank_id'] = '8' if self.applicant.applicant_id.startswith('dsh'): xmldict['institution_acct'] = '1014847058' xmldict['institution_bank_id'] = '7' if self.applicant.applicant_id.startswith('ijmbe'): xmldict['institution_acct'] = '1012278272' xmldict['institution_bank_id'] = '123' xmldict['detail_ref'] = self.context.p_id xmldict['provider_amt'] = 100 * provider_amt xmldict['provider_acct'] = PROVIDER_ACCT xmldict['provider_bank_id'] = PROVIDER_BANK_ID if 'alumni' in self.application_url(): xmldict['provider_acct'] = '0427773399' xmldict['provider_bank_id'] = '10' xmldict['provider_item_name'] = PROVIDER_ITEM_NAME xmldict['institution_item_name'] = self.context.category xmldict['institution_name'] = INSTITUTION_NAME xmldict['institution_amt'] = 100 * self.context.net_amt if not self.context.provider_amt: self.context.provider_amt = provider_amt self.context.amount_auth += provider_amt if provider_amt: xmltext = """ """ % xmldict else: xmltext = """ """ % xmldict # Overwrite above configuration if self.applicant.__parent__.code in ('cert1', 'cert2', 'cert6', 'cert9'): xmldict['institution_amt'] = 100 * self.context.net_amt - 100000 xmltext = """ """ % xmldict self.xml_data = """""" % xmltext xmlitems = '' xmldoc = minidom.parseString(xmltext) itemlist = xmldoc.getElementsByTagName('item_detail') for s in itemlist: xmlitems += "%s (%s %s, %s (%s)), " % ( s.attributes['item_name'].value, u'\u20a6', int(s.attributes['item_amt'].value)/100, s.attributes['acct_num'].value, s.attributes['bank_id'].value, ) self.context.p_split_data = xmlitems self.amount_auth = int(100 * self.context.amount_auth) hashargs = ( self.context.p_id + PRODUCT_ID + self.pay_item_id + str(int(self.amount_auth)) + self.site_redirect_url + MAC) self.hashvalue = hashlib.sha512(hashargs).hexdigest() return class CustomInterswitchPageStudent(InterswitchPageStudent): """ View which sends a POST request to the Interswitch CollegePAY payment gateway. """ grok.context(ICustomStudentOnlinePayment) action = POST_ACTION site_name = SITE_NAME currency = CURRENCY pay_item_id = '101' product_id = PRODUCT_ID def update(self): if not module_activated( self.context.student.current_session, self.context): self.flash(_('Forbidden'), type='danger') self.redirect(self.url(self.context, '@@index')) return error = self.init_update() if error: self.flash(error, type='danger') self.redirect(self.url(self.context, '@@index')) return student = self.student category = self.context.p_category # To guarantee that cleared students pay both acceptance fee # and school fees, the page can only be accessed # for school fee payments if acceptance/clearance fee has # been successfully queried/paid beforehand. This # requirement applies to students in state 'cleared' and # entry_session greater than 2012 only. if self.context.p_category.startswith('schoolfee') and \ student.state == CLEARED and \ student.entry_session > 2012: acceptance_fee_paid = False for ticket in student['payments'].values(): if ticket.p_state == 'paid' and \ ticket.p_category.startswith('clearance'): acceptance_fee_paid = True break if not acceptance_fee_paid: self.flash( _('Please pay acceptance fee first.'), type="danger") self.redirect(self.url(self.context, '@@index')) return # Already now it becomes an Interswitch payment. We set the net amount # and add the gateway amount. if not self.context.r_company: self.context.net_amt = self.context.amount_auth self.context.amount_auth += GATEWAY_AMT self.context.gateway_amt = GATEWAY_AMT self.context.r_company = u'interswitch' xmldict = self.xmldict # Provider data xmldict['detail_ref'] = self.context.p_id xmldict['provider_acct'] = PROVIDER_ACCT xmldict['provider_bank_id'] = PROVIDER_BANK_ID xmldict['provider_item_name'] = PROVIDER_ITEM_NAME # Institution data xmldict['institution_acct'] = '00000000' xmldict['institution_bank_id'] = '00' provider_amt = 0.0 if category.startswith('clearance'): provider_amt = 1500.0 elif category.startswith('hostel_maintenance'): provider_amt = 1000.0 elif category in ('schoolfee', 'schoolfee_1', 'schoolfee_incl'): provider_amt = 2500.0 xmldict['provider_amt'] = 100 * provider_amt xmldict['institution_item_name'] = self.context.category xmldict['institution_name'] = INSTITUTION_NAME xmldict['institution_amt'] = 100 * self.context.net_amt if not self.context.provider_amt: self.context.provider_amt = provider_amt self.context.amount_auth += provider_amt xmltext = '' # School fee if category.startswith('schoolfee'): # collect additional fees if self.context.p_category in ('schoolfee_1', 'schoolfee_incl'): xmltext = """ """ % xmldict item_id = 1 if student.entry_session < 2013: sorted_items = SCHOOLFEES[12][student.certcode].items() elif student.entry_session < 2014: sorted_items = SCHOOLFEES[13][student.certcode].items() elif student.entry_session < 2015: sorted_items = SCHOOLFEES[14][student.certcode].items() elif student.entry_session < 2020: sorted_items = SCHOOLFEES[15][student.certcode].items() elif student.entry_session < 2021: sorted_items = SCHOOLFEES[20][student.certcode].items() elif student.entry_session < 2022: sorted_items = SCHOOLFEES[21][student.certcode].items() elif student.entry_session < 2023: sorted_items = SCHOOLFEES[22][student.certcode].items() else: sorted_items = SCHOOLFEES[23][student.certcode].items() # Move tuition. We expect that tuition is the fourth element sorted_items.insert(0, sorted_items.pop(3)) for item in sorted_items: try: if item[1].startswith('100_') and student.state == CLEARED: # first year payment only item_amt = int(item[1].split('_')[1]) else: item_amt = int(item[1]) if self.context.p_category == 'schoolfee_1' and item[0] == 'tuition': item_amt /= 2 # Add non-indigenous fee and session specific penalty fees try: academic_session = grok.getSite()['configuration'][str(self.context.p_session)] except KeyError: self.flash( _('Session configuration object is missing.'), type="danger") self.redirect(self.url(self.context, '@@index')) return if student.is_postgrad and item[0] == 'tuition': item_amt += academic_session.penalty_pg if student.lga and not student.lga.startswith('edo') \ and student.entry_session < 2022: item_amt += 20000.0 else: item_amt += academic_session.penalty_ug item_amt *= 100 acct_num = '' bank_id = '' item_name = '' # Find appropriate bank try: bank = BANK_ACCOUNTS[item[0]] except: # transfer to faculty account if student.is_postgrad: bank = BANK_ACCOUNTS['postgrad'] elif student.current_mode in ( 'ug_pt', 'de_pt','dp_pt', 'de_dsh', 'ug_dsh'): bank = BANK_ACCOUNTS['parttime'] elif student.faccode in ('FAG', 'FAT', 'FBM', 'FMLS', 'fac1'): bank = BANK_ACCOUNTS['fac1'] elif student.faccode in ('FCS', 'FED', 'FES', 'FET'): bank = BANK_ACCOUNTS['fac2'] elif student.faccode in ('FLS', 'FLW', 'FMS', 'FPS', 'FSS'): bank = BANK_ACCOUNTS['fac3'] elif student.faccode in ('FP',): bank = BANK_ACCOUNTS['ijmb'] acct_num = bank[0] bank_id = bank[1] item_name = FEE_NAMES[item[0]] xmltext += """ """ % (item_id, item_name, item_amt, bank_id, acct_num) item_id += 1 except: pass xmldict['item_id'] = item_id xmltext += """ """ % xmldict # no additional charges, determine faculty bank only else: if student.is_postgrad: bank = BANK_ACCOUNTS['postgrad'] elif student.current_mode in ( 'ug_pt', 'de_pt','dp_pt', 'de_dsh', 'ug_dsh'): bank = BANK_ACCOUNTS['parttime'] elif student.faccode in ('FAG', 'FAT', 'FBM', 'FMLS', 'fac1'): bank = BANK_ACCOUNTS['fac1'] elif student.faccode in ('FCS', 'FED', 'FES', 'FET'): bank = BANK_ACCOUNTS['fac2'] elif student.faccode in ('FLS', 'FLW', 'FMS', 'FPS', 'FSS'): bank = BANK_ACCOUNTS['fac3'] elif student.faccode in ('FP',): bank = BANK_ACCOUNTS['ijmb'] xmldict['institution_acct'] = bank[0] xmldict['institution_bank_id'] = bank[1] # Clearance (acceptance) fee elif category.startswith('clearance'): # collect additional fees if self.context.p_category == 'clearance_incl': xmltext = """ """ % xmldict item_id = 1 for item in ACCEPTANCEFEES[student.certcode].items(): try: item_amt = 100 * int(item[1]) bank = BANK_ACCOUNTS[item[0]] acct_num = bank[0] bank_id = bank[1] item_name = FEE_NAMES[item[0]] xmltext += """ """ % (item_id, item_name, item_amt, bank_id, acct_num) item_id += 1 except: pass xmldict['item_id'] = item_id xmltext += """ """ % xmldict # no additional charges, determine faculty bank only else: if student.is_postgrad: bank = BANK_ACCOUNTS['postgrad'] else: bank = BANK_ACCOUNTS['acceptance'] xmldict['institution_acct'] = bank[0] xmldict['institution_bank_id'] = bank[1] # Hostel Maintenance elif category == 'hostel_maintenance': xmldict['institution_amt'] = 100 * self.context.net_amt - 1000000 bank = BANK_ACCOUNTS[category] xmldict['institution_acct'] = bank[0] xmldict['institution_bank_id'] = bank[1] xmltext = """ """ % xmldict # Other fees elif category in BANK_ACCOUNTS.keys(): bank = BANK_ACCOUNTS[category] xmldict['institution_acct'] = bank[0] xmldict['institution_bank_id'] = bank[1] if not xmltext and provider_amt == 0: xmltext = """ """ % xmldict elif not xmltext: xmltext = """ """ % xmldict self.xml_data = """""" % xmltext xmlitems = '' xmldoc = minidom.parseString(xmltext) itemlist = xmldoc.getElementsByTagName('item_detail') for s in itemlist: xmlitems += "%s (%s %s, %s (%s)), " % ( s.attributes['item_name'].value, u'\u20a6', int(s.attributes['item_amt'].value)/100, s.attributes['acct_num'].value, s.attributes['bank_id'].value, ) self.context.p_split_data = xmlitems self.context.provider_amt = provider_amt self.amount_auth = int(100 * self.context.amount_auth) hashargs = ( self.context.p_id + PRODUCT_ID + self.pay_item_id + str(int(self.amount_auth)) + self.site_redirect_url + MAC) self.hashvalue = hashlib.sha512(hashargs).hexdigest() return class CustomInterswitchPaymentRequestWebservicePageApplicant( InterswitchPaymentRequestWebservicePageApplicant): """Request webservice view for the CollegePAY gateway """ grok.context(ICustomApplicantOnlinePayment) gateway_host = HOST gateway_url = URL https = HTTPS mac = MAC product_id = PRODUCT_ID class CustomInterswitchPaymentVerifyWebservicePageApplicant( InterswitchPaymentVerifyWebservicePageApplicant): """Payment verify view for the CollegePAY gateway """ grok.context(ICustomApplicantOnlinePayment) gateway_host = HOST gateway_url = URL https = HTTPS mac = MAC product_id = PRODUCT_ID class CustomInterswitchPaymentRequestWebservicePageStudent( InterswitchPaymentRequestWebservicePageStudent): """Request webservice view for the CollegePAY gateway """ grok.context(ICustomStudentOnlinePayment) gateway_host = HOST gateway_url = URL https = HTTPS mac = MAC product_id = PRODUCT_ID class CustomInterswitchPaymentVerifyWebservicePageStudent( InterswitchPaymentVerifyWebservicePageStudent): """Payment verify view for the CollegePAY gateway """ grok.context(ICustomStudentOnlinePayment) gateway_host = HOST gateway_url = URL https = HTTPS mac = MAC product_id = PRODUCT_ID