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

Last change on this file since 15702 was 14245, checked in by Henrik Bettermann, 8 years ago

Be more tolerant.

  • Property svn:keywords set to Id
File size: 8.6 KB
Line 
1## $Id: helpers.py 14245 2016-10-31 08:50:38Z 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    #  {u'SplitAccounts': [],
157    #   u'MerchantReference':u'p4210665523377',
158    #   u'PaymentReference':u'GTB|WEB|KPOLY|12-01-2015|013138',
159    #   u'TransactionDate':u'2015-01-12T13:43:39.27',
160    #   u'RetrievalReferenceNumber':u'000170548791',
161    #   u'ResponseDescription': u'Approved Successful',
162    #   u'Amount': 2940000,
163    #   u'CardNumber': u'2507',
164    #   u'ResponseCode': u'00',
165    #   u'LeadBankCbnCode': None,
166    #   u'LeadBankName': None}
167
168    if len(jr) < 11:
169        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
170        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
171        return False, msg, log
172    if verify and jr['ResponseCode'] == '20050':
173        msg = _('Integration method has changed.')
174        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
175        return False, msg, log
176    payment.r_code = jr['ResponseCode']
177    payment.r_desc = jr['ResponseDescription']
178    payment.r_amount_approved = jr['Amount'] / 100.0
179    payment.r_card_num = jr['CardNumber']
180    payment.r_pay_reference = jr['PaymentReference']
181    payment.r_company = u'interswitch'
182    if payment.r_code != '00':
183        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
184        log = 'unsuccessful callback for %s payment %s: %s' % (
185            payment.p_category, payment.p_id, payment.r_desc)
186        payment.p_state = 'failed'
187        notify(grok.ObjectModifiedEvent(payment))
188        return False, msg, log
189    if round(payment.r_amount_approved, 0) != round(payment.amount_auth, 0):
190        msg = _('Callback amount does not match.')
191        log = 'wrong callback for %s payment %s: %s' % (
192            payment.p_category, payment.p_id, str(jr))
193        payment.p_state = 'failed'
194        notify(grok.ObjectModifiedEvent(payment))
195        return False, msg, log
196    if jr['MerchantReference'] != payment.p_id:
197        msg = _('Callback transaction id does not match.')
198        log = 'wrong callback for %s payment %s: %s' % (
199            payment.p_category, payment.p_id, str(jr))
200        payment.p_state = 'failed'
201        notify(grok.ObjectModifiedEvent(payment))
202        return False, msg, log
203    payment.p_state = 'paid'
204    if not verify:
205        payment.payment_date = datetime.utcnow()
206    msg = _('Successful callback received')
207    log = 'valid callback for %s payment %s: %s' % (
208        payment.p_category, payment.p_id, str(jr))
209    notify(grok.ObjectModifiedEvent(payment))
210    return True, msg, log
211
212def write_payments_log(id, payment):
213    payment.logger.info(
214        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
215        id, payment.p_id, payment.p_category,
216        payment.amount_auth, payment.r_code,
217        payment.provider_amt, payment.gateway_amt,
218        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.