source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/helpers.py @ 15739

Last change on this file since 15739 was 15734, checked in by Henrik Bettermann, 5 years ago

Add test.

  • Property svn:keywords set to Id
File size: 11.6 KB
Line 
1## $Id: helpers.py 15734 2019-11-01 11:39:34Z henrik $
2##
3## Copyright (C) 2017 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 etranzact module in custom packages.
19"""
20import grok
21import re
22from datetime import datetime
23from urllib import urlencode
24from urllib2 import urlopen
25from urlparse import parse_qs
26import httplib
27import hashlib
28import json
29from zope.event import notify
30from waeup.kofa.utils.helpers import extract_formvars
31from kofacustom.nigeria.interfaces import MessageFactory as _
32
33ERROR_PART1 = (
34        'PayeeName=N/A~'
35        + 'Faculty=N/A~'
36        + 'Department=N/A~'
37        + 'Level=N/A~'
38        + 'ProgrammeType=N/A~'
39        + 'StudyType=N/A~'
40        + 'Session=N/A~'
41        + 'PayeeID=N/A~'
42        + 'Amount=N/A~'
43        + 'FeeStatus=')
44ERROR_PART2 = (
45        '~Semester=N/A~'
46        + 'PaymentType=N/A~'
47        + 'MatricNumber=N/A~'
48        + 'Email=N/A~'
49        + 'PhoneNumber=N/A')
50
51def write_payments_log(id, payment):
52    payment.logger.info(
53        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
54        id, payment.p_id, payment.p_category,
55        payment.amount_auth, payment.r_code,
56        payment.provider_amt, payment.gateway_amt,
57        payment.thirdparty_amt))
58
59def query_history(host, terminal_id, transaction_id, https):
60    headers={"Content-type": "application/x-www-form-urlencoded",
61             "Accept": "text/plain"}
62    url = "/webconnect/v3/query.jsp"
63    if https:
64        h = httplib.HTTPSConnection(host)
65    else:
66        h = httplib.HTTPConnection(host)
67    args = {'TERMINAL_ID': terminal_id,
68            'TRANSACTION_ID': transaction_id,
69            }
70    #args['RESPONSE_URL'] = responseurl
71    h.request('POST', url, urlencode(args), headers)
72    response = h.getresponse()
73    if response.status!=200:
74        return 'Connection error (%s, %s)' % (response.status, response.reason), None
75    raw = response.read()
76    return raw, extract_formvars(raw)
77
78 # A sample caller response sent to the RESPONSE_URL
79
80 # http://salsa:8080/app/applicants/cbt2015/449072/p5679522929425/receive_etranzact?
81 # AMOUNT=3333.0&
82 # DESCRIPTION=&
83 # CHECKSUM=8aab3904652f8ba69ebed42d3bae80a2&
84 # EMAIL=aa%40aa.de&
85 # SUCCESS=C&
86 # MESSAGE=Cancel&
87 # LOGO_URL=https%3A%2F%2Fiuokada.waeup.org%2Fstatic_custom%2Fiou_logo.png&
88 # RESPONSE_URL=http%3A%2F%2Fsalsa%3A8080%2Fapp%2Fapplicants%2Fcbt2015%2F449072%2Fp5679522929425%2Freceive_etranzact&
89 # CURRENCY_CODE=NGN&
90 # TERMINAL_ID=0000000001&
91 # TRANSACTION_ID=p5679522929425&
92 # MERCHANT_CODE=0339990001&
93 # RESPONSE_CODE=C&
94 # FINAL_CHECKSUM=E524590DBFAB719EEE428C778FFF1650&
95 # STATUS_REASON=Cancel&
96 # TRANS_NUM=01ESA20190913062149AHGUHQ&
97 # CARD_NO=null&
98 # CARD_TYPE=null
99
100 # A sample query response
101
102 # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
103 #     "http://www.w3.org/TR/html4/loose.dtd">
104 # <script language='javascript'>
105 # var spanId = document.getElementById("message_container");
106 # spanId.textContent = 'Redirecting...';</script>
107 # <form method="GET" id="redirect_form" name="redirect_form"
108 # action="http://localhost:81/projects/Webconnect/response.php" target="_top">
109 # <input type="hidden" name = "LOGO_URL"
110 # value="http://localhost:81/projects/Webconnect/images/elogo.fw.png">
111 # <input type="hidden" name = "RESPONSE_URL"
112 # value="http://localhost:81/projects/Webconnect/response.php">
113 # <input type="hidden" name = "CURRENCY_CODE" value="NGN">
114 # <input type="hidden" name = "TERMINAL_ID" value="0000000001">
115 # <input type="hidden" name = "TRANSACTION_ID" value="etz1568638104web">
116 # <input type="hidden" name = "AMOUNT" value="100">
117 # <input type="hidden" name = "DESCRIPTION" value="Payment Description">
118 # <input type="hidden" name = "CHECKSUM"
119 # value="5be04064f4bb250f73650059a8e921cc">
120 # <input type="hidden" name = "MERCHANT_CODE" value="0339990001">
121 # <input type="hidden" name = "EMAIL" value="xyz@yahoo.com">
122 # <input type="hidden" name = "SUCCESS" value="0">
123 # <input type="hidden" name = "FINAL_CHECKSUM"
124 # value="FD67A4CCC39E2C8DEEEC78D6C64A61FE">
125 # <input type="hidden" name = "STATUS_REASON" value="Approved">
126 # <input type="hidden" name = "TRANS_NUM" value="01ESA20190916134824YA3YJ8">
127 # <input type="hidden" name = "CARD_NO" value="506066XXXXXXXXX6666">
128 # <input type="hidden" name = "CARD_TYPE" value="Verve">
129 # </form>
130 # <script language='javascript'>
131 # var fom = document.forms["redirect_form"];
132 # fom.submit();</script>
133
134 # A sample query response sent to the RESPONSE_URL after the browser has
135 # automatically executed the Javascript above (we don't use this response)
136
137 # http://salsa:8080/app/applicants/cbt2015/449072/p5686487280654/receive_etranzact?
138 # LOGO_URL=https%3A%2F%2Fiuokada.waeup.org%2Fstatic_custom%2Fiou_logo.png&
139 # RESPONSE_URL=http%3A%2F%2Fsalsa%3A8080%2Fapp%2Fapplicants%2Fcbt2015%2F449072%2Fp5686487280654%2Freceive_etranzact&
140 # CURRENCY_CODE=NGN&
141 # TERMINAL_ID=0000000001&
142 # TRANSACTION_ID=p5686487280654&
143 # AMOUNT=3333.0&
144 # DESCRIPTION=&
145 # CHECKSUM=3886118fcd91a376cc95c48c94dc499a&
146 # MERCHANT_CODE=0339990001&
147 # EMAIL=aa%40aa.de&
148 # SUCCESS=0&
149 # FINAL_CHECKSUM=EE105B703F84B1D67D0A4234622C03E8&
150 # STATUS_REASON=Approved&
151 # TRANS_NUM=01ESA20190916164636L2UTU7&
152 # CARD_NO=506066XXXXXXXXX6666&
153 # CARD_TYPE=Verve
154
155def process_response(payment, form, view, verify):
156    if not form or not form.get('SUCCESS', None):
157        msg = _('No valid response from Etranzact.')
158        log = 'No valid response from Etranzact for payment %s' % payment.p_id
159        payment.p_state = 'failed'
160        notify(grok.ObjectModifiedEvent(payment))
161        return False, msg, log
162    success = form.get('SUCCESS', None)
163    # Compute final checksum
164    transaction_id = payment.p_id
165    amount = "%.1f" % payment.amount_auth
166    responseurl = view.url(payment, 'receive_etranzact')
167    hashargs =  success + amount + view.terminal_id + transaction_id \
168        + responseurl + view.secret_key
169    final_checksum = hashlib.md5(hashargs).hexdigest().upper()
170    if form.get('FINAL_CHECKSUM', None) != final_checksum:
171        msg = _('Wrong checksum.')
172        log = 'wrong checksum for %s payment %s: %s' % (
173            payment.p_category, payment.p_id, str(form))
174        payment.p_state = 'failed'
175        notify(grok.ObjectModifiedEvent(payment))
176        return False, msg, log
177    payment.r_code = form.get('SUCCESS', None)
178    payment.r_desc = form.get('STATUS_REASON', None) # MESSAGE also available
179    payment.r_amount_approved = float(form.get('AMOUNT', None))
180    payment.r_pay_reference = form.get('TRANS_NUM', None)
181    payment.r_card_num = "%s %s" % (form.get('CARD_TYPE', None),
182                                    form.get('CARD_NO', None))
183    payment.r_company = u'etranzact'
184    if payment.r_code != '0':
185        msg = _('Unsuccessful response: ${a}', mapping = {'a': payment.r_desc})
186        log = 'unsuccessful response for %s payment %s: %s' % (
187            payment.p_category, payment.p_id, payment.r_desc)
188        payment.p_state = 'failed'
189        notify(grok.ObjectModifiedEvent(payment))
190        return False, msg, log
191    if round(payment.r_amount_approved/10.0, 0) != round(
192        payment.amount_auth/10.0, 0):
193        msg = _('Response amount does not match.')
194        log = 'wrong response for %s payment %s: %s' % (
195            payment.p_category, payment.p_id, str(form))
196        payment.p_state = 'failed'
197        notify(grok.ObjectModifiedEvent(payment))
198        return False, msg, log
199    transaction_id = form.get('TRANSACTION_ID', None)
200    if transaction_id != payment.p_id:
201        msg = _('Response transaction id does not match.')
202        log = 'wrong response for %s payment %s: %s' % (
203            payment.p_category, payment.p_id, str(form))
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 response received')
211    log = 'valid response for %s payment %s: %s' % (
212        payment.p_category, payment.p_id, str(form))
213    notify(grok.ObjectModifiedEvent(payment))
214    return True, msg, log
215
216# Requerying Etranzact Payoutlet payments
217
218# Expected response:
219# RECEIPT_NO=500191030486&PAYMENT_CODE=500854291572447457669&MERCHANT_CODE=700602WDUB
220# &TRANS_AMOUNT=200000.0&TRANS_DATE=2019/10/30
221# 15:13:47&TRANS_DESCR=Test%20Test%20Test-CLEARANCE%20-001-p5723474039401
222# &CUSTOMER_ID=p5723474039401&BANK_CODE=500&BRANCH_CODE=001
223# &SERVICE_ID=p5723474039401&CUSTOMER_NAME=Test%20Test%20Test
224# &CUSTOMER_ADDRESS=ASS-ENG&TELLER_ID=etzbankteller&USERNAME=%20
225# &PASSWORD=%20&BANK_NAME=eTranzact%20Intl%20Plc
226# &BRANCH_NAME=ETRANZACT&CHANNEL_NAME=Bank&PAYMENT_METHOD_NAME=Cash
227# &PAYMENT_CURRENCY=566&TRANS_TYPE=101&TRANS_FEE=0.0
228# &TYPE_NAME=CLEARANCE&LEAD_BANK_CODE=700&LEAD_BANK_NAME=eTranzact%20Intl%20Plc
229# COL1=2018/2019&COL2=Acceptance
230# Fee&COL3=ASS&COL4=ENG&COL5=BARTENL&COL6=400&COL7=ug_ft&COL8=N/A&COL9=11/12345
231# &COL10=damms005@gmail.com&COL11=None&COL12=&COL13=
232
233def query_payoutlet(host, terminal_id, confirmation_number, payment, https):
234    headers={"Content-type": "application/x-www-form-urlencoded",
235             "Accept": "text/plain"}
236    url = "/WebConnectPlus/query.jsp"
237    if https:
238        h = httplib.HTTPSConnection(host)
239    else:
240        h = httplib.HTTPConnection(host)
241    args = {'TERMINAL_ID': terminal_id,
242            'CONFIRMATION_NO': confirmation_number,
243            }
244    h.request('POST', url, urlencode(args), headers)
245    response = h.getresponse()
246    if response.status!=200:
247        return False, 'Connection error (%s, %s)' % (response.status, response.reason), None
248    raw = response.read()
249    # Remove empty lines
250    raw = raw.replace('\r\n','')
251    success = parse_qs(raw)
252    if not success.get('CUSTOMER_ID'):
253        msg = _('Invalid or unsuccessful callback: ${a}',
254            mapping = {'a': raw})
255        log = 'invalid callback for payment %s: %s' % (payment.p_id, raw)
256        payment.p_state = 'failed'
257        return False, msg, log
258    # We expect at least two parameters
259    if len(success) < 2:
260        msg = _('Invalid callback: ${a}', mapping = {'a': raw})
261        log = 'invalid callback for payment %s: %s' % (payment.p_id, raw)
262        payment.p_state = 'failed'
263        return False, msg, log
264    payment.r_code = u'ET'
265    payment.r_desc = u'%s' % success.get('TRANS_DESCR')[0]
266    payment.r_amount_approved = float(success.get('TRANS_AMOUNT')[0])
267    payment.r_card_num = None
268    payment.r_pay_reference = u'%s' % success.get('RECEIPT_NO')[0]
269    if payment.r_amount_approved != payment.amount_auth:
270        msg = _('Wrong amount')
271        log = 'wrong callback for payment %s: %s' % (payment.p_id, raw)
272        payment.p_state = 'failed'
273        return False, msg, log
274    customer_id = success.get('CUSTOMER_ID')[0]
275    if payment.p_id != customer_id:
276        msg = _('Wrong payment id')
277        log = 'wrong callback for payment %s: %s' % (payment.p_id, raw)
278        payment.p_state = 'failed'
279        return False, msg, log
280    log = 'valid callback for payment %s: %s' % (payment.p_id, raw)
281    msg = _('Successful callback received')
282    payment.p_state = 'paid'
283    payment.payment_date = datetime.utcnow()
284    return True, msg, log
Note: See TracBrowser for help on using the repository browser.