source: main/waeup.fceokene/trunk/src/waeup/fceokene/interswitch/browser.py @ 9626

Last change on this file since 9626 was 9613, checked in by Henrik Bettermann, 12 years ago

Prvide correct bank details for maintenance fee payment. Do not use school fee banks.

  • Property svn:keywords set to Id
File size: 15.9 KB
RevLine 
[7894]1## $Id: browser.py 9613 2012-11-11 06:09:18Z 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##
18from datetime import datetime
[7898]19import httplib
20import urllib
21from xml.dom.minidom import parseString
[7894]22import grok
[8281]23from zope.component import getUtility
[7894]24from waeup.kofa.browser.layout import KofaPage, UtilityView
25from waeup.kofa.accesscodes import create_accesscode
[8281]26from waeup.kofa.interfaces import RETURNING, IKofaUtils
27from waeup.kofa.utils.helpers import to_timezone
[8421]28from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
29from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
[8460]30from waeup.fceokene.students.interfaces import ICustomStudentOnlinePayment
31from waeup.fceokene.applicants.interfaces import ICustomApplicantOnlinePayment
32from waeup.fceokene.interfaces import MessageFactory as _
[7894]33
[8644]34PRODUCT_ID = '83'
[8460]35SITE_NAME = 'fceokene-kofa.waeup.org'
[8641]36PROVIDER_ACCT = '0026781725'
37PROVIDER_BANK_ID = '31'
[8263]38PROVIDER_ITEM_NAME = 'BT Education'
[8460]39INSTITUTION_NAME = 'FCEOkene'
[7894]40CURRENCY = '566'
[8401]41#QUERY_URL = 'https://webpay.interswitchng.com/paydirect/services/TransactionQueryURL.aspx'
[8293]42#QUERY_URL = 'https://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryURL.aspx'
[8385]43POST_ACTION = 'https://webpay.interswitchng.com/paydirect/webpay/pay.aspx'
[8293]44#POST_ACTION = 'https://testwebpay.interswitchng.com/test_paydirect/webpay/pay.aspx'
[7894]45
[8293]46HOST = 'webpay.interswitchng.com'
47#HOST = 'testwebpay.interswitchng.com'
48URL = '/paydirect/services/TransactionQueryWs.asmx'
49#URL = '/test_paydirect/services/TransactionQueryWs.asmx'
[7898]50httplib.HTTPConnection.debuglevel = 0
51
[8256]52
[7898]53def SOAP_post(soap_action,xml):
54    """Handles making the SOAP request.
55
56    Further reading:
57    http://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryWs.asmx?op=getTransactionData
58    """
59    h = httplib.HTTPConnection(HOST)
60    headers={
61        'Host':HOST,
62        'Content-Type':'text/xml; charset=utf-8',
63        'Content-Length':len(xml),
64        'SOAPAction':'"%s"' % soap_action,
65    }
66    h.request('POST', URL, body=xml,headers=headers)
67    r = h.getresponse()
68    d = r.read()
69    if r.status!=200:
70        raise ValueError('Error connecting: %s, %s' % (r.status, r.reason))
71    return d
72
73def get_SOAP_response(product_id, transref):
74    xml="""\
75<?xml version="1.0" encoding="utf-8"?>
76<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
77  <soap:Body>
78    <getTransactionData xmlns="http://tempuri.org/">
79      <product_id>%s</product_id>
80      <trans_ref>%s</trans_ref>
81    </getTransactionData>
82  </soap:Body>
83</soap:Envelope>""" % (product_id, transref)
84    result_xml=SOAP_post("http://tempuri.org/getTransactionData",xml)
85    doc=parseString(result_xml)
86    response=doc.getElementsByTagName('getTransactionDataResult')[0].firstChild.data
87    return response
88
[8430]89def query_interswitch(payment):
[8256]90    sr = get_SOAP_response(PRODUCT_ID, payment.p_id)
91    wlist = sr.split(':')
92    if len(wlist) != 7:
[8430]93        msg = _('Invalid callback: ${a}', mapping = {'a': sr})
94        log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
95        return False, msg, log
[8256]96    payment.r_code = wlist[0]
97    payment.r_desc = wlist[1]
98    payment.r_amount_approved = float(wlist[2]) / 100
99    payment.r_card_num = wlist[3]
100    payment.r_pay_reference = wlist[5]
[9440]101    payment.r_company = u'interswitch'
[8256]102    if payment.r_code != '00':
[8430]103        msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
[9440]104        log = 'unsuccessful callback for %s payment %s: %s' % (
105            payment.p_category, payment.p_id, sr)
[8256]106        payment.p_state = 'failed'
[8430]107        return False, msg, log
[8263]108    if payment.r_amount_approved != payment.amount_auth:
[8430]109        msg = _('Callback amount does not match.')
[9440]110        log = 'wrong callback for %s payment %s: %s' % (
111            payment.p_category, payment.p_id, sr)
[8256]112        payment.p_state = 'failed'
[8430]113        return False, msg, log
[8256]114    if wlist[4] != payment.p_id:
[8430]115        msg = _('Callback transaction id does not match.')
[9440]116        log = 'wrong callback for %s payment %s: %s' % (
117            payment.p_category, payment.p_id, sr)
[8256]118        payment.p_state = 'failed'
[8430]119        return False, msg, log
[8256]120    payment.p_state = 'paid'
[8433]121    payment.payment_date = datetime.utcnow()
[8430]122    msg = _('Successful callback received')
[9434]123    log = 'valid callback for %s payment %s: %s' % (
124        payment.p_category, payment.p_id, sr)
[8430]125    return True, msg, log
[8256]126
[8421]127class InterswitchActionButtonStudent(APABStudent):
[8259]128    grok.order(1)
[8255]129    grok.context(ICustomStudentOnlinePayment)
[8430]130    grok.require('waeup.payStudent')
[7894]131    icon = 'actionicon_pay.png'
132    text = _('CollegePAY')
133    target = 'goto_interswitch'
134
135    @property
136    def target_url(self):
137        if self.context.p_state != 'unpaid':
138            return ''
139        return self.view.url(self.view.context, self.target)
140
[8421]141class InterswitchActionButtonApplicant(APABApplicant):
[8259]142    grok.order(1)
[8256]143    grok.context(ICustomApplicantOnlinePayment)
[8430]144    grok.require('waeup.payApplicant')
[8256]145    icon = 'actionicon_pay.png'
146    text = _('CollegePAY')
147    target = 'goto_interswitch'
148
149    @property
150    def target_url(self):
151        if self.context.p_state != 'unpaid':
152            return ''
153        return self.view.url(self.view.context, self.target)
154
[8421]155class InterswitchRequestWebserviceActionButtonStudent(APABStudent):
[8259]156    grok.order(2)
[8255]157    grok.context(ICustomStudentOnlinePayment)
[8430]158    grok.require('waeup.payStudent')
[7919]159    icon = 'actionicon_call.png'
[8421]160    text = _('Requery CollegePAY')
[7919]161    target = 'request_webservice'
162
[8421]163class InterswitchRequestWebserviceActionButtonApplicant(APABApplicant):
[8259]164    grok.order(2)
[8256]165    grok.context(ICustomApplicantOnlinePayment)
[8430]166    grok.require('waeup.payApplicant')
[8256]167    icon = 'actionicon_call.png'
[8421]168    text = _('Requery CollegePAY')
[8256]169    target = 'request_webservice'
[7919]170
[8256]171class InterswitchPageStudent(KofaPage):
[7894]172    """ View which sends a POST request to the Interswitch
173    CollegePAY payment gateway.
174    """
[8255]175    grok.context(ICustomStudentOnlinePayment)
[7894]176    grok.name('goto_interswitch')
[8256]177    grok.template('student_goto_interswitch')
[7894]178    grok.require('waeup.payStudent')
179    label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
180    submit_button = _('Submit')
181    action = POST_ACTION
182    site_name = SITE_NAME
183    currency = CURRENCY
[9613]184    pay_item_id = ''
[7894]185    product_id = PRODUCT_ID
186
187    def update(self):
[8256]188        if self.context.p_state == 'paid':
[7894]189            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
190            self.redirect(self.url(self.context, '@@index'))
191            return
[8256]192
[8745]193        student = self.student = self.context.student
194        certificate = getattr(student['studycourse'],'certificate',None)
[8276]195        self.amount_auth = 100 * self.context.amount_auth
[7894]196        xmldict = {}
197        if certificate is not None:
198            xmldict['department'] = certificate.__parent__.__parent__.code
199            xmldict['faculty'] = certificate.__parent__.__parent__.__parent__.code
200        else:
201            xmldict['department'] = None
202            xmldict['faculty'] = None
[9406]203        self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
[8281]204        tz = getUtility(IKofaUtils).tzinfo
205        self.local_date_time = to_timezone(
206            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
[8256]207        self.site_redirect_url = self.url(self.context, 'request_webservice')
[8263]208        # Provider data
209        xmldict['detail_ref'] = self.context.p_id
210        xmldict['provider_acct'] = PROVIDER_ACCT
211        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
212        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
[9108]213        xmldict['provider_amt'] = 100 * 500
[8263]214        # Institution data
[9613]215        xmldict['fceokene_acct'] = "0000000000000"
216        xmldict['institution_bank_id'] = '0'
217        xmldict['institution_item_name'] = self.category
218        xmldict['institution_name'] = INSTITUTION_NAME
219        if self.context.p_category == 'schoolfee':
220            self.pay_item_id = '8302'
221            studycourse = student['studycourse']
222            if student.current_mode in ('ug_sw','prence',):
223                xmldict['institution_acct'] = "6216801025"
224                xmldict['institution_bank_id'] = '117'
225            elif student.current_mode in ('ug_ft',) and \
226                student['studycourse'].current_verdict == 'O':
227                xmldict['institution_acct'] = "6216801025"
228                xmldict['institution_bank_id'] = '117'
229            elif student.current_mode in ('ug_ft',):
230                xmldict['institution_acct'] = "6216801033"
231                xmldict['institution_bank_id'] = '117'
232            elif student.current_mode in ('pd_ft',):
233                xmldict['institution_acct'] = "6216801025"
234                xmldict['institution_bank_id'] = '117'
235            xmldict['fceokene_split'] = 100 * 1400
236            xmldict['institution_amt'] = 100 * (
237                self.context.amount_auth - 500 - 150 - 1400)
238        elif 'maintenance' in self.context.p_category:
239            self.pay_item_id = '8300'
240            xmldict['institution_amt'] = 100 * (
241                self.context.amount_auth - 150)
242            xmldict['institution_acct'] = "1012044132"
[9129]243            xmldict['institution_bank_id'] = '117'
[9613]244            xmldict['institution_amt'] = 100 * (
245                self.context.amount_auth - 150)
[8263]246        # Interswitch amount is not part of the xml data
[9613]247
248        if self.context.p_category == 'schoolfee':
249            xmltext = """<payment_item_detail>
[8263]250<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
251<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" />
[9129]252<item_detail item_id="2" item_name="FCEOkene Split" item_amt="%(fceokene_split)s" bank_id="117" acct_num="6216801058" />
253<item_detail item_id="3" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
[8263]254</item_details>
255</payment_item_detail>""" % xmldict
[9613]256
257        elif 'maintenance' in self.context.p_category:
258            xmltext = """<payment_item_detail>
259<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
260<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" />
261</item_details>
262</payment_item_detail>""" % xmldict
263
[8263]264        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
[7894]265        return
266
[8263]267class InterswitchPageApplicant(KofaPage):
[8256]268    """ View which sends a POST request to the Interswitch
269    CollegePAY payment gateway.
270    """
271    grok.context(ICustomApplicantOnlinePayment)
272    grok.require('waeup.payApplicant')
273    grok.template('applicant_goto_interswitch')
[8263]274    grok.name('goto_interswitch')
275    label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
276    submit_button = _('Submit')
277    action = POST_ACTION
278    site_name = SITE_NAME
279    currency = CURRENCY
[8644]280    pay_item_id = '8303'
[8263]281    product_id = PRODUCT_ID
[8256]282
283    def update(self):
[8263]284        if self.context.p_state != 'unpaid':
285            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
286            self.redirect(self.url(self.context, '@@index'))
287            return
[8831]288        if self.context.__parent__.__parent__.expired \
289            and self.context.__parent__.__parent__.strict_deadline:
[8706]290            self.flash(_("Payment ticket can't be send to CollegePAY. "
291                         "Application period has expired."))
292            self.redirect(self.url(self.context, '@@index'))
293            return
[8256]294        self.applicant = self.context.__parent__
[8276]295        self.amount_auth = 100 * self.context.amount_auth
[8256]296        xmldict = {}
[9406]297        self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
[8281]298        tz = getUtility(IKofaUtils).tzinfo
299        self.local_date_time = to_timezone(
300            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
[8256]301        self.site_redirect_url = self.url(self.context, 'request_webservice')
[8641]302        xmldict['detail_ref'] = self.context.p_id
[8263]303        # Provider data
[8644]304        xmldict['provider_amt'] = 100 * 500
[8263]305        xmldict['provider_acct'] = PROVIDER_ACCT
306        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
307        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
308        # Institution data
[8644]309        xmldict['institution_amt'] = 100 * (self.context.amount_auth - 500 - 150)
[9108]310        xmldict['institution_acct'] = '1012445289'
[8641]311        xmldict['institution_bank_id'] = '117'
[8263]312        xmldict['institution_item_name'] = self.context.p_category
313        xmldict['institution_name'] = INSTITUTION_NAME
314        # Interswitch amount is not part of the xml data
315        xmltext = """<payment_item_detail>
316<item_details detail_ref="%(detail_ref)s" college="%(institution_name)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_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" />
319</item_details>
320</payment_item_detail>""" % xmldict
321        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
[8256]322        return
323
[7894]324
[8256]325class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
[7919]326    """ Request webservice view for the CollegePAY gateway
327    """
[8255]328    grok.context(ICustomStudentOnlinePayment)
[7919]329    grok.name('request_webservice')
330    grok.require('waeup.payStudent')
331
332    def update(self):
[8430]333        ob_class = self.__implemented__.__name__
[7919]334        if self.context.p_state == 'paid':
335            self.flash(_('This ticket has already been paid.'))
336            return
[8745]337        student = self.context.student
[8430]338        success, msg, log = query_interswitch(self.context)
[8745]339        student.writeLogMessage(self, log)
[8430]340        if not success:
[9613]341            msg = self.request['QUERY_STRING'].replace('%20',' ')
342            self.flash(_('Response from Interswitch: ') + msg)
[8430]343            return
344        success, msg, log = self.context.doAfterStudentPayment()
345        if log is not None:
[8745]346            student.writeLogMessage(self, log)
[8430]347        self.flash(msg)
[8256]348        return
[7919]349
[8256]350    def render(self):
351        self.redirect(self.url(self.context, '@@index'))
352        return
[7926]353
[8256]354class InterswitchPaymentRequestWebservicePageApplicant(UtilityView, grok.View):
355    """ Request webservice view for the CollegePAY gateway
356    """
357    grok.context(ICustomApplicantOnlinePayment)
358    grok.name('request_webservice')
359    grok.require('waeup.payApplicant')
[7919]360
[8256]361    def update(self):
362        if self.context.p_state == 'paid':
363            self.flash(_('This ticket has already been paid.'))
[7919]364            return
[8256]365        applicant = self.context.__parent__
[8430]366        success, msg, log = query_interswitch(self.context)
[8745]367        applicant.writeLogMessage(self, log)
[8430]368        if not success:
369            self.flash(msg)
370            return
371        success, msg, log = self.context.doAfterApplicantPayment()
372        if log is not None:
[8745]373            applicant.writeLogMessage(self, log)
[8430]374        self.flash(msg)
[7919]375        return
376
377    def render(self):
378        self.redirect(self.url(self.context, '@@index'))
[9108]379        return
Note: See TracBrowser for help on using the repository browser.