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

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

Remove accidentally added line.

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