## $Id: browser.py 9616 2012-11-11 16:53:20Z 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 of
## 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
##
from datetime import datetime
import httplib
import urllib
import hashlib
from xml.dom.minidom import parseString
import grok
from zope.interface import Interface
from zope.component import getUtility, queryAdapter
from waeup.kofa.browser.layout import KofaPage, UtilityView
from waeup.kofa.accesscodes import create_accesscode
from waeup.kofa.interfaces import RETURNING, IKofaUtils
from waeup.kofa.utils.helpers import to_timezone
from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
from waeup.kwarapoly.students.interfaces import ICustomStudentOnlinePayment
from waeup.kwarapoly.applicants.interfaces import ICustomApplicantOnlinePayment
from waeup.kwarapoly.interfaces import MessageFactory as _
PRODUCT_ID = '3930'
SITE_NAME = 'kwarapoly-kofa.waeup.org'
PROVIDER_ACCT = '1010764827'
PROVIDER_BANK_ID = '117'
PROVIDER_ITEM_NAME = 'BT Education'
INSTITUTION_NAME = 'KwaraPoly'
CURRENCY = '566'
#QUERY_URL = 'https://webpay.interswitchng.com/paydirect/services/TransactionQueryURL.aspx'
#QUERY_URL = 'https://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryURL.aspx'
POST_ACTION = 'https://webpay.interswitchng.com/paydirect/webpay/pay.aspx'
#POST_ACTION = 'https://testwebpay.interswitchng.com/test_paydirect/webpay/pay.aspx'
HOST = 'webpay.interswitchng.com'
#HOST = 'testwebpay.interswitchng.com'
URL = '/paydirect/services/TransactionQueryWs.asmx'
#URL = '/test_paydirect/services/TransactionQueryWs.asmx'
httplib.HTTPConnection.debuglevel = 0
def SOAP_post(soap_action,xml):
"""Handles making the SOAP request.
Further reading:
http://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryWs.asmx?op=getTransactionData
"""
h = httplib.HTTPConnection(HOST)
headers={
'Host':HOST,
'Content-Type':'text/xml; charset=utf-8',
'Content-Length':len(xml),
'SOAPAction':'"%s"' % soap_action,
}
h.request('POST', URL, body=xml,headers=headers)
r = h.getresponse()
d = r.read()
if r.status!=200:
raise ValueError('Error connecting: %s, %s' % (r.status, r.reason))
return d
def get_SOAP_response(product_id, transref):
xml="""\
%s
%s
""" % (product_id, transref)
result_xml=SOAP_post("http://tempuri.org/getTransactionData",xml)
doc=parseString(result_xml)
response=doc.getElementsByTagName('getTransactionDataResult')[0].firstChild.data
return response
def query_interswitch(payment):
sr = get_SOAP_response(PRODUCT_ID, payment.p_id)
wlist = sr.split(':')
if len(wlist) != 7:
msg = _('Invalid callback: ${a}', mapping = {'a': sr})
log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
return False, msg, log
payment.r_code = wlist[0]
payment.r_desc = wlist[1]
payment.r_amount_approved = float(wlist[2]) / 100
payment.r_card_num = wlist[3]
payment.r_pay_reference = wlist[5]
payment.r_company = u'interswitch'
if payment.r_code != '00':
msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
log = 'unsuccessful callback for %s payment %s: %s' % (
payment.p_category, payment.p_id, sr)
payment.p_state = 'failed'
return False, msg, log
if payment.r_amount_approved != payment.amount_auth:
msg = _('Callback amount does not match.')
log = 'wrong callback for %s payment %s: %s' % (
payment.p_category, payment.p_id, sr)
payment.p_state = 'failed'
return False, msg, log
if wlist[4] != payment.p_id:
msg = _('Callback transaction id does not match.')
log = 'wrong callback for %s payment %s: %s' % (
payment.p_category, payment.p_id, sr)
payment.p_state = 'failed'
return False, msg, log
payment.p_state = 'paid'
payment.payment_date = datetime.utcnow()
msg = _('Successful callback received')
log = 'valid callback for %s payment %s: %s' % (
payment.p_category, payment.p_id, sr)
return True, msg, log
def interswitch_img_url(view):
static = view.static
if static is None or static.get(
'interswitch_verve_mastercard.gif', None) is None:
static = queryAdapter(
self.request, Interface, name='waeup.kwarapoly.interswitch')
return static['interswitch_verve_mastercard.gif']()
class InterswitchActionButtonStudent(APABStudent):
grok.order(1)
grok.context(ICustomStudentOnlinePayment)
grok.require('waeup.payStudent')
icon = 'actionicon_pay.png'
text = _('CollegePAY')
target = 'goto_interswitch'
@property
def target_url(self):
if self.context.p_state != 'unpaid':
return ''
return self.view.url(self.view.context, self.target)
class InterswitchActionButtonApplicant(APABApplicant):
grok.order(1)
grok.context(ICustomApplicantOnlinePayment)
grok.require('waeup.payApplicant')
icon = 'actionicon_pay.png'
text = _('CollegePAY')
target = 'goto_interswitch'
@property
def target_url(self):
if self.context.p_state != 'unpaid':
return ''
return self.view.url(self.view.context, self.target)
class InterswitchRequestWebserviceActionButtonStudent(APABStudent):
grok.order(2)
grok.context(ICustomStudentOnlinePayment)
grok.require('waeup.payStudent')
icon = 'actionicon_call.png'
text = _('Requery CollegePAY')
target = 'request_webservice'
class InterswitchRequestWebserviceActionButtonApplicant(APABApplicant):
grok.order(2)
grok.context(ICustomApplicantOnlinePayment)
grok.require('waeup.payApplicant')
icon = 'actionicon_call.png'
text = _('Requery CollegePAY')
target = 'request_webservice'
class InterswitchPageStudent(KofaPage):
""" View which sends a POST request to the Interswitch
CollegePAY payment gateway.
"""
grok.context(ICustomStudentOnlinePayment)
grok.name('goto_interswitch')
grok.template('student_goto_interswitch')
grok.require('waeup.payStudent')
label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
submit_button = _('Submit')
action = POST_ACTION
site_name = SITE_NAME
currency = CURRENCY
product_id = PRODUCT_ID
mac = 'E6BA6CBBA9AF2871EE25C32C8D57C98895B9B001DC5B9CB2C463E2A9BDA44A3F1260C8A364F33789CDF74CB3EE7E6EF5D94F48D3AF7B727E75D97F07618DFA6D'
def interswitch_img_url(self):
return interswitch_img_url(self)
def update(self):
#if self.context.p_state != 'unpaid':
if self.context.p_state == 'paid':
self.flash(_("Payment ticket can't be re-send to CollegePAY."))
self.redirect(self.url(self.context, '@@index'))
return
student = self.student = self.context.student
certificate = getattr(student['studycourse'],'certificate',None)
self.amount_auth = 100 * self.context.amount_auth
xmldict = {}
if certificate is not None:
xmldict['department'] = certificate.__parent__.__parent__.code
xmldict['faculty'] = certificate.__parent__.__parent__.__parent__.code
else:
xmldict['department'] = None
xmldict['faculty'] = None
self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
tz = getUtility(IKofaUtils).tzinfo
self.local_date_time = to_timezone(
self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
self.site_redirect_url = self.url(self.context, 'request_webservice')
# 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
xmldict['provider_amt'] = 100 * 1200
# Institution data
xmldict['institution_acct'] = "0000000000000"
xmldict['institution_bank_id'] = '0'
xmldict['institution_item_name'] = self.category
xmldict['institution_name'] = INSTITUTION_NAME
xmldict['institution_amt'] = 100 * self.context.amount_auth
xmldict['dalash_amt'] = 0
self.pay_item_id = '000'
if self.context.p_category == 'schoolfee':
self.pay_item_id = '101'
# Dalash data
xmldict['dalash_amt'] = 100 * 1800
if xmldict['faculty'] in ('CPGS',):
xmldict['institution_acct'] = "1771180233"
xmldict['institution_bank_id'] = '120'
elif xmldict['faculty'] in ('IBAS',):
xmldict['institution_acct'] = "0006772436"
xmldict['institution_bank_id'] = '121'
elif xmldict['faculty'] in ('IETS',):
xmldict['institution_acct'] = "0106259811"
xmldict['institution_bank_id'] = '10'
elif xmldict['faculty'] in ('IFMS',):
xmldict['institution_acct'] = "2013910271"
xmldict['institution_bank_id'] = '8'
elif xmldict['faculty'] in ('ITCH',):
#acct. number changed from Zenith to FBN by gbenga Nov. 11, 2012
xmldict['institution_acct'] = "2013910271"
xmldict['institution_bank_id'] = '8'
xmldict['institution_amt'] = 100 * (
self.context.amount_auth - 1200 - 300 - 1800)
hashargs = (
self.context.p_id +
PRODUCT_ID +
self.pay_item_id +
str(int(self.amount_auth)) +
self.site_redirect_url +
self.mac)
self.hashvalue = hashlib.sha512(hashargs).hexdigest()
# Interswitch amount is not part of the xml data
xmltext = """
""" % xmldict
self.xml_data = """""" % xmltext
return
class InterswitchPageApplicant(KofaPage):
""" View which sends a POST request to the Interswitch
CollegePAY payment gateway.
"""
grok.context(ICustomApplicantOnlinePayment)
grok.require('waeup.payApplicant')
grok.template('applicant_goto_interswitch')
grok.name('goto_interswitch')
label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
submit_button = _('Submit')
action = POST_ACTION
site_name = SITE_NAME
currency = CURRENCY
pay_item_id = ''
product_id = PRODUCT_ID
def interswitch_img_url(self):
return interswitch_img_url(self)
def update(self):
if self.context.p_state != 'unpaid':
self.flash(_("Payment ticket can't be re-send to CollegePAY."))
self.redirect(self.url(self.context, '@@index'))
return
if self.context.__parent__.__parent__.expired \
and self.context.__parent__.__parent__.strict_deadline:
self.flash(_("Payment ticket can't be send to CollegePAY. "
"Application period has expired."))
self.redirect(self.url(self.context, '@@index'))
return
self.applicant = self.context.__parent__
self.amount_auth = 100 * self.context.amount_auth
xmldict = {}
self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
tz = getUtility(IKofaUtils).tzinfo
self.local_date_time = to_timezone(
self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
self.site_redirect_url = self.url(self.context, 'request_webservice')
xmldict['detail_ref'] = self.context.p_id
# Provider data
xmldict['provider_amt'] = 100 * 500
xmldict['provider_acct'] = PROVIDER_ACCT
xmldict['provider_bank_id'] = PROVIDER_BANK_ID
xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
# Institution data
xmldict['institution_amt'] = 100 * (self.context.amount_auth - 500 - 150)
xmldict['institution_acct'] = '0'
xmldict['institution_bank_id'] = '0'
xmldict['institution_item_name'] = self.context.p_category
xmldict['institution_name'] = INSTITUTION_NAME
# Interswitch amount is not part of the xml data
xmltext = """
""" % xmldict
self.xml_data = """""" % xmltext
return
class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
""" Request webservice view for the CollegePAY gateway
"""
grok.context(ICustomStudentOnlinePayment)
grok.name('request_webservice')
grok.require('waeup.payStudent')
def update(self):
ob_class = self.__implemented__.__name__
if self.context.p_state == 'paid':
self.flash(_('This ticket has already been paid.'))
return
student = self.context.student
success, msg, log = query_interswitch(self.context)
student.writeLogMessage(self, log)
if not success:
self.flash(msg)
return
success, msg, log = self.context.doAfterStudentPayment()
if log is not None:
student.writeLogMessage(self, log)
self.flash(msg)
return
def render(self):
self.redirect(self.url(self.context, '@@index'))
return
class InterswitchPaymentRequestWebservicePageApplicant(UtilityView, grok.View):
""" Request webservice view for the CollegePAY gateway
"""
grok.context(ICustomApplicantOnlinePayment)
grok.name('request_webservice')
grok.require('waeup.payApplicant')
def update(self):
if self.context.p_state == 'paid':
self.flash(_('This ticket has already been paid.'))
return
applicant = self.context.__parent__
success, msg, log = query_interswitch(self.context)
applicant.writeLogMessage(self, log)
if not success:
self.flash(msg)
return
success, msg, log = self.context.doAfterApplicantPayment()
if log is not None:
applicant.writeLogMessage(self, log)
self.flash(msg)
return
def render(self):
self.redirect(self.url(self.context, '@@index'))
return