source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/helpers.py @ 13544

Last change on this file since 13544 was 13387, checked in by Henrik Bettermann, 9 years ago

Implement Interswitch JSON transaction query. This webservice is
automatically used if a mac key is set in the request webservice
view.

  • Property svn:keywords set to Id
File size: 8.3 KB
Line 
1## $Id: helpers.py 13387 2015-11-04 09:16:13Z 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##
18"""General helper functions for the interswitch module in custom packages.
19"""
20from datetime import datetime
21import httplib
22import hashlib
23import json
24from urllib import urlencode
25import grok
26from xml.dom.minidom import parseString
27from zope.event import notify
28from kofacustom.nigeria.interfaces import MessageFactory as _
29
30def SOAP_post(soap_action, xml, host, url, https):
31    """Handles making the SOAP request.
32
33    Further reading:
34    http://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryWs.asmx?op=getTransactionData
35    """
36    if https:
37        h = httplib.HTTPSConnection(host)
38    else:
39        h = httplib.HTTPConnection(host)
40    headers={
41        'Host':host,
42        'Content-Type':'text/xml; charset=utf-8',
43        'Content-Length':len(xml),
44        'SOAPAction':'"%s"' % soap_action,
45    }
46    h.request('POST', url, body=xml,headers=headers)
47    response = h.getresponse()
48    return response
49
50
51def get_SOAP_response(product_id, transref, host, url, https):
52    xml="""\
53<?xml version="1.0" encoding="utf-8"?>
54<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/">
55  <soap:Body>
56    <getTransactionData xmlns="http://tempuri.org/">
57      <product_id>%s</product_id>
58      <trans_ref>%s</trans_ref>
59    </getTransactionData>
60  </soap:Body>
61</soap:Envelope>""" % (product_id, transref)
62    response=SOAP_post("http://tempuri.org/getTransactionData",xml, host, url, https)
63    if response.status!=200:
64        return 'Connection error (%s, %s)' % (response.status, response.reason)
65    result_xml = response.read()
66    doc=parseString(result_xml)
67    response=doc.getElementsByTagName('getTransactionDataResult')[0].firstChild.data
68    return response
69
70
71def get_JSON_response(product_id, transref, host, url, https, mac, amount):
72    hashargs = product_id + transref + mac
73    hashvalue = hashlib.sha512(hashargs).hexdigest()
74    headers={
75        'Content-Type':'text/xml; charset=utf-8',
76        'Hash':hashvalue,
77    }
78    if https:
79        h = httplib.HTTPSConnection(host)
80    else:
81        h = httplib.HTTPConnection(host)
82    amount = int(100 * amount)
83    args = {'productid': product_id,
84            'transactionreference': transref,
85            'amount': amount}
86    url = '%s?' % url + urlencode(args)
87    h.request("GET", url, headers=headers)
88    response = h.getresponse()
89    if response.status!=200:
90        return {'error': 'Connection error (%s, %s)' % (response.status, response.reason)}
91    jsonout = response.read()
92    parsed_json = json.loads(jsonout)
93    return parsed_json
94
95def query_interswitch_SOAP(payment, product_id, host, url, https):
96    sr = get_SOAP_response(product_id, payment.p_id, host, url, https)
97    if sr.startswith('Connection error'):
98        msg = _('Connection error')
99        log = sr
100        return False, msg, log
101    wlist = sr.split(':')
102    if len(wlist) != 7:
103        msg = _('Invalid callback: ${a}', mapping = {'a': sr})
104        log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
105        return False, msg, log
106    payment.r_code = wlist[0]
107    payment.r_desc = wlist[1]
108    payment.r_amount_approved = float(wlist[2]) / 100
109    payment.r_card_num = wlist[3]
110    payment.r_pay_reference = wlist[5]
111    payment.r_company = u'interswitch'
112    if payment.r_code != '00':
113        msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
114        log = 'unsuccessful callback for %s payment %s: %s' % (
115            payment.p_category, payment.p_id, sr)
116        payment.p_state = 'failed'
117        notify(grok.ObjectModifiedEvent(payment))
118        return False, msg, log
119    if round(payment.r_amount_approved, 2) != round(payment.amount_auth, 2):
120        msg = _('Callback amount does not match.')
121        log = 'wrong callback for %s payment %s: %s' % (
122            payment.p_category, payment.p_id, sr)
123        payment.p_state = 'failed'
124        notify(grok.ObjectModifiedEvent(payment))
125        return False, msg, log
126    if wlist[4] != payment.p_id:
127        msg = _('Callback transaction id does not match.')
128        log = 'wrong callback for %s payment %s: %s' % (
129            payment.p_category, payment.p_id, sr)
130        payment.p_state = 'failed'
131        notify(grok.ObjectModifiedEvent(payment))
132        return False, msg, log
133    payment.p_state = 'paid'
134    payment.payment_date = datetime.utcnow()
135    msg = _('Successful callback received')
136    log = 'valid callback for %s payment %s: %s' % (
137        payment.p_category, payment.p_id, sr)
138    notify(grok.ObjectModifiedEvent(payment))
139    return True, msg, log
140
141def query_interswitch(payment, product_id, host, url, https, mac):
142    # If no mac mac key is given, fall back to deprecated SOAP method
143    # (Uniben, AAUA, FCEOkene).
144    if mac == None:
145        return query_interswitch_SOAP(payment, product_id, host, url, https)
146    jr = get_JSON_response(product_id, payment.p_id, host, url,
147                           https, mac, payment.amount_auth)
148    error = jr.get('error')
149    if error:
150        msg = log = error
151        return False, msg, log
152
153    # A typical JSON response
154    #  {u'SplitAccounts': [],
155    #   u'MerchantReference':u'p4210665523377',
156    #   u'PaymentReference':u'GTB|WEB|KPOLY|12-01-2015|013138',
157    #   u'TransactionDate':u'2015-01-12T13:43:39.27',
158    #   u'RetrievalReferenceNumber':u'000170548791',
159    #   u'ResponseDescription': u'Approved Successful',
160    #   u'Amount': 2940000,
161    #   u'CardNumber': u'2507',
162    #   u'ResponseCode': u'00',
163    #   u'LeadBankCbnCode': None,
164    #   u'LeadBankName': None}
165
166    if len(jr) != 11:
167        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
168        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
169        return False, msg, log
170    payment.r_code = jr['ResponseCode']
171    payment.r_desc = jr['ResponseDescription']
172    payment.r_amount_approved = jr['Amount'] / 100.0
173    payment.r_card_num = jr['CardNumber']
174    payment.r_pay_reference = jr['PaymentReference']
175    payment.r_company = u'interswitch'
176    if payment.r_code != '00':
177        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
178        log = 'unsuccessful callback for %s payment %s: %s' % (
179            payment.p_category, payment.p_id, payment.r_desc)
180        payment.p_state = 'failed'
181        notify(grok.ObjectModifiedEvent(payment))
182        return False, msg, log
183    if round(payment.r_amount_approved, 2) != round(payment.amount_auth, 2):
184        msg = _('Callback amount does not match.')
185        log = 'wrong callback for %s payment %s: %s' % (
186            payment.p_category, payment.p_id, str(jr))
187        payment.p_state = 'failed'
188        notify(grok.ObjectModifiedEvent(payment))
189        return False, msg, log
190    if jr['MerchantReference'] != payment.p_id:
191        msg = _('Callback transaction id does not match.')
192        log = 'wrong callback for %s payment %s: %s' % (
193            payment.p_category, payment.p_id, str(jr))
194        payment.p_state = 'failed'
195        notify(grok.ObjectModifiedEvent(payment))
196        return False, msg, log
197    payment.p_state = 'paid'
198    payment.payment_date = datetime.utcnow()
199    msg = _('Successful callback received')
200    log = 'valid callback for %s payment %s: %s' % (
201        payment.p_category, payment.p_id, str(jr))
202    notify(grok.ObjectModifiedEvent(payment))
203    return True, msg, log
204
205def write_payments_log(id, payment):
206    payment.logger.info(
207        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
208        id, payment.p_id, payment.p_category,
209        payment.amount_auth, payment.r_code,
210        payment.provider_amt, payment.gateway_amt,
211        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.