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

Last change on this file since 16114 was 16114, checked in by Henrik Bettermann, 4 years ago

Interswitch changed their system (and API). Some response parameters are missing.

  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1## $Id: helpers.py 16114 2020-06-11 09:41:40Z 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, verify):
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, 0) != round(payment.amount_auth, 0):
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    if not verify:
135        payment.payment_date = datetime.utcnow()
136    msg = _('Successful callback received.')
137    log = 'valid callback for %s payment %s: %s' % (
138        payment.p_category, payment.p_id, sr)
139    notify(grok.ObjectModifiedEvent(payment))
140    return True, msg, log
141
142def query_interswitch(payment, product_id, host, url, https, mac, verify):
143    # If no mac mac key is given, fall back to deprecated SOAP method
144    # (Uniben, AAUA, FCEOkene).
145    if mac == None:
146        return query_interswitch_SOAP(
147            payment, product_id, host, url, https, verify)
148    jr = get_JSON_response(product_id, payment.p_id, host, url,
149                           https, mac, payment.amount_auth)
150    error = jr.get('error')
151    if error:
152        msg = log = error
153        return False, msg, log
154
155    # A typical JSON response
156
157    # old:
158
159    #  {u'SplitAccounts': [],
160    #   u'MerchantReference':u'p4210665523377',
161    #   u'PaymentReference':u'GTB|WEB|KPOLY|12-01-2015|013138',
162    #   u'TransactionDate':u'2015-01-12T13:43:39.27',
163    #   u'RetrievalReferenceNumber':u'000170548791',
164    #   u'ResponseDescription': u'Approved Successful',
165    #   u'Amount': 2940000,
166    #   u'CardNumber': u'2507',
167    #   u'ResponseCode': u'00',
168    #   u'LeadBankCbnCode': None,
169    #   u'LeadBankName': None}
170
171    # new:
172
173    # 'PaymentReference' is maybe missing
174
175    #  {u'SplitAccounts': [],
176    #  u'MerchantReference':u'p5918633006916',
177    #  u'TransactionDate':u'2020-06-11T09:17:37',
178    #  u'ResponseDescription':u'Customer Cancellation',
179    #  u'Amount': 89525000,
180    #  u'CardNumber': u'',
181    #  u'ResponseCode': u'Z6',
182    #  u'BankCode': u''}
183
184
185    if len(jr) < 8:
186        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
187        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
188        return False, msg, log
189    if verify and jr['ResponseCode'] == '20050':
190        msg = _('Integration method has changed.')
191        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
192        return False, msg, log
193    payment.r_code = jr['ResponseCode']
194    payment.r_desc = jr['ResponseDescription']
195    payment.r_amount_approved = jr['Amount'] / 100.0
196    payment.r_card_num = jr['CardNumber']
197    payment.r_pay_reference = jr.get('PaymentReference', u'')
198    #payment.r_company = u'interswitch'
199    if payment.r_code != '00':
200        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
201        log = 'unsuccessful callback for %s payment %s: %s' % (
202            payment.p_category, payment.p_id, payment.r_desc)
203        payment.p_state = 'failed'
204        notify(grok.ObjectModifiedEvent(payment))
205        return False, msg, log
206    if round(payment.r_amount_approved, 0) != round(payment.amount_auth, 0):
207        msg = _('Callback amount does not match.')
208        log = 'wrong callback for %s payment %s: %s' % (
209            payment.p_category, payment.p_id, str(jr))
210        payment.p_state = 'failed'
211        notify(grok.ObjectModifiedEvent(payment))
212        return False, msg, log
213    if jr['MerchantReference'] != payment.p_id:
214        msg = _('Callback transaction id does not match.')
215        log = 'wrong callback for %s payment %s: %s' % (
216            payment.p_category, payment.p_id, str(jr))
217        payment.p_state = 'failed'
218        notify(grok.ObjectModifiedEvent(payment))
219        return False, msg, log
220    payment.p_state = 'paid'
221    if not verify:
222        payment.payment_date = datetime.utcnow()
223    msg = _('Successful callback received')
224    log = 'valid callback for %s payment %s: %s' % (
225        payment.p_category, payment.p_id, str(jr))
226    notify(grok.ObjectModifiedEvent(payment))
227    return True, msg, log
228
229def write_payments_log(id, payment):
230    payment.logger.info(
231        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
232        id, payment.p_id, payment.p_category,
233        payment.amount_auth, payment.r_code,
234        payment.provider_amt, payment.gateway_amt,
235        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.