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

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

Adjust to new Interswitch API.

  • Property svn:keywords set to Id
File size: 9.1 KB
RevLine 
[9746]1## $Id: helpers.py 16167 2020-07-15 09:00: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##
18"""General helper functions for the interswitch module in custom packages.
19"""
[9747]20from datetime import datetime
[9746]21import httplib
[13387]22import hashlib
23import json
24from urllib import urlencode
[9746]25import grok
26from xml.dom.minidom import parseString
27from zope.event import notify
28from kofacustom.nigeria.interfaces import MessageFactory as _
29
[11914]30def SOAP_post(soap_action, xml, host, url, https):
[9746]31    """Handles making the SOAP request.
32
33    Further reading:
34    http://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryWs.asmx?op=getTransactionData
35    """
[11914]36    if https:
37        h = httplib.HTTPSConnection(host)
38    else:
39        h = httplib.HTTPConnection(host)
[9746]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
[13387]50
[11914]51def get_SOAP_response(product_id, transref, host, url, https):
[9746]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)
[11914]62    response=SOAP_post("http://tempuri.org/getTransactionData",xml, host, url, https)
[9746]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
[13387]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
[13584]95def query_interswitch_SOAP(payment, product_id, host, url, https, verify):
[11914]96    sr = get_SOAP_response(product_id, payment.p_id, host, url, https)
[9746]97    if sr.startswith('Connection error'):
98        msg = _('Connection error')
99        log = sr
100        return False, msg, log
101    wlist = sr.split(':')
[13709]102    if len(wlist) < 7:
[9746]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
[14245]119    if round(payment.r_amount_approved, 0) != round(payment.amount_auth, 0):
[9746]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'
[13584]134    if not verify:
135        payment.payment_date = datetime.utcnow()
[13581]136    msg = _('Successful callback received.')
[9746]137    log = 'valid callback for %s payment %s: %s' % (
138        payment.p_category, payment.p_id, sr)
139    notify(grok.ObjectModifiedEvent(payment))
[9774]140    return True, msg, log
141
[13584]142def query_interswitch(payment, product_id, host, url, https, mac, verify):
[13387]143    # If no mac mac key is given, fall back to deprecated SOAP method
144    # (Uniben, AAUA, FCEOkene).
145    if mac == None:
[13584]146        return query_interswitch_SOAP(
147            payment, product_id, host, url, https, verify)
[13387]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
[16114]156
157    # old:
158
[13387]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
[16114]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
[16167]184    if not 'ResponseCode' in jr.keys() \
185        or not 'ResponseDescription' in jr.keys() \
186        or not 'Amount' in jr.keys():
[13387]187        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
188        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
189        return False, msg, log
[13585]190    if verify and jr['ResponseCode'] == '20050':
191        msg = _('Integration method has changed.')
192        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
193        return False, msg, log
[13387]194    payment.r_code = jr['ResponseCode']
195    payment.r_desc = jr['ResponseDescription']
196    payment.r_amount_approved = jr['Amount'] / 100.0
[16167]197    payment.r_card_num = jr.get('CardNumber', u'')
[16114]198    payment.r_pay_reference = jr.get('PaymentReference', u'')
[15727]199    #payment.r_company = u'interswitch'
[13387]200    if payment.r_code != '00':
201        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
202        log = 'unsuccessful callback for %s payment %s: %s' % (
203            payment.p_category, payment.p_id, payment.r_desc)
204        payment.p_state = 'failed'
205        notify(grok.ObjectModifiedEvent(payment))
206        return False, msg, log
[14245]207    if round(payment.r_amount_approved, 0) != round(payment.amount_auth, 0):
[13387]208        msg = _('Callback amount does not match.')
209        log = 'wrong callback for %s payment %s: %s' % (
210            payment.p_category, payment.p_id, str(jr))
211        payment.p_state = 'failed'
212        notify(grok.ObjectModifiedEvent(payment))
213        return False, msg, log
214    if jr['MerchantReference'] != payment.p_id:
215        msg = _('Callback transaction id does not match.')
216        log = 'wrong callback for %s payment %s: %s' % (
217            payment.p_category, payment.p_id, str(jr))
218        payment.p_state = 'failed'
219        notify(grok.ObjectModifiedEvent(payment))
220        return False, msg, log
221    payment.p_state = 'paid'
[13584]222    if not verify:
223        payment.payment_date = datetime.utcnow()
[13387]224    msg = _('Successful callback received')
225    log = 'valid callback for %s payment %s: %s' % (
226        payment.p_category, payment.p_id, str(jr))
227    notify(grok.ObjectModifiedEvent(payment))
228    return True, msg, log
229
[9774]230def write_payments_log(id, payment):
231    payment.logger.info(
232        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
233        id, payment.p_id, payment.p_category,
234        payment.amount_auth, payment.r_code,
235        payment.provider_amt, payment.gateway_amt,
236        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.