source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/remita/helpers.py @ 17936

Last change on this file since 17936 was 17907, checked in by Henrik Bettermann, 2 months ago

Remita's programmers may not know that case sensitivity matters
in programming languages.

  • Property svn:keywords set to Id
File size: 6.6 KB
Line 
1## $Id: helpers.py 17907 2024-08-29 06:29:26Z 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 remita module in custom packages.
19"""
20import grok
21from datetime import datetime
22import httplib
23import hashlib
24import json
25
26from zope.event import notify
27from kofacustom.nigeria.interfaces import MessageFactory as _
28
29def get_JSON_POST_response(
30        merchantId, serviceTypeId, api_key, orderId,
31        amount, responseurl, host, url, https, fullname, email, lineitems):
32    hashargs =  merchantId + serviceTypeId + orderId + str(amount) + responseurl + api_key
33    hashvalue = hashlib.sha512(hashargs).hexdigest()
34    headers={
35        'Content-Type':'application/json; charset=utf-8',
36    }
37    if https:
38        h = httplib.HTTPSConnection(host)
39    else:
40        h = httplib.HTTPConnection(host)
41    data = {
42            "merchantId":merchantId,
43            "serviceTypeId":serviceTypeId,
44            "totalAmount":amount,
45            "hash":hashvalue,
46            "payerName":fullname,
47            "payerEmail":email,
48            "orderId":orderId,
49            "responseurl":responseurl,
50            "lineItems":lineitems
51            }
52    try:
53        h.request("POST", url, body=json.dumps(data), headers=headers)
54        resp = h.getresponse()
55    except:
56        return {'error': 'Socket Error: Connection to Remita gateway refused.'}
57    if resp.status!=200:
58        return {'error': 'Connection Error (%s, %s)' % (resp.status, resp.reason)}
59    jsonout = resp.read()
60    try:
61        parsed_json = json.loads(jsonout[6:-1])
62    except ValueError:
63        return {'error': 'No JSON response'}
64    return parsed_json
65
66def get_payment_status_via_rrr(merchantId, api_key, RRR, host, https):
67    RRR = RRR.rstrip()
68    hashargs =  RRR + api_key + merchantId
69    hashvalue = hashlib.sha512(hashargs).hexdigest()
70    headers={
71        'Content-Type':'application/json; charset=utf-8',
72    }
73   
74    # url = '/remita/ecomm/%s/%s/%s/status.reg' % (merchantId, RRR, hashvalue)
75
76    # On 28/10/22 Balogun Olalekan wrote:
77    # Kindly update the transaction validation endpoint being used to the below.
78    # https://login.remita.net/remita/exapp/api/v1/send/api/echannelsvc/{{merchantId}}/{{rrr}}/{{apiHash}}/status.reg.
79
80    url = '/remita/exapp/api/v1/send/api/echannelsvc/%s/%s/%s/status.reg' % (merchantId, RRR, hashvalue)
81
82    if https:
83        h = httplib.HTTPSConnection(host)
84    else:
85        h = httplib.HTTPConnection(host)
86    try:
87        h.request("GET", url, headers=headers)
88        resp = h.getresponse()
89    except:
90        return {'error': 'Socket Error: Connection to Remita gateway refused.'}
91    if resp.status!=200:
92        return {'error': 'Connection error (%s, %s)' % (resp.status, resp.reason)}
93    jsonout = resp.read()
94    try:
95        parsed_json = json.loads(jsonout)
96    except ValueError:
97        return {'error': 'No JSON response'}
98    return parsed_json
99
100def query_remita(payment, merchantId, api_key, RRR, host, https, verify):
101
102    jr = get_payment_status_via_rrr(merchantId, api_key, RRR, host, https)
103    error = jr.get('error')
104    if error:
105        msg = log = error
106        return False, msg, log
107
108    # A typical JSON response
109    # {
110    # u'orderId': u'3456346346',
111    # u'status': u'021',
112    # u'amount': 1000.0,
113    # u'transactiontime': u'2017-07-31 11:17:24 AM',
114    # u'message': u'Transaction Pending',
115    # u'lineitems': [{u'status': u'021', u'lineItemsId': u'itemid1'},
116    #                {u'status': u'021', u'lineItemsId': u'itemid2'}
117    #               ],
118    # u'RRR': u'280007640804'}
119
120    payment.r_code = jr['status']
121    payment.r_desc = jr['message']
122    payment.r_amount_approved = jr['amount']
123    try:
124        payment.r_pay_reference = jr['RRR']
125    except KeyError:
126        # Remita's programmers may not know that case sensitivity matters
127        # in programming languages.
128        try:
129            payment.r_pay_reference = jr['rrr']
130        except KeyError:
131            msg = _('Error message from Remita: ${a}', mapping = {'a': payment.r_desc})
132            log = 'unsuccessful response for %s payment %s: %s' % (
133                payment.p_category, payment.p_id, str(jr))
134            payment.p_state = 'failed'
135            notify(grok.ObjectModifiedEvent(payment))
136            return False, msg, log
137    #payment.r_company = u'remita'
138    if payment.r_code not in ('00', '01'):
139        msg = _('Unsuccessful response: ${a}', mapping = {'a': payment.r_desc})
140        log = 'unsuccessful response for %s payment %s: %s' % (
141            payment.p_category, payment.p_id, payment.r_desc)
142        payment.p_state = 'failed'
143        notify(grok.ObjectModifiedEvent(payment))
144        return False, msg, log
145    if round(payment.r_amount_approved/10.0, 0) != round(
146        payment.amount_auth/10.0, 0):
147        msg = _('Response amount does not match.')
148        log = 'wrong response for %s payment %s: %s' % (
149            payment.p_category, payment.p_id, str(jr))
150        payment.p_state = 'failed'
151        notify(grok.ObjectModifiedEvent(payment))
152        return False, msg, log
153    orderId = jr.get('orderId', None)
154    if orderId and orderId != payment.p_id:
155        msg = _('Response order id does not match.')
156        log = 'wrong response for %s payment %s: %s' % (
157            payment.p_category, payment.p_id, str(jr))
158        payment.p_state = 'failed'
159        notify(grok.ObjectModifiedEvent(payment))
160        return False, msg, log
161    payment.p_state = 'paid'
162    if not verify:
163        payment.payment_date = datetime.utcnow()
164    msg = _('Successful response received')
165    log = 'valid response for %s payment %s: %s' % (
166        payment.p_category, payment.p_id, str(jr))
167    notify(grok.ObjectModifiedEvent(payment))
168    return True, msg, log
169
170def write_payments_log(id, payment):
171    payment.logger.info(
172        '%s,%s,%s,%s,%s,%s,%s,%s,,,' % (
173        id, payment.p_id, payment.p_category,
174        payment.amount_auth, payment.r_code,
175        payment.provider_amt, payment.gateway_amt,
176        payment.thirdparty_amt))
Note: See TracBrowser for help on using the repository browser.