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

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

Only Interswitch payments are suitable for verification.

Do no change payment ticket if response code is 20050.

  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1## $Id: helpers.py 13585 2016-01-11 08:57: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    log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
98    if sr.startswith('Connection error'):
99        msg = _('Connection error')
100        log = sr
101        return False, msg, log
102    wlist = sr.split(':')
103    if len(wlist) != 7:
104        msg = _('Invalid callback: ${a}', mapping = {'a': sr})
105        log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
106        return False, msg, log
107    if verify and jr['ResponseCode'] == '20050':
108        msg = _('Integration method has changed.')
109        log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
110        return False, msg, log
111    payment.r_code = wlist[0]
112    payment.r_desc = wlist[1]
113    payment.r_amount_approved = float(wlist[2]) / 100
114    payment.r_card_num = wlist[3]
115    payment.r_pay_reference = wlist[5]
116    payment.r_company = u'interswitch'
117    if payment.r_code != '00':
118        msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
119        log = 'unsuccessful callback for %s payment %s: %s' % (
120            payment.p_category, payment.p_id, sr)
121        payment.p_state = 'failed'
122        notify(grok.ObjectModifiedEvent(payment))
123        return False, msg, log
124    if round(payment.r_amount_approved, 2) != round(payment.amount_auth, 2):
125        msg = _('Callback amount does not match.')
126        log = 'wrong callback for %s payment %s: %s' % (
127            payment.p_category, payment.p_id, sr)
128        payment.p_state = 'failed'
129        notify(grok.ObjectModifiedEvent(payment))
130        return False, msg, log
131    if wlist[4] != payment.p_id:
132        msg = _('Callback transaction id does not match.')
133        log = 'wrong callback for %s payment %s: %s' % (
134            payment.p_category, payment.p_id, sr)
135        payment.p_state = 'failed'
136        notify(grok.ObjectModifiedEvent(payment))
137        return False, msg, log
138    payment.p_state = 'paid'
139    if not verify:
140        payment.payment_date = datetime.utcnow()
141    msg = _('Successful callback received.')
142    log = 'valid callback for %s payment %s: %s' % (
143        payment.p_category, payment.p_id, sr)
144    notify(grok.ObjectModifiedEvent(payment))
145    return True, msg, log
146
147def query_interswitch(payment, product_id, host, url, https, mac, verify):
148    # If no mac mac key is given, fall back to deprecated SOAP method
149    # (Uniben, AAUA, FCEOkene).
150    if mac == None:
151        return query_interswitch_SOAP(
152            payment, product_id, host, url, https, verify)
153    jr = get_JSON_response(product_id, payment.p_id, host, url,
154                           https, mac, payment.amount_auth)
155    error = jr.get('error')
156    if error:
157        msg = log = error
158        return False, msg, log
159
160    # A typical JSON response
161    #  {u'SplitAccounts': [],
162    #   u'MerchantReference':u'p4210665523377',
163    #   u'PaymentReference':u'GTB|WEB|KPOLY|12-01-2015|013138',
164    #   u'TransactionDate':u'2015-01-12T13:43:39.27',
165    #   u'RetrievalReferenceNumber':u'000170548791',
166    #   u'ResponseDescription': u'Approved Successful',
167    #   u'Amount': 2940000,
168    #   u'CardNumber': u'2507',
169    #   u'ResponseCode': u'00',
170    #   u'LeadBankCbnCode': None,
171    #   u'LeadBankName': None}
172
173    if len(jr) != 11:
174        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
175        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
176        return False, msg, log
177    if verify and jr['ResponseCode'] == '20050':
178        msg = _('Integration method has changed.')
179        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
180        return False, msg, log
181    payment.r_code = jr['ResponseCode']
182    payment.r_desc = jr['ResponseDescription']
183    payment.r_amount_approved = jr['Amount'] / 100.0
184    payment.r_card_num = jr['CardNumber']
185    payment.r_pay_reference = jr['PaymentReference']
186    payment.r_company = u'interswitch'
187    if payment.r_code != '00':
188        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
189        log = 'unsuccessful callback for %s payment %s: %s' % (
190            payment.p_category, payment.p_id, payment.r_desc)
191        payment.p_state = 'failed'
192        notify(grok.ObjectModifiedEvent(payment))
193        return False, msg, log
194    if round(payment.r_amount_approved, 2) != round(payment.amount_auth, 2):
195        msg = _('Callback amount does not match.')
196        log = 'wrong callback for %s payment %s: %s' % (
197            payment.p_category, payment.p_id, str(jr))
198        payment.p_state = 'failed'
199        notify(grok.ObjectModifiedEvent(payment))
200        return False, msg, log
201    if jr['MerchantReference'] != payment.p_id:
202        msg = _('Callback transaction id does not match.')
203        log = 'wrong callback for %s payment %s: %s' % (
204            payment.p_category, payment.p_id, str(jr))
205        payment.p_state = 'failed'
206        notify(grok.ObjectModifiedEvent(payment))
207        return False, msg, log
208    payment.p_state = 'paid'
209    if not verify:
210        payment.payment_date = datetime.utcnow()
211    msg = _('Successful callback received')
212    log = 'valid callback for %s payment %s: %s' % (
213        payment.p_category, payment.p_id, str(jr))
214    notify(grok.ObjectModifiedEvent(payment))
215    return True, msg, log
216
217def write_payments_log(id, payment):
218    payment.logger.info(
219        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
220        id, payment.p_id, payment.p_category,
221        payment.amount_auth, payment.r_code,
222        payment.provider_amt, payment.gateway_amt,
223        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.