source: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/interswitch/browser.py @ 9388

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

Switch to test environment and setup xmp split data (with constant school fee so far).

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