## $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