# -*- 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': ('2043582455', '8'),
'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': ('2043582644', '8'),
'parttime': ('1012678566', '123'),
'ijmb': ('1012278272', '123'),
'exam': ('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
xmldict['institution_acct'] = '2043582644'
xmldict['institution_bank_id'] = '8'
if self.applicant.__parent__.code in ('ver2019', 'send2019', 'res2024'):
provider_amt = 3000.0
xmldict['institution_acct'] = '2043583005'
xmldict['institution_bank_id'] = '8'
elif self.applicant.__parent__.code.startswith('cert'):
provider_amt = 3000.0
elif self.applicant.__parent__.code.startswith('trans'):
provider_amt = 3000.0
if self.applicant.applicant_id.startswith('dsh'):
xmldict['institution_acct'] = '2043582644'
xmldict['institution_bank_id'] = '8'
elif self.applicant.applicant_id.startswith('ijmbe'):
xmldict['institution_acct'] = '2043582644'
xmldict['institution_bank_id'] = '8'
elif 'alumni' in self.application_url():
xmldict['institution_acct'] = '2043583005'
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()
elif student.entry_session < 2024:
sorted_items = SCHOOLFEES[23][student.certcode].items()
else:
sorted_items = SCHOOLFEES[24][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
elif item[0] == 'tuition':
item_amt += academic_session.penalty_ug
item_amt *= 100
acct_num = ''
bank_id = ''
item_name = ''
# Find appropriate bank
bank = None
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', 'dp_ft'):
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']
if not bank:
self.flash(
'Bank account not set (%s).' % item[0], type="danger")
self.redirect(self.url(self.context, '@@index'))
return
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:
bank = None
if student.is_postgrad:
bank = BANK_ACCOUNTS['postgrad']
elif student.current_mode in (
'ug_pt', 'de_pt','dp_pt', 'de_dsh', 'ug_dsh', 'dp_ft'):
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']
if not bank:
self.flash('Bank account not set.', type="danger")
self.redirect(self.url(self.context, '@@index'))
return
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