Index: /main/waeup.aaue/trunk/CHANGES.txt
===================================================================
--- /main/waeup.aaue/trunk/CHANGES.txt	(revision 11867)
+++ /main/waeup.aaue/trunk/CHANGES.txt	(revision 11868)
@@ -4,4 +4,6 @@
 1.2dev (unreleased)
 ===================
+
+* Add and configure Interswitch module.
 
 * Customize payment_slip_download_warning. Application payment tickets can be
Index: /main/waeup.aaue/trunk/src/waeup/aaue/etranzact/browser.py
===================================================================
--- /main/waeup.aaue/trunk/src/waeup/aaue/etranzact/browser.py	(revision 11867)
+++ /main/waeup.aaue/trunk/src/waeup/aaue/etranzact/browser.py	(revision 11868)
@@ -357,18 +357,5 @@
 # respectively. But then all interswitch.browser modules have to be extended.
 
-class InterswitchActionButtonStudent(InterswitchActionButtonStudent):
-
-    @property
-    def target_url(self):
-        return ''
-
-class InterswitchRequestWebserviceActionButtonStudent(
-    InterswitchRequestWebserviceActionButtonStudent):
-
-    @property
-    def target_url(self):
-        return ''
-
-#class InterswitchActionButtonApplicant(InterswitchActionButtonApplicant):
+#class InterswitchActionButtonStudent(InterswitchActionButtonStudent):
 
 #    @property
@@ -376,8 +363,21 @@
 #        return ''
 
-#class InterswitchRequestWebserviceActionButtonApplicant(
-#    InterswitchRequestWebserviceActionButtonApplicant):
+#class InterswitchRequestWebserviceActionButtonStudent(
+#    InterswitchRequestWebserviceActionButtonStudent):
 
 #    @property
 #    def target_url(self):
 #        return ''
+
+#class InterswitchActionButtonApplicant(InterswitchActionButtonApplicant):
+
+#    @property
+#    def target_url(self):
+#        return ''
+
+#class InterswitchRequestWebserviceActionButtonApplicant(
+#    InterswitchRequestWebserviceActionButtonApplicant):
+
+#    @property
+#    def target_url(self):
+#        return ''
Index: /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/browser.py
===================================================================
--- /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/browser.py	(revision 11867)
+++ /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/browser.py	(revision 11868)
@@ -65,7 +65,4 @@
     mac = 'CEF793CBBE838AA0CBB29B74D571113B4EA6586D3BA77E7CFA0B95E278364EFC4526ED7BD255A366CDDE11F1F607F0F844B09D93B16F7CFE87563B2272007AB3'
 
-    def interswitch_img_url(self):
-        return interswitch_img_url(self)
-
     def update(self):
 
@@ -105,4 +102,68 @@
         return
 
+class CustomInterswitchPageStudent(InterswitchPageStudent):
+    """ View which sends a POST request to the Interswitch
+    CollegePAY payment gateway.
+    """
+    grok.context(ICustomStudentOnlinePayment)
+    action = POST_ACTION
+    site_name = SITE_NAME
+    currency = CURRENCY
+    product_id = PRODUCT_ID
+    pay_item_id = '101'
+    mac = 'CEF793CBBE838AA0CBB29B74D571113B4EA6586D3BA77E7CFA0B95E278364EFC4526ED7BD255A366CDDE11F1F607F0F844B09D93B16F7CFE87563B2272007AB3'
+
+    def update(self):
+        error = self.init_update()
+        if error:
+            self.flash(error, type='danger')
+            self.redirect(self.url(self.context, '@@index'))
+            return
+        student = self.student
+        xmldict = self.xmldict
+        # Provider data
+        xmldict['detail_ref'] = self.context.p_id
+        xmldict['provider_acct'] = PROVIDER_ACCT
+        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
+        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
+        # Institution data
+        xmldict['institution_acct'] = '00000000'
+        xmldict['institution_bank_id'] = '00'
+        xmldict['institution_amt'] = '0.0'
+        provider_amt = 0.0
+        xmldict['provider_amt'] = 100 * provider_amt
+        xmldict['institution_item_name'] = self.category
+        xmldict['institution_name'] = INSTITUTION_NAME
+        xmldict['institution_amt'] = 100 * (
+            self.context.amount_auth - provider_amt - GATEWAY_AMT)
+        # Interswitch amount is not part of the xml data
+        if provider_amt == 0:
+            xmltext = """<payment_item_detail>
+<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
+<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
+</item_details>
+</payment_item_detail>""" % xmldict
+        else:
+            xmltext = """<payment_item_detail>
+<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
+<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
+<item_detail item_id="2" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
+</item_details>
+</payment_item_detail>""" % xmldict
+        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
+        self.context.provider_amt = provider_amt
+        self.context.gateway_amt = GATEWAY_AMT
+
+        hashargs = (
+            self.context.p_id +
+            PRODUCT_ID +
+            self.pay_item_id +
+            str(int(self.amount_auth)) +
+            self.site_redirect_url +
+            self.mac)
+        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
+
+        return
+
 
 class CustomInterswitchPaymentRequestWebservicePageApplicant(
@@ -114,2 +175,11 @@
     gateway_host = HOST
     gateway_url = URL
+
+class CustomInterswitchPaymentRequestWebservicePageStudent(
+    InterswitchPaymentRequestWebservicePageStudent):
+    """ Request webservice view for the CollegePAY gateway
+    """
+    grok.context(ICustomStudentOnlinePayment)
+    product_id = PRODUCT_ID
+    gateway_host = HOST
+    gateway_url = URL
Index: /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/tests.py
===================================================================
--- /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/tests.py	(revision 11867)
+++ /main/waeup.aaue/trunk/src/waeup/aaue/interswitch/tests.py	(revision 11868)
@@ -96,2 +96,125 @@
         self.assertMatches('...<span>Failed</span>...',
                           self.browser.contents)
+
+class InterswitchTestsStudents(StudentsFullSetup):
+    """Tests for the Interswitch payment gateway.
+    """
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(InterswitchTestsStudents, self).setUp()
+
+    def test_interswitch_form(self):
+        self.app['configuration']['2004'].school_fee_1 = 6666.0
+        # Manager can access InterswitchForm
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.payments_path)
+        IWorkflowState(self.student).setState('cleared')
+        self.student.nationality = u'NG'
+        self.browser.open(self.payments_path + '/addop')
+        self.browser.getControl(name="form.p_category").value = ['schoolfee']
+        self.browser.getControl("Create ticket").click()
+        self.assertMatches('...ticket created...',
+                           self.browser.contents)
+        ctrl = self.browser.getControl(name='val_id')
+        self.value = ctrl.options[0]
+        self.browser.getLink(self.value).click()
+        self.assertMatches('...Amount Authorized...',
+                           self.browser.contents)
+        self.assertMatches(
+            '...<span>6666.0</span>...',
+            self.browser.contents)
+        self.payment_url = self.browser.url
+        self.browser.getLink("CollegePAY", index=0).click()
+        # The total amount to be processed by Interswitch
+        # has been reduced by the Interswitch fee of 150 Nairas
+        self.assertMatches('...<input type="hidden" name="pay_item_id" value="101" />...',
+                           self.browser.contents)
+        self.assertMatches('...Total Amount Authorized:...',
+                           self.browser.contents)
+        self.assertEqual(self.student.current_mode, 'ug_ft')
+        self.assertMatches(
+            '...<input type="hidden" name="amount" value="666600" />...',
+            self.browser.contents)
+        self.assertMatches(
+            '...item_name="School Fee" item_amt="641600" bank_id="00" acct_num="00000000"...',
+            self.browser.contents)
+
+    @external_test
+    def test_webservice(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.payments_path)
+        IWorkflowState(self.student).setState('cleared')
+        self.student.nationality = u'NG'
+        self.browser.open(self.payments_path + '/addop')
+        self.browser.getControl(name="form.p_category").value = ['schoolfee']
+        self.browser.getControl("Create ticket").click()
+        self.assertMatches('...ticket created...',
+                           self.browser.contents)
+        ctrl = self.browser.getControl(name='val_id')
+        self.value = ctrl.options[0]
+        self.browser.getLink(self.value).click()
+        self.payment_url = self.browser.url
+        # First we have open InterswitchPageStudent to set provider_amt
+        # and gateway_amt
+        self.browser.open(self.payment_url + '/goto_interswitch')
+        # Now we can call the webservice
+        self.browser.open(self.payment_url + '/request_webservice')
+        self.assertMatches('...Unsuccessful callback...',
+                          self.browser.contents)
+        # The payment is now in state failed ...
+        self.assertMatches('...<span>Failed</span>...',
+                          self.browser.contents)
+        # ... and the catalog has been updated
+        cat = getUtility(ICatalog, name='payments_catalog')
+        results = list(
+            cat.searchResults(p_state=('failed', 'failed')))
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0].p_state, 'failed')
+
+        # Let's replace the p_id with a valid p_id of the Uniben
+        # live system. This is definitely not an appropriate
+        # solution for testing, but we have no choice since
+        # Interswitch doesn't provide any interface
+        # for testing.
+        payment = self.student['payments'][self.value]
+        payment.p_id = 'p3547789850240'
+        self.browser.open(self.payment_url + '/request_webservice')
+        self.assertMatches('...Callback amount does not match...',
+                          self.browser.contents)
+        # The payment is now in state failed ...
+        self.assertMatches('...<span>Failed</span>...',
+                          self.browser.contents)
+        # Let's replace the amount autorized with the amount of the
+        # live system payment
+        payment.amount_auth = payment.r_amount_approved
+        self.browser.open(self.payment_url + '/request_webservice')
+        self.assertMatches('...Successful payment...',
+                          self.browser.contents)
+        # The payment is now in state paid ...
+        self.assertMatches('...<span>Paid</span>...',
+                          self.browser.contents)
+        # ... and the catalog has been updated
+        cat = getUtility(ICatalog, name='payments_catalog')
+        results = list(
+            cat.searchResults(p_state=('paid', 'paid')))
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0].p_state, 'paid')
+        # Approval is logged in students.log ...
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'students.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            'zope.mgr - '
+            'waeup.aaue.interswitch.browser.CustomInterswitchPaymentRequestWebservicePageStudent - '
+            'X1000000 - successful schoolfee payment: p3547789850240\n'
+            in logcontent)
+        # ... and in payments.log
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'payments.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            '"zope.mgr",X1000000,p3547789850240,schoolfee,'
+            '12000.0,00,0.0,150.0,0.0,,,\n'
+            in logcontent)
