Index: /main/waeup.custom/trunk/src/waeup/custom/etranzact/__init__.py
===================================================================
--- /main/waeup.custom/trunk/src/waeup/custom/etranzact/__init__.py	(revision 7929)
+++ /main/waeup.custom/trunk/src/waeup/custom/etranzact/__init__.py	(revision 7929)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/waeup.custom/trunk/src/waeup/custom/etranzact/browser.py
===================================================================
--- /main/waeup.custom/trunk/src/waeup/custom/etranzact/browser.py	(revision 7929)
+++ /main/waeup.custom/trunk/src/waeup/custom/etranzact/browser.py	(revision 7929)
@@ -0,0 +1,125 @@
+## $Id$
+##
+## 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
+from xml.dom.minidom import parseString
+import grok
+from waeup.kofa.browser.layout import KofaPage, UtilityView
+from waeup.kofa.students.interfaces import IStudentOnlinePayment
+from waeup.kofa.students.browser import write_log_message
+from waeup.kofa.students.viewlets import RequestCallbackActionButton
+from waeup.custom.interfaces import MessageFactory as _
+from waeup.custom.utils.utils import actions_after_payment
+
+TERMINAL_ID = '0000000001'
+QUERY_URL =   'http://demo.etranzact.com:8080/WebConnect/queryPayoutletTransaction.jsp'
+
+class EtranzactEnterPinActionButton(RequestCallbackActionButton):
+    grok.order(5)
+    icon = 'actionicon_call.png'
+    text = _('Query eTranzact History')
+    target = 'enter_pin'
+
+class EtranzactEnterPinPage(KofaPage):
+    grok.context(IStudentOnlinePayment)
+    grok.name('enter_pin')
+    grok.template('enter_pin')
+    grok.require('waeup.payStudent')
+
+
+class EtranzactQueryHistoryPage(UtilityView, grok.View):
+    """ Query history of eTranzact payments
+    """
+    grok.context(IStudentOnlinePayment)
+    grok.name('query_history')
+    grok.require('waeup.payStudent')
+
+    def update(self, confirmation_number=None):
+        if self.context.p_state == 'paid':
+            self.flash(_('This ticket has already been paid.'))
+            return
+        student = self.context.getStudent()
+        postdict = {}
+        postdict['TERMINAL_ID'] = TERMINAL_ID
+        #postdict['RESPONSE_URL'] = 'http://dummy'
+        postdict['CONFIRMATION_NO'] = confirmation_number
+        data = urllib.urlencode(postdict)
+        try:
+            f = urllib.urlopen(url=QUERY_URL, data=data)
+            success = f.read()
+            if '-1' in success:
+                self.flash(_('Unsuccessful callback'))
+                write_log_message(self,'unsuccessful callback: %s' % self.context.p_id)
+                self.context.p_state = 'failed'
+                return
+            success = success.replace('%20',' ').split('&')
+            # We expect at least two parameters
+            if len(success) < 2:
+                self.flash(_('Invalid callback: ${a}',
+                    mapping = {'a': success}))
+                write_log_message(self,'invalid callback: %s' % self.context.p_id)
+                self.context.p_state = 'failed'
+                return
+            try:
+                success_dict = dict([tuple(i.split('=')) for i in success])
+            except ValueError:
+                self.flash(_('Invalid callback: ${a}',
+                    mapping = {'a': success}))
+                write_log_message(self,'invalid callback: %s' % self.context.p_id)
+                self.context.p_state = 'failed'
+                return
+        except IOError:
+            self.flash(_('eTranzact IOError'))
+            return
+        write_log_message(self,'callback received: %s' % success)
+
+        self.context.r_code = u'ET'
+        self.context.r_desc = u'%s' % success_dict.get('TRANS_DESCR')
+        self.context.r_amount_approved = float(success_dict.get('TRANS_AMOUNT',0.0))
+        self.context.r_card_num = None
+        self.context.r_pay_reference = u'%s' % success_dict.get('RECEIPT_NO')
+        
+        if self.context.r_amount_approved != self.context.amount_auth:
+            self.flash(_('Wrong amount'))
+            write_log_message(
+                self,'successful callback but wrong amount: %s'
+                % self.context.p_id)
+            self.context.p_state = 'failed'
+            return
+
+        if success_dict.get('PAYMENT_CODE') != self.context.p_id:
+            self.flash(_('Wrong transaction id'))
+            write_log_message(
+                self,'successful callback but wrong transaction id: %s'
+                % self.context.p_id)
+            self.context.p_state = 'failed'
+            return
+
+        write_log_message(self,'successful callback: %s' % self.context.p_id)
+
+        self.context.p_state = 'paid'
+        self.context.payment_date = datetime.now()
+
+        actions_after_payment(student, self.context, self)
+
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, '@@index'))
+        return
Index: /main/waeup.custom/trunk/src/waeup/custom/etranzact/tests.py
===================================================================
--- /main/waeup.custom/trunk/src/waeup/custom/etranzact/tests.py	(revision 7929)
+++ /main/waeup.custom/trunk/src/waeup/custom/etranzact/tests.py	(revision 7929)
@@ -0,0 +1,57 @@
+## $Id$
+##
+## Copyright (C) 2011 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 hurry.workflow.interfaces import IWorkflowState
+from waeup.kofa.students.tests.test_browser import StudentsFullSetup
+from waeup.custom.testing import FunctionalLayer
+
+class EtranzactTests(StudentsFullSetup):
+    """Tests foreTranzact payment gateway.
+    """
+
+    layer = FunctionalLayer
+
+    CONFIRMATION_NO = '500856521315472785095'
+
+    def test_etranzact_query_history(self):
+
+        # Managers can add online payment tickets
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.payments_student_path)
+        IWorkflowState(self.student).setState('cleared')
+        self.browser.open(self.payments_student_path + '/addop')
+        self.browser.getControl("Create ticket").click()
+        self.assertMatches('...ticket created...',
+                           self.browser.contents)
+        ctrl = self.browser.getControl(name='val_id')
+        value = ctrl.options[0]
+        self.browser.getLink(value).click()
+        self.assertMatches('...Amount Authorized...',
+                           self.browser.contents)
+        payment_url = self.browser.url
+        
+        self.browser.open(payment_url + '/query_history?confirmation_number=%s'
+            % self.CONFIRMATION_NO)
+        self.assertMatches('...Wrong amount...',
+                          self.browser.contents)
+
+        self.student['payments'][value].amount_auth = self.student[
+            'payments'][value].r_amount_approved
+        self.browser.open(payment_url + '/query_history?confirmation_number=%s'
+            % self.CONFIRMATION_NO)
+        self.assertMatches('...Wrong transaction id...',
+                          self.browser.contents)
Index: /main/waeup.custom/trunk/src/waeup/custom/interswitch/tests.py
===================================================================
--- /main/waeup.custom/trunk/src/waeup/custom/interswitch/tests.py	(revision 7928)
+++ /main/waeup.custom/trunk/src/waeup/custom/interswitch/tests.py	(revision 7929)
@@ -20,6 +20,6 @@
 from waeup.custom.testing import FunctionalLayer
 
-class StudentUITests(StudentsFullSetup):
-    """Tests for customized student class views and pages
+class InterswitchTests(StudentsFullSetup):
+    """Tests for the Interswitch payment gateway.
     """
 
@@ -37,7 +37,6 @@
             '&url=http://xyz') % (resp, apprAmt)
 
-    def test_manage_payments(self):
+    def test_callback(self):
 
-        # Managers can add online payment tickets
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.browser.open(self.payments_student_path)
