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
Line 
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
19import httplib
20import urllib
21from xml.dom.minidom import parseString
22import grok
23from zope.component import getUtility
24from waeup.kofa.browser.layout import KofaPage, UtilityView
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.interfaces import RETURNING, IKofaUtils
27from waeup.kofa.utils.helpers import to_timezone
28from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
29from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
30from waeup.kofa.payments.interfaces import payment_categories
31from waeup.kwarapoly.students.interfaces import ICustomStudentOnlinePayment
32from waeup.kwarapoly.applicants.interfaces import ICustomApplicantOnlinePayment
33from waeup.kwarapoly.interfaces import MessageFactory as _
34
35PRODUCT_ID = '3986'
36SITE_NAME = 'kwarapoly-kofa.waeup.org'
37PROVIDER_ACCT = '1010764827'
38PROVIDER_BANK_ID = '117'
39PROVIDER_ITEM_NAME = 'BT Education'
40INSTITUTION_NAME = 'KwaraPoly'
41CURRENCY = '566'
42#QUERY_URL = 'https://webpay.interswitchng.com/paydirect/services/TransactionQueryURL.aspx'
43#QUERY_URL = 'https://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryURL.aspx'
44#POST_ACTION = 'https://webpay.interswitchng.com/paydirect/webpay/pay.aspx'
45POST_ACTION = 'https://testwebpay.interswitchng.com/test_paydirect/webpay/pay.aspx'
46
47#HOST = 'webpay.interswitchng.com'
48HOST = 'testwebpay.interswitchng.com'
49#URL = '/paydirect/services/TransactionQueryWs.asmx'
50URL = '/test_paydirect/services/TransactionQueryWs.asmx'
51httplib.HTTPConnection.debuglevel = 0
52
53
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
90def query_interswitch(payment):
91    sr = get_SOAP_response(PRODUCT_ID, payment.p_id)
92    wlist = sr.split(':')
93    if len(wlist) != 7:
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
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':
103        msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
104        log = 'unsuccessful callback for payment %s: %s' % (payment.p_id, sr)
105        payment.p_state = 'failed'
106        return False, msg, log
107    if payment.r_amount_approved != payment.amount_auth:
108        msg = _('Callback amount does not match.')
109        log = 'wrong callback for payment %s: %s' % (payment.p_id, sr)
110        payment.p_state = 'failed'
111        return False, msg, log
112    if wlist[4] != payment.p_id:
113        msg = _('Callback transaction id does not match.')
114        log = 'wrong callback for payment %s: %s' % (payment.p_id, sr)
115        payment.p_state = 'failed'
116        return False, msg, log
117    payment.p_state = 'paid'
118    payment.payment_date = datetime.utcnow()
119    msg = _('Successful callback received')
120    log = 'valid callback for payment %s: %s' % (payment.p_id, sr)
121    return True, msg, log
122
123class InterswitchActionButtonStudent(APABStudent):
124    grok.order(1)
125    grok.context(ICustomStudentOnlinePayment)
126    grok.require('waeup.payStudent')
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
137class InterswitchActionButtonApplicant(APABApplicant):
138    grok.order(1)
139    grok.context(ICustomApplicantOnlinePayment)
140    grok.require('waeup.payApplicant')
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
151class InterswitchRequestWebserviceActionButtonStudent(APABStudent):
152    grok.order(2)
153    grok.context(ICustomStudentOnlinePayment)
154    grok.require('waeup.payStudent')
155    icon = 'actionicon_call.png'
156    text = _('Requery CollegePAY')
157    target = 'request_webservice'
158
159class InterswitchRequestWebserviceActionButtonApplicant(APABApplicant):
160    grok.order(2)
161    grok.context(ICustomApplicantOnlinePayment)
162    grok.require('waeup.payApplicant')
163    icon = 'actionicon_call.png'
164    text = _('Requery CollegePAY')
165    target = 'request_webservice'
166
167class InterswitchPageStudent(KofaPage):
168    """ View which sends a POST request to the Interswitch
169    CollegePAY payment gateway.
170    """
171    grok.context(ICustomStudentOnlinePayment)
172    grok.name('goto_interswitch')
173    grok.template('student_goto_interswitch')
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
180    pay_item_id = '101'
181    product_id = PRODUCT_ID
182
183    def update(self):
184        #if self.context.p_state != 'unpaid':
185        if self.context.p_state == 'paid':
186            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
187            self.redirect(self.url(self.context, '@@index'))
188            return
189
190        student = self.student = self.context.student
191        certificate = getattr(student['studycourse'],'certificate',None)
192        self.amount_auth = 100 * self.context.amount_auth
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
200        self.category = payment_categories.getTermByToken(
201            self.context.p_category).title
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")
205        self.site_redirect_url = self.url(self.context, 'request_webservice')
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
211        xmldict['provider_amt'] = 100 * 1200
212        # Dalash data
213        xmldict['dalash_amt'] = 100 * 1800
214        # Institution data
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"
229            xmldict['institution_bank_id'] = '117'
230        else:
231            xmldict['institution_acct'] = "0000000000000"
232            xmldict['institution_bank_id'] = '0'
233        xmldict['institution_amt'] = 100 * (
234            self.context.amount_auth - 1200 - 300 - 1800)
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" />
241<item_detail item_id="2" item_name="Dalash" item_amt="%(dalash_amt)s" bank_id="117" acct_num="1013196791" />
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" />
243</item_details>
244</payment_item_detail>""" % xmldict
245        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
246        return
247
248class InterswitchPageApplicant(KofaPage):
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')
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
261    pay_item_id = '8303'
262    product_id = PRODUCT_ID
263
264    def update(self):
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
269        if self.context.__parent__.__parent__.expired \
270            and self.context.__parent__.__parent__.strict_deadline:
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
275        self.applicant = self.context.__parent__
276        self.amount_auth = 100 * self.context.amount_auth
277        xmldict = {}
278        self.category = payment_categories.getTermByToken(
279            self.context.p_category).title
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")
283        self.site_redirect_url = self.url(self.context, 'request_webservice')
284        xmldict['detail_ref'] = self.context.p_id
285        # Provider data
286        xmldict['provider_amt'] = 100 * 500
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
291        xmldict['institution_amt'] = 100 * (self.context.amount_auth - 500 - 150)
292        xmldict['institution_acct'] = '0'
293        xmldict['institution_bank_id'] = '0'
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
304        return
305
306
307class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
308    """ Request webservice view for the CollegePAY gateway
309    """
310    grok.context(ICustomStudentOnlinePayment)
311    grok.name('request_webservice')
312    grok.require('waeup.payStudent')
313
314    def update(self):
315        ob_class = self.__implemented__.__name__
316        if self.context.p_state == 'paid':
317            self.flash(_('This ticket has already been paid.'))
318            return
319        student = self.context.student
320        success, msg, log = query_interswitch(self.context)
321        student.writeLogMessage(self, log)
322        if not success:
323            self.flash(msg)
324            return
325        success, msg, log = self.context.doAfterStudentPayment()
326        if log is not None:
327            student.writeLogMessage(self, log)
328        self.flash(msg)
329        return
330
331    def render(self):
332        self.redirect(self.url(self.context, '@@index'))
333        return
334
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')
341
342    def update(self):
343        if self.context.p_state == 'paid':
344            self.flash(_('This ticket has already been paid.'))
345            return
346        applicant = self.context.__parent__
347        success, msg, log = query_interswitch(self.context)
348        applicant.writeLogMessage(self, log)
349        if not success:
350            self.flash(msg)
351            return
352        success, msg, log = self.context.doAfterApplicantPayment()
353        if log is not None:
354            applicant.writeLogMessage(self, log)
355        self.flash(msg)
356        return
357
358    def render(self):
359        self.redirect(self.url(self.context, '@@index'))
360        return
Note: See TracBrowser for help on using the repository browser.