Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/applicantsbrowser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/applicantsbrowser.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/applicantsbrowser.py	(revision 15702)
@@ -40,5 +40,5 @@
     try:
         return getattr(grok.getSite()['configuration'][str(session)],
-            'etranzact_enabled', False)
+            'etranzact_webconnect_enabled', False)
     except KeyError:
         return False
@@ -50,5 +50,5 @@
     grok.require('waeup.payApplicant')
     icon = 'actionicon_pay.png'
-    text = _('Pay via eTranzact')
+    text = _('Pay via Etranzact')
     target = 'goto_etranzact'
 
@@ -67,5 +67,5 @@
     grok.require('waeup.payApplicant')
     icon = 'actionicon_call.png'
-    text = _('Requery eTranzact History')
+    text = _('Requery Etranzact History')
     target = 'requery_history'
 
@@ -79,5 +79,5 @@
 
 class EtranzactPageApplicant(KofaPage):
-    """ View which sends a POST request to the eTranzact payment gateway.
+    """ View which sends a POST request to the Etranzact payment gateway.
     """
     grok.context(INigeriaApplicantOnlinePayment)
@@ -85,5 +85,5 @@
     grok.template('goto_etranzact')
     grok.require('waeup.payApplicant')
-    label = _('Pay via eTranzact')
+    label = _('Pay via Etranzact')
     submit_button = _('Pay now')
 
@@ -102,5 +102,5 @@
     def init_update(self):
         if self.context.p_state == 'paid':
-            return _("Payment ticket can't be re-sent to eTranzact.")
+            return _("Payment ticket can't be re-sent to Etranzact.")
         now = datetime.utcnow()
         if self.context.creation_date.tzinfo is not None:
@@ -112,9 +112,9 @@
         # In contrast to the procedure in the Remita and Interswitch modules,
         # we do not call requery_history but receive and evaluate
-        # the response form from eTranzact directly. This is possible
-        # because eTranzact provides the FINAL_CHECKSUM hash value
+        # the response form from Etranzact directly. This is possible
+        # because Etranzact provides the FINAL_CHECKSUM hash value
         # which authenticates the response.
         self.responseurl = self.url(self.context, 'receive_etranzact')
-        # Already now it becomes a eTranzact payment
+        # Already now it becomes a Etranzact payment
         self.context.r_company = u'etranzact'
         hashargs = 	self.amount + self.terminal_id+self.transaction_id \
@@ -166,5 +166,5 @@
 
 class EtranzactRequestPaymentStatusPageApplicant(UtilityView, grok.View):
-    """ Request webservice view for the eTranzact gateway.
+    """ Request webservice view for the Etranzact gateway.
     """
     grok.context(INigeriaApplicantOnlinePayment)
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/browser_templates/enterpin.pt
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/browser_templates/enterpin.pt	(revision 15702)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/browser_templates/enterpin.pt	(revision 15702)
@@ -0,0 +1,11 @@
+<form method="POST" class="form-inline" tal:attributes="action view/action">
+<br />
+  <div class="form-group half">
+    <input name="confirmation_number" type="text" class="form-control" maxlength=""
+           value="" tal:attributes="placeholder view/placeholder"/>
+  </div>
+  <div class="form-group">
+    <input type="submit" name="SUBMIT" class="btn btn-primary"
+           tal:attributes="value view/buttonname" />
+  </div>
+</form>
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/helpers.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/helpers.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/helpers.py	(revision 15702)
@@ -135,6 +135,6 @@
 def process_response(payment, form, view, verify):
     if not form or not form.get('SUCCESS', None):
-        msg = _('No valid response from eTranzact.')
-        log = 'No valid response from eTranzact for payment %s' % payment.p_id
+        msg = _('No valid response from Etranzact.')
+        log = 'No valid response from Etranzact for payment %s' % payment.p_id
         payment.p_state = 'failed'
         notify(grok.ObjectModifiedEvent(payment))
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletbrowser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletbrowser.py	(revision 15702)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletbrowser.py	(revision 15702)
@@ -0,0 +1,236 @@
+## $Id: browser.py 15346 2019-03-06 22:19:56Z henrik $
+##
+## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+from datetime import datetime
+import httplib
+import urllib
+import urllib2
+import re
+from xml.dom.minidom import parseString
+import grok
+from zope.component import getUtility
+from zope.catalog.interfaces import ICatalog
+from waeup.kofa.interfaces import IUniversity, CLEARED
+from waeup.kofa.payments.interfaces import IPayer
+from waeup.kofa.webservices import PaymentDataWebservice
+from waeup.kofa.browser.layout import KofaPage, UtilityView
+from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
+from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
+from kofacustom.nigeria.interswitch.browser import (
+    InterswitchActionButtonStudent,
+    InterswitchRequestWebserviceActionButtonStudent,
+    InterswitchActionButtonApplicant,
+    InterswitchRequestWebserviceActionButtonApplicant)
+from kofacustom.nigeria.interfaces import MessageFactory as _
+from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
+from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
+from kofacustom.nigeria.etranzact.tests import PAYOUTLET_QUERY_URL, TERMINAL_ID
+
+grok.templatedir('browser_templates')
+
+def module_activated(session):
+    try:
+        return getattr(grok.getSite()['configuration'][str(session)],
+            'etranzact_payoutlet_enabled', False)
+    except KeyError:
+        return False
+
+# Requerying Etranzact Payoutlet payments
+
+def query_payoutlet(confirmation_number, payment, terminal_id, query_url):
+    postdict = {}
+    postdict['TERMINAL_ID'] = terminal_id
+    postdict['RESPONSE_URL'] = 'http://dummy'
+    postdict['CONFIRMATION_NO'] = confirmation_number
+    data = urllib.urlencode(postdict)
+    payment.conf_number = confirmation_number
+    try:
+        # Etranzact only accepts HTTP 1.1 requests. Therefore
+        # the urllib2 package is required here.
+        f = urllib2.urlopen(url=query_url, data=data)
+        success = f.read()
+        success = success.replace('\r\n','')
+        # Etranzact sends strange HTML tags which must be removed.
+        success = re.sub("<.*?>", "", success)
+        if 'CUSTOMER_ID' not in success:
+            msg = _('Invalid or unsuccessful callback: ${a}',
+                mapping = {'a': success})
+            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
+            payment.p_state = 'failed'
+            return False, msg, log
+        success = success.replace('%20',' ').split('&')
+        # We expect at least two parameters
+        if len(success) < 2:
+            msg = _('Invalid callback: ${a}', mapping = {'a': success})
+            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
+            payment.p_state = 'failed'
+            return False, msg, log
+        try:
+            success_dict = dict([tuple(i.split('=')) for i in success])
+        except ValueError:
+            msg = _('Invalid callback: ${a}', mapping = {'a': success})
+            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
+            payment.p_state = 'failed'
+            return False, msg, log
+    except IOError:
+        msg = _('Etranzact IOError')
+        log = 'Etranzact IOError'
+        return False, msg, log
+    payment.r_code = u'ET'
+    payment.r_company = u'etranzact'
+    payment.r_desc = u'%s' % success_dict.get('TRANS_DESCR')
+    payment.r_amount_approved = float(success_dict.get('TRANS_AMOUNT',0.0))
+    payment.r_card_num = None
+    payment.r_pay_reference = u'%s' % success_dict.get('RECEIPT_NO')
+    if payment.r_amount_approved != payment.amount_auth:
+        msg = _('Wrong amount')
+        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
+        payment.p_state = 'failed'
+        return False, msg, log
+    customer_id = success_dict.get('CUSTOMER_ID')
+    if payment.p_id != customer_id:
+        msg = _('Wrong payment id')
+        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
+        payment.p_state = 'failed'
+        return False, msg, log
+    log = 'valid callback for payment %s: %s' % (payment.p_id, success)
+    msg = _('Successful callback received')
+    payment.p_state = 'paid'
+    payment.payment_date = datetime.utcnow()
+    return True, msg, log
+
+class EtranzactEnterPinActionButtonApplicant(APABApplicant):
+    grok.context(INigeriaApplicantOnlinePayment)
+    grok.require('waeup.payApplicant')
+    grok.order(3)
+    icon = 'actionicon_call.png'
+    text = _('Enter Etranzact PIN')
+    target = 'enterpin'
+
+    @property
+    def target_url(self):
+        if not module_activated(self.context.__parent__.__parent__.year):
+            return ''
+        if self.context.p_state != 'unpaid':
+            return ''
+        return self.view.url(self.view.context, self.target)
+
+class EtranzactEnterPinActionButtonStudent(APABStudent):
+    grok.context(INigeriaStudentOnlinePayment)
+    grok.require('waeup.payStudent')
+    grok.order(3)
+    icon = 'actionicon_call.png'
+    text = _('Enter Etranzact PIN')
+    target = 'enterpin'
+
+    @property
+    def target_url(self):
+        if not module_activated(self.context.student.current_session):
+            return ''
+        if self.context.p_state != 'unpaid':
+            return ''
+        return self.view.url(self.view.context, self.target)
+
+class EtranzactEnterPinPageStudent(KofaPage):
+    """
+    """
+    grok.context(INigeriaStudentOnlinePayment)
+    grok.name('enterpin')
+    grok.template('enterpin')
+    grok.require('waeup.payStudent')
+
+    buttonname = _('Submit to Etranzact')
+    label = _('Requery Etranzact History')
+    action = 'query_payoutlet_history'
+    placeholder = _('Confirmation Number (PIN)')
+
+    def update(self):
+        if not module_activated(self.context.student.current_session):
+            return
+        super(EtranzactEnterPinPageStudent, self).update()
+        return
+
+class EtranzactEnterPinPageApplicant(EtranzactEnterPinPageStudent):
+    """
+    """
+    grok.require('waeup.payApplicant')
+    grok.context(INigeriaApplicantOnlinePayment)
+
+class EtranzactQueryHistoryPageStudent(UtilityView, grok.View):
+    """ Query history of Etranzact payments
+    """
+    grok.context(INigeriaStudentOnlinePayment)
+    grok.name('query_payoutlet_history')
+    grok.require('waeup.payStudent')
+    terminal_id = TERMINAL_ID
+    query_url = PAYOUTLET_QUERY_URL
+
+    def update(self, confirmation_number=None):
+        if not module_activated(self.context.student.current_session):
+            return
+        if self.context.p_state == 'paid':
+            self.flash(_('This ticket has already been paid.'))
+            return
+        student = self.context.student
+        success, msg, log = query_payoutlet(
+            confirmation_number,self.context, self.terminal_id, self.query_url)
+        student.writeLogMessage(self, log)
+        if not success:
+            self.flash(msg)
+            return
+        flashtype, msg, log = self.context.doAfterStudentPayment()
+        if log is not None:
+            student.writeLogMessage(self, log)
+        self.flash(msg, type=flashtype)
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, '@@index'))
+        return
+
+class EtranzactQueryHistoryPageApplicant(UtilityView, grok.View):
+    """ Query history of Etranzact payments
+    """
+    grok.context(INigeriaApplicantOnlinePayment)
+    grok.name('query_payoutlet_history')
+    grok.require('waeup.payApplicant')
+    terminal_id = TERMINAL_ID
+    query_url = PAYOUTLET_QUERY_URL
+
+    def update(self, confirmation_number=None):
+        if not module_activated(self.context.__parent__.__parent__.year):
+            return
+        ob_class = self.__implemented__.__name__
+        if self.context.p_state == 'paid':
+            self.flash(_('This ticket has already been paid.'))
+            return
+        applicant = self.context.__parent__
+        success, msg, log = query_payoutlet(
+            confirmation_number,self.context, self.terminal_id, self.query_url)
+        applicant.writeLogMessage(self, log)
+        if not success:
+            self.flash(msg)
+            return
+        flashtype, msg, log = self.context.doAfterApplicantPayment()
+        if log is not None:
+            applicant.writeLogMessage(self, log)
+        self.flash(msg, type=flashtype)
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, '@@index'))
+        return
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletwebservice.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletwebservice.py	(revision 15702)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/payoutletwebservice.py	(revision 15702)
@@ -0,0 +1,135 @@
+## $Id: browser.py 15346 2019-03-06 22:19:56Z henrik $
+##
+## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+
+import grok
+from zope.component import getUtility
+from zope.catalog.interfaces import ICatalog
+from waeup.kofa.interfaces import academic_sessions_vocab, IKofaUtils
+from waeup.kofa.payments.interfaces import IPayer
+from waeup.kofa.webservices import PaymentDataWebservice
+
+ERROR_PART1 = (
+        'PayeeName=N/A~'
+        + 'Faculty=N/A~'
+        + 'Department=N/A~'
+        + 'Level=N/A~'
+        + 'ProgrammeType=N/A~'
+        + 'StudyType=N/A~'
+        + 'Session=N/A~'
+        + 'PayeeID=N/A~'
+        + 'Amount=N/A~'
+        + 'FeeStatus=')
+ERROR_PART2 = (
+        '~Semester=N/A~'
+        + 'PaymentType=N/A~'
+        + 'MatricNumber=N/A~'
+        + 'Email=N/A~'
+        + 'PhoneNumber=N/A')
+
+class NigeriaPaymentDataWebservice(PaymentDataWebservice):
+    """A simple webservice to publish payment and payer details on request from
+    accepted IP addresses without authentication.
+
+    Etranzact is asking for the PAYEE_ID which is indeed misleading.
+    These are not the data of the payee but of the payer. And it's
+    not the id of the payer but of the payment.
+    """
+    grok.name('feerequest')
+
+    #ACCEPTED_IP = ('195.219.3.181', '195.219.3.184')
+    ACCEPTED_IP = None
+
+    CATEGORY_MAPPING = {
+        'SCHOOLFEE': ('schoolfee',),
+         }
+
+    def update(self, PAYEE_ID=None, PAYMENT_TYPE=None):
+
+        if PAYEE_ID == None:
+            self.output = ERROR_PART1 + 'Missing PAYEE_ID' + ERROR_PART2
+            return
+        real_ip = self.request.get('HTTP_X_FORWARDED_FOR', None)
+        # We can forego the logging once eTranzact payments run smoothly
+        # and the accepted IP addresses are used.
+        if real_ip:
+            self.context.logger.info('PaymentDataWebservice called: %s' % real_ip)
+        if real_ip  and self.ACCEPTED_IP:
+            if real_ip not in  self.ACCEPTED_IP:
+                self.output = ERROR_PART1 + 'Wrong IP address' + ERROR_PART2
+                return
+        payment_cats_dict = getUtility(IKofaUtils).PAYMENT_CATEGORIES
+        if PAYMENT_TYPE not in self.CATEGORY_MAPPING.keys() \
+            and PAYMENT_TYPE.lower() not in payment_cats_dict.keys():
+            self.output = ERROR_PART1 + 'Invalid PAYMENT_TYPE' + ERROR_PART2
+            return
+        cat = getUtility(ICatalog, name='payments_catalog')
+        results = list(cat.searchResults(p_id=(PAYEE_ID, PAYEE_ID)))
+        if len(results) != 1:
+            self.output = ERROR_PART1 + 'Invalid PAYEE_ID' + ERROR_PART2
+            return
+        amount = results[0].amount_auth
+        payment_type = results[0].category
+        p_category = results[0].p_category
+        programme_type = results[0].p_item
+        if not programme_type:
+            programme_type = 'N/A'
+        academic_session = academic_sessions_vocab.getTerm(
+            results[0].p_session).title
+        status = results[0].p_state
+        if status == 'paid':
+            self.output = ERROR_PART1 + 'PAYEE_ID already used' + ERROR_PART2
+            return
+        if p_category not in self.CATEGORY_MAPPING.get('PAYMENT_TYPE', []) \
+            and p_category != PAYMENT_TYPE.lower():
+            self.output = ERROR_PART1 + 'Wrong PAYMENT_TYPE' + ERROR_PART2
+            return
+        try:
+            owner = IPayer(results[0])
+            full_name = owner.display_fullname
+            matric_no = owner.matric_number
+            faculty = owner.faculty
+            department = owner.department
+            study_type = owner.current_mode
+            email = owner.email
+            phone = owner.phone
+            level = owner.current_level
+        except (TypeError, AttributeError):
+            self.output = ERROR_PART1 +  'Unknown error' + ERROR_PART2
+            return
+        self.output = (
+            'PayeeName=%s~' +
+            'Faculty=%s~' +
+            'Department=%s~' +
+            'Level=%s~' +
+            'ProgrammeType=%s~' +
+            'StudyType=%s~' +
+            'Session=%s~' +
+            'PayeeID=%s~' +
+            'Amount=%s~' +
+            'FeeStatus=%s~' +
+            'Semester=N/A~' +
+            'PaymentType=%s~' +
+            'MatricNumber=%s~' +
+            'Email=%s~' +
+            'PhoneNumber=%s'
+
+            ) % (full_name, faculty,
+            department, level, programme_type, study_type,
+            academic_session, PAYEE_ID, amount, status, payment_type,
+            matric_no, email, phone)
+        return
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/studentsbrowser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/studentsbrowser.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/studentsbrowser.py	(revision 15702)
@@ -40,5 +40,5 @@
     try:
         return getattr(grok.getSite()['configuration'][str(session)],
-            'etranzact_enabled', False)
+            'etranzact_webconnect_enabled', False)
     except KeyError:
         return False
@@ -50,5 +50,5 @@
     grok.require('waeup.payStudent')
     icon = 'actionicon_pay.png'
-    text = _('Pay via eTranzact')
+    text = _('Pay via Etranzact')
     target = 'goto_etranzact'
 
@@ -67,5 +67,5 @@
     grok.require('waeup.payStudent')
     icon = 'actionicon_call.png'
-    text = _('Requery eTranzact History')
+    text = _('Requery Etranzact History')
     target = 'requery_history'
 
@@ -79,5 +79,5 @@
 
 class EtranzactPageStudent(KofaPage):
-    """ View which sends a POST request to the eTranzact payment gateway.
+    """ View which sends a POST request to the Etranzact payment gateway.
     """
     grok.context(INigeriaStudentOnlinePayment)
@@ -85,5 +85,5 @@
     grok.template('goto_etranzact')
     grok.require('waeup.payStudent')
-    label = _('Pay via eTranzact')
+    label = _('Pay via Etranzact')
     submit_button = _('Pay now')
 
@@ -102,5 +102,5 @@
     def init_update(self):
         if self.context.p_state == 'paid':
-            return _("Payment ticket can't be re-sent to eTranzact.")
+            return _("Payment ticket can't be re-sent to Etranzact.")
         now = datetime.utcnow()
         if self.context.creation_date.tzinfo is not None:
@@ -112,9 +112,9 @@
         # In contrast to the procedure in the Remita and Interswitch modules,
         # we do not call requery_history but receive and evaluate
-        # the response form from eTranzact directly. This is possible
-        # because eTranzact provides the FINAL_CHECKSUM hash value
+        # the response form from Etranzact directly. This is possible
+        # because Etranzact provides the FINAL_CHECKSUM hash value
         # which authenticates the response.
         self.responseurl = self.url(self.context, 'receive_etranzact')
-        # Already now it becomes a eTranzact payment
+        # Already now it becomes a Etranzact payment
         self.context.r_company = u'etranzact'
         hashargs = 	self.amount + self.terminal_id+self.transaction_id \
@@ -166,5 +166,5 @@
 
 class EtranzactRequestPaymentStatusPageStudent(UtilityView, grok.View):
-    """ Request webservice view for the eTranzact gateway.
+    """ Request webservice view for the Etranzact gateway.
     """
     grok.context(INigeriaStudentOnlinePayment)
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/tests.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/tests.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/tests.py	(revision 15702)
@@ -48,6 +48,7 @@
 SECRET_KEY = 'DEMO_KEY'
 LOGO_URL = 'https://iuokada.waeup.org/static_custom/iou_logo.png'
-
-# Valid transaction id in eTranzact system
+PAYOUTLET_QUERY_URL = 'http://demo.etranzact.com/WebConnectPlus/query.jsp'
+
+# Valid transaction id in Etranzact system
 TID = 'p5689785145198'
 
@@ -143,5 +144,5 @@
         # which seems to be valid till next restart of the demo portal.
         raw, formvars = query_history(HOST, self.terminal_id, TID, HTTPS)
-        # Now eTranzact is redirecting but the response is still useless
+        # Now Etranzact is redirecting but the response is still useless
         self.assertTrue('Redirecting...' in raw)
         self.assertEqual(formvars['TRANSACTION_ID'], TID)
@@ -158,5 +159,6 @@
         configuration = SessionConfiguration()
         configuration.academic_session = datetime.now().year - 2
-        configuration.etranzact_enabled = True
+        configuration.etranzact_webconnect_enabled = True
+        configuration.etranzact_payoutlet_enabled = True
         self.app['configuration'].addSessionConfiguration(configuration)
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
@@ -176,6 +178,6 @@
     @external_test
     def test_applicant_views(self):
-        # Manager can access eTranzact form
-        self.browser.getLink("Pay via eTranzact").click()
+        # Manager can access Etranzact form
+        self.browser.getLink("Pay via Etranzact").click()
         self.assertTrue("Pay now" in self.browser.contents)
         # Means of testing end here.
@@ -183,5 +185,5 @@
         self.payment.p_id = TID
         self.browser.open(self.payment_url)
-        self.browser.getLink("Requery eTranzact History").click()
+        self.browser.getLink("Requery Etranzact History").click()
         self.assertTrue('Wrong checksum.' in self.browser.contents)
         # ... probably because responseurl of the transaction stored in the
@@ -199,5 +201,6 @@
     def setUp(self):
         super(EtranzactTestsStudents, self).setUp()
-        self.app['configuration']['2004'].etranzact_enabled = True
+        self.app['configuration']['2004'].etranzact_webconnect_enabled = True
+        self.app['configuration']['2004'].etranzact_payoutlet_enabled = True
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.browser.open(self.payments_path)
@@ -218,9 +221,8 @@
         self.payment = self.student['payments'][self.value]
 
-
     @external_test
     def test_student_views(self):
-        # Manager can access eTranzact form
-        self.browser.getLink("Pay via eTranzact").click()
+        # Manager can access Etranzact form
+        self.browser.getLink("Pay via Etranzact").click()
         self.assertTrue("Pay now" in self.browser.contents)
         # Means of testing end here.
@@ -228,5 +230,5 @@
         self.payment.p_id = TID
         self.browser.open(self.payment_url)
-        self.browser.getLink("Requery eTranzact History").click()
+        self.browser.getLink("Requery Etranzact History").click()
         self.assertTrue('Wrong checksum.' in self.browser.contents)
         # ... probably because responseurl and amount stored in the
@@ -235,2 +237,8 @@
         # Means of testing end here again.
         return
+
+    @external_test
+    def test_student_payoutlet_views(self):
+        self.browser.getLink("Enter Etranzact PIN").click()
+        self.browser.getControl(name="confirmation_number").value = '1234'
+        self.browser.getControl("Submit to Etranzact").click()
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interfaces.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interfaces.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interfaces.py	(revision 15702)
@@ -129,6 +129,11 @@
         )
 
-    etranzact_enabled = schema.Bool(
-        title = _(u'eTranzact integration enabled'),
+    etranzact_webconnect_enabled = schema.Bool(
+        title = _(u'Etranzact Webconnect integration enabled'),
+        default = False,
+        )
+
+    etranzact_payoutlet_enabled = schema.Bool(
+        title = _(u'Etranzact Payoutlet integration enabled'),
         default = False,
         )
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/browser.py	(revision 15702)
@@ -49,5 +49,5 @@
     grok.require('waeup.payStudent')
     icon = 'actionicon_pay.png'
-    text = _('CollegePAY')
+    text = _('Pay via Interswitch')
     target = 'goto_interswitch'
 
@@ -78,5 +78,5 @@
     grok.require('waeup.payStudent')
     icon = 'actionicon_call.png'
-    text = _('Requery CollegePAY')
+    text = _('Requery Interswitch History')
     target = 'request_webservice'
 
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py	(revision 15702)
@@ -71,5 +71,5 @@
     def test_interswitch_form(self):
         # Manager can access InterswitchForm
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         # The total amount to be processed by Interswitch
         # has been reduced by the Interswitch fee of 150 Nairas
@@ -100,5 +100,5 @@
         self.assertEqual(self.student['payments'][value].gateway_amt, 0.0)
         self.browser.getLink(value).click()
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         # Split amounts have been set.
         self.assertEqual(self.student['payments'][value].provider_amt, 0.0)
@@ -112,5 +112,5 @@
     def test_interswitch_form_ticket_expired(self):
         # Manager can access InterswitchForm
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches('...<input type="hidden" name="pay_item_id" />...',
                            self.browser.contents)
@@ -124,5 +124,5 @@
         self.payment.creation_date -= delta
         self.browser.open(self.payment_url)
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches(
             '...This payment ticket is too old. Please create a new ticket...',
@@ -131,5 +131,5 @@
         self.payment.creation_date += delta
         self.browser.open(self.payment_url)
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches('...Total Amount Authorized:...',
                            self.browser.contents)
@@ -139,5 +139,5 @@
         # We should not have TZ data in timestamps processed, but it looks
         # like we get some with imports :-/
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         delta = timedelta(days=8)
         self.payment.creation_date -= delta
@@ -146,5 +146,5 @@
             self.payment.creation_date)
         self.browser.open(self.payment_url)
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches(
             '...This payment ticket is too old. Please create a new ticket...',
@@ -153,5 +153,5 @@
         self.payment.creation_date += delta
         self.browser.open(self.payment_url)
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches('...Total Amount Authorized:...',
                            self.browser.contents)
@@ -240,5 +240,5 @@
             self.browser.contents)
         # Manager can access InterswitchForm
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches('...Total Amount Authorized:...',
                            self.browser.contents)
@@ -249,5 +249,5 @@
         self.payment.creation_date -= delta
         self.browser.open(self.payment_url)
-        self.browser.getLink("CollegePAY", index=0).click()
+        self.browser.getLink("Pay via Interswitch", index=0).click()
         self.assertMatches(
             '...This payment ticket is too old. Please create a new ticket...',
@@ -255,6 +255,6 @@
         delta = timedelta(days=2)
         self.payment.creation_date += delta
-        self.browser.getLink("CollegePAY", index=0).click()
-        self.assertMatches('...Total Amount Authorized:...',
-                           self.browser.contents)
-
+        self.browser.getLink("Pay via Interswitch", index=0).click()
+        self.assertMatches('...Total Amount Authorized:...',
+                           self.browser.contents)
+
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/payments/interfaces.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/payments/interfaces.py	(revision 15687)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/payments/interfaces.py	(revision 15702)
@@ -78,5 +78,5 @@
         vocabulary = SimpleKofaVocabulary(
             (_('Interswitch'), 'interswitch'),
-            (_('eTranzact'), 'etranzact'),
+            (_('Etranzact'), 'etranzact'),
             (_('Remita'), 'remita'),
             (_('Scratch Card'), 'sc'),
