Index: /main/kofacustom.nigeria/trunk/CHANGES.txt
===================================================================
--- /main/kofacustom.nigeria/trunk/CHANGES.txt	(revision 13386)
+++ /main/kofacustom.nigeria/trunk/CHANGES.txt	(revision 13387)
@@ -5,5 +5,7 @@
 =====================
 
-- no changes yet
+- Implement Interswitch JSON transaction query. This webservice is
+  automatically used if a mac key is set in the request webservice
+  view.
 
 1.2 (2015-09-14)
Index: /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py
===================================================================
--- /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py	(revision 13386)
+++ /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py	(revision 13387)
@@ -83,4 +83,5 @@
     gateway_url = None
     https = True
+    mac = None
 
     def update(self):
@@ -92,5 +93,5 @@
             self.context, self.product_id,
             self.gateway_host, self.gateway_url,
-            self.https)
+            self.https, self.mac)
         student.writeLogMessage(self, log)
         if not success:
@@ -119,4 +120,5 @@
     gateway_url = None
     https = True
+    mac = None
 
     def update(self):
@@ -128,5 +130,5 @@
             self.context, self.product_id,
             self.gateway_host, self.gateway_url,
-            self.https)
+            self.https, self.mac)
         applicant.writeLogMessage(self, log)
         if not success:
Index: /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/helpers.py
===================================================================
--- /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/helpers.py	(revision 13386)
+++ /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/helpers.py	(revision 13387)
@@ -20,4 +20,7 @@
 from datetime import datetime
 import httplib
+import hashlib
+import json
+from urllib import urlencode
 import grok
 from xml.dom.minidom import parseString
@@ -44,4 +47,5 @@
     response = h.getresponse()
     return response
+
 
 def get_SOAP_response(product_id, transref, host, url, https):
@@ -64,5 +68,30 @@
     return response
 
-def query_interswitch(payment, product_id, host, url, https=False):
+
+def get_JSON_response(product_id, transref, host, url, https, mac, amount):
+    hashargs = product_id + transref + mac
+    hashvalue = hashlib.sha512(hashargs).hexdigest()
+    headers={
+        'Content-Type':'text/xml; charset=utf-8',
+        'Hash':hashvalue,
+    }
+    if https:
+        h = httplib.HTTPSConnection(host)
+    else:
+        h = httplib.HTTPConnection(host)
+    amount = int(100 * amount)
+    args = {'productid': product_id,
+            'transactionreference': transref,
+            'amount': amount}
+    url = '%s?' % url + urlencode(args)
+    h.request("GET", url, headers=headers)
+    response = h.getresponse()
+    if response.status!=200:
+        return {'error': 'Connection error (%s, %s)' % (response.status, response.reason)}
+    jsonout = response.read()
+    parsed_json = json.loads(jsonout)
+    return parsed_json
+
+def query_interswitch_SOAP(payment, product_id, host, url, https):
     sr = get_SOAP_response(product_id, payment.p_id, host, url, https)
     if sr.startswith('Connection error'):
@@ -110,4 +139,68 @@
     return True, msg, log
 
+def query_interswitch(payment, product_id, host, url, https, mac):
+    # If no mac mac key is given, fall back to deprecated SOAP method
+    # (Uniben, AAUA, FCEOkene).
+    if mac == None:
+        return query_interswitch_SOAP(payment, product_id, host, url, https)
+    jr = get_JSON_response(product_id, payment.p_id, host, url,
+                           https, mac, payment.amount_auth)
+    error = jr.get('error')
+    if error:
+        msg = log = error
+        return False, msg, log
+
+    # A typical JSON response
+    #  {u'SplitAccounts': [],
+    #   u'MerchantReference':u'p4210665523377',
+    #   u'PaymentReference':u'GTB|WEB|KPOLY|12-01-2015|013138',
+    #   u'TransactionDate':u'2015-01-12T13:43:39.27',
+    #   u'RetrievalReferenceNumber':u'000170548791',
+    #   u'ResponseDescription': u'Approved Successful',
+    #   u'Amount': 2940000,
+    #   u'CardNumber': u'2507',
+    #   u'ResponseCode': u'00',
+    #   u'LeadBankCbnCode': None,
+    #   u'LeadBankName': None}
+
+    if len(jr) != 11:
+        msg = _('Invalid callback: ${a}', mapping = {'a': str(jr)})
+        log = 'invalid callback for payment %s: %s' % (payment.p_id, str(jr))
+        return False, msg, log
+    payment.r_code = jr['ResponseCode']
+    payment.r_desc = jr['ResponseDescription']
+    payment.r_amount_approved = jr['Amount'] / 100.0
+    payment.r_card_num = jr['CardNumber']
+    payment.r_pay_reference = jr['PaymentReference']
+    payment.r_company = u'interswitch'
+    if payment.r_code != '00':
+        msg = _('Unsuccessful callback: ${a}', mapping = {'a': payment.r_desc})
+        log = 'unsuccessful callback for %s payment %s: %s' % (
+            payment.p_category, payment.p_id, payment.r_desc)
+        payment.p_state = 'failed'
+        notify(grok.ObjectModifiedEvent(payment))
+        return False, msg, log
+    if round(payment.r_amount_approved, 2) != round(payment.amount_auth, 2):
+        msg = _('Callback amount does not match.')
+        log = 'wrong callback for %s payment %s: %s' % (
+            payment.p_category, payment.p_id, str(jr))
+        payment.p_state = 'failed'
+        notify(grok.ObjectModifiedEvent(payment))
+        return False, msg, log
+    if jr['MerchantReference'] != payment.p_id:
+        msg = _('Callback transaction id does not match.')
+        log = 'wrong callback for %s payment %s: %s' % (
+            payment.p_category, payment.p_id, str(jr))
+        payment.p_state = 'failed'
+        notify(grok.ObjectModifiedEvent(payment))
+        return False, msg, log
+    payment.p_state = 'paid'
+    payment.payment_date = datetime.utcnow()
+    msg = _('Successful callback received')
+    log = 'valid callback for %s payment %s: %s' % (
+        payment.p_category, payment.p_id, str(jr))
+    notify(grok.ObjectModifiedEvent(payment))
+    return True, msg, log
+
 def write_payments_log(id, payment):
     payment.logger.info(
Index: /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py
===================================================================
--- /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py	(revision 13386)
+++ /main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py	(revision 13387)
@@ -31,5 +31,5 @@
 #   If you enable this, please make sure the external services
 #   do exist really and are not bothered by being spammed by a test programme.
-EXTERNAL_TESTS = True
+EXTERNAL_TESTS = False
 
 def external_test(func):
@@ -136,8 +136,9 @@
 
     @external_test
-    def test_query_interswitch(self):
+    def test_query_interswitch_SOAP(self):
         host = 'webpay.interswitchng.com'
         url = '/paydirect/services/TransactionQueryWs.asmx'
         https = True
+        mac = None
         product_id = '5845' # AAUE regular
         payment = StudentOnlinePayment()
@@ -145,5 +146,5 @@
         payment.amount_auth = 60250.0
         success, msg, log = query_interswitch(
-            payment, product_id, host, url, https)
+            payment, product_id, host, url, https, mac)
         self.assertEqual('Successful callback received', msg)
         self.assertTrue(success)
@@ -151,4 +152,37 @@
             '00:Approved Successful:6025000:3154:p4465649308559:'
             'ZIB|WEB|ABAL|3-11-2015|021336:000457580882' in log)
+
+    @external_test
+    def test_query_interswitch_JSON(self):
+        host = 'webpay.interswitchng.com'
+        url = '/paydirect/api/v1/gettransaction.json'
+        mac = '9718FA00B0F5070B388A9896ADCED9B2FB02D30F71E12E68BDADC63F6852A3496FF97D8A0F9DA9F753B911A49BB09BB87B55FD02046BD325C74C46C0123CF023'
+        https = True
+        product_id = '5845' # AAUE regular
+        payment = StudentOnlinePayment()
+        payment.p_id ='p4465649308559'
+        payment.amount_auth = 1.0
+        success, msg, log = query_interswitch(
+            payment, product_id, host, url, https, mac)
+        self.assertFalse(success)
+        self.assertTrue('Unsuccessful callback:' in msg)
+        self.assertTrue('Amount Inconsistency' in log)
+        payment.amount_auth = 60250.0
+        success, msg, log = query_interswitch(
+            payment, product_id, host, url, https, mac)
+        self.assertEqual('Successful callback received', msg)
+        self.assertTrue(success)
+        self.assertTrue(
+            "{u'SplitAccounts': [], "
+            "u'MerchantReference': u'p4465649308559', "
+            "u'PaymentReference': u'ZIB|WEB|ABAL|3-11-2015|021336', "
+            "u'TransactionDate': u'2015-11-03T16:40:54.487', "
+            "u'RetrievalReferenceNumber': u'000457580882', "
+            "u'ResponseDescription': u'Approved Successful', "
+            "u'Amount': 6025000, "
+            "u'CardNumber': u'3154', "
+            "u'ResponseCode': u'00', "
+            "u'LeadBankCbnCode': None, "
+            "u'LeadBankName': None}" in log)
 
 class InterswitchTestsApplicants(ApplicantsFullSetup):
