source: main/waeup.aaue/trunk/src/waeup/aaue/etranzact/browser.py @ 9919

Last change on this file since 9919 was 9904, checked in by Henrik Bettermann, 12 years ago

Disable Interswitch action buttons.

  • Property svn:keywords set to Id
File size: 11.0 KB
RevLine 
[7929]1## $Id: browser.py 9904 2013-01-22 08:05:20Z henrik $
2##
3## Copyright (C) 2012 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##
18from datetime import datetime
19import httplib
20import urllib
21from xml.dom.minidom import parseString
22import grok
[8704]23from zope.component import getUtility
24from zope.catalog.interfaces import ICatalog
[8698]25from waeup.kofa.interfaces import IUniversity
[8704]26from waeup.kofa.payments.interfaces import IPaymentWebservice
[7929]27from waeup.kofa.browser.layout import KofaPage, UtilityView
[8430]28from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
29from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
[8710]30from waeup.aaue.interfaces import academic_sessions_vocab
[9904]31from kofacustom.nigeria.interswitch.browser import (
32    InterswitchActionButtonStudent,
33    InterswitchRequestWebserviceActionButtonStudent,
34    InterswitchActionButtonApplicant,
35    InterswitchRequestWebserviceActionButtonApplicant)
[8444]36from waeup.aaue.interfaces import MessageFactory as _
37from waeup.aaue.students.interfaces import ICustomStudentOnlinePayment
38from waeup.aaue.applicants.interfaces import ICustomApplicantOnlinePayment
[7929]39
[8769]40#ACCEPTED_IP = ('195.219.3.181', '195.219.3.184')
[8746]41ACCEPTED_IP = None
42
[8769]43
[8698]44# Kofa's webservice
45
46class KofaFeeRequest(grok.View):
47    grok.context(IUniversity)
48    grok.name('feerequest')
49    grok.require('waeup.Public')
50
51    def update(self, PAYEE_ID=None):
[9508]52        if PAYEE_ID == None:
53            self.output = '-1'
54            return
[8746]55        real_ip = self.request.get('HTTP_X_FORWARDED_FOR', None)
[8769]56        # We can forego the logging once eTranzact payments run smoothly
57        # and the accepted IP addresses are used.
58        if real_ip:
59            self.context.logger.info('KofaFeeRequest called: %s' % real_ip)
[8746]60        if real_ip  and ACCEPTED_IP:
[8769]61            if real_ip not in  ACCEPTED_IP:
[8746]62                self.output = '-4'
63                return
[8754]64
65        # It seems eTranzact sends a POST request with an empty body but the URL
66        # contains a query string. So it's actually a GET request pretended
67        # to be a POST request. Although this does not comply with the
68        # RFC 2616 HTTP guidelines we may try to fetch the id from the QUERY_STRING
69        # value of the request.
70        #if PAYEE_ID is None:
71        #    try:
72        #        PAYEE_ID = self.request['QUERY_STRING'].split('=')[1]
73        #    except:
74        #        self.output = '-2'
75        #        return
76
[8704]77        cat = getUtility(ICatalog, name='payments_catalog')
78        results = list(cat.searchResults(p_id=(PAYEE_ID, PAYEE_ID)))
79        if len(results) != 1:
80            self.output = '-1'
[8746]81            return
82        try:
83            owner = IPaymentWebservice(results[0])
84            full_name = owner.display_fullname
85            matric_no = owner.id
86            faculty = owner.faculty
87            department = owner.department
88        except (TypeError, AttributeError):
89            self.output = '-3'
90            return
91        amount = results[0].amount_auth
92        payment_type = results[0].category
93        programme_type = results[0].p_item
94        academic_session = academic_sessions_vocab.getTerm(
95            results[0].p_session).title
96        status = results[0].p_state
97        self.output = (
98            'FULL_NAME=%s&' +
99            'FACULTY=%s&' +
100            'DEPARTMENT=%s&' +
101            'RETURN_TYPE=%s&' +
102            'PROGRAMME_TYPE=%s&' +
103            'PAYMENT_TYPE=%s&' +
104            'ACADEMIC_SESSION=%s&' +
105            'MATRIC_NO=%s&' +
106            'FEE_AMOUNT=%s&' +
107            'TRANSACTION_STATUS=%s') % (full_name, faculty,
108            department, PAYEE_ID, programme_type, payment_type,
109            academic_session, matric_no, amount, status)
[8698]110        return
111
112    def render(self):
113        return self.output
114
115
116# Requerying eTranzact payments
117
[8776]118TERMINAL_ID = '0330000046'
119QUERY_URL =   'https://www.etranzact.net/Query/queryPayoutletTransaction.jsp'
[8259]120
[8680]121# Test environment
[8776]122#QUERY_URL =   'http://demo.etranzact.com:8080/WebConnect/queryPayoutletTransaction.jsp'
123#TERMINAL_ID = '5009892289'
[8680]124
[8430]125def query_etranzact(confirmation_number, payment):
126   
[8247]127    postdict = {}
128    postdict['TERMINAL_ID'] = TERMINAL_ID
129    #postdict['RESPONSE_URL'] = 'http://dummy'
130    postdict['CONFIRMATION_NO'] = confirmation_number
131    data = urllib.urlencode(postdict)
[8682]132    payment.conf_number = confirmation_number
[8247]133    try:
134        f = urllib.urlopen(url=QUERY_URL, data=data)
135        success = f.read()
[8432]136        success = success.replace('\r\n','')
[8769]137        if 'COL6' not in success:
[8430]138            msg = _('Invalid or unsuccessful callback: ${a}',
139                mapping = {'a': success})
140            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
[8247]141            payment.p_state = 'failed'
[8430]142            return False, msg, log
[8247]143        success = success.replace('%20',' ').split('&')
144        # We expect at least two parameters
145        if len(success) < 2:
[8430]146            msg = _('Invalid callback: ${a}', mapping = {'a': success})
147            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
[8247]148            payment.p_state = 'failed'
[8430]149            return False, msg, log
[8247]150        try:
151            success_dict = dict([tuple(i.split('=')) for i in success])
152        except ValueError:
[8430]153            msg = _('Invalid callback: ${a}', mapping = {'a': success})
154            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
[8247]155            payment.p_state = 'failed'
[8430]156            return False, msg, log
[8247]157    except IOError:
[8430]158        msg = _('eTranzact IOError')
159        log = 'eTranzact IOError'
160        return False, msg, log
[8247]161    payment.r_code = u'ET'
[9327]162    payment.r_company = u'etranzact'
[8247]163    payment.r_desc = u'%s' % success_dict.get('TRANS_DESCR')
164    payment.r_amount_approved = float(success_dict.get('TRANS_AMOUNT',0.0))
165    payment.r_card_num = None
166    payment.r_pay_reference = u'%s' % success_dict.get('RECEIPT_NO')
167    if payment.r_amount_approved != payment.amount_auth:
[8430]168        msg = _('Wrong amount')
169        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
[8247]170        payment.p_state = 'failed'
[8430]171        return False, msg, log
[8717]172    #tcode = payment.p_id
173    #tcode = tcode[len(tcode)-8:len(tcode)]
[8247]174    col1 = success_dict.get('COL1')
[8717]175    #col1 = col1[len(col1)-8:len(col1)]
176    #if tcode != col1:
177    if payment.p_id != col1:
178        #msg = _('Wrong transaction code')
179        msg = _('Wrong payment id')
[8430]180        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
[8247]181        payment.p_state = 'failed'
[8430]182        return False, msg, log
183    log = 'valid callback for payment %s: %s' % (payment.p_id, success)
184    msg = _('Successful callback received')
[8247]185    payment.p_state = 'paid'
[8433]186    payment.payment_date = datetime.utcnow()
[8430]187    return True, msg, log
[8247]188
[8430]189class EtranzactEnterPinActionButtonApplicant(APABApplicant):
[8253]190    grok.context(ICustomApplicantOnlinePayment)
[8430]191    grok.require('waeup.payApplicant')
[8259]192    grok.order(3)
[7929]193    icon = 'actionicon_call.png'
194    text = _('Query eTranzact History')
[7976]195    target = 'enterpin'
[7929]196
[8430]197class EtranzactEnterPinActionButtonStudent(APABStudent):
[8253]198    grok.context(ICustomStudentOnlinePayment)
[8430]199    grok.require('waeup.payStudent')
[8259]200    grok.order(3)
[8247]201    icon = 'actionicon_call.png'
202    text = _('Query eTranzact History')
203    target = 'enterpin'
204
205class EtranzactEnterPinPageStudent(KofaPage):
[7976]206    """
207    """
[8253]208    grok.context(ICustomStudentOnlinePayment)
[7976]209    grok.name('enterpin')
210    grok.template('enterpin')
[7929]211    grok.require('waeup.payStudent')
212
[7976]213    buttonname = _('Submit to eTranzact')
214    label = _('Requery eTranzact History')
215    action = 'query_history'
[7929]216
[8247]217class EtranzactEnterPinPageApplicant(EtranzactEnterPinPageStudent):
218    """
219    """
220    grok.require('waeup.payApplicant')
[8253]221    grok.context(ICustomApplicantOnlinePayment)
[8247]222
223class EtranzactQueryHistoryPageStudent(UtilityView, grok.View):
[7929]224    """ Query history of eTranzact payments
225    """
[8253]226    grok.context(ICustomStudentOnlinePayment)
[7929]227    grok.name('query_history')
228    grok.require('waeup.payStudent')
229
230    def update(self, confirmation_number=None):
231        if self.context.p_state == 'paid':
232            self.flash(_('This ticket has already been paid.'))
233            return
[8763]234        student = self.context.student
[8430]235        success, msg, log = query_etranzact(confirmation_number,self.context)
[8764]236        student.writeLogMessage(self, log)
[8430]237        if not success:
238            self.flash(msg)
239            return
240        success, msg, log = self.context.doAfterStudentPayment()
241        if log is not None:
[8764]242            student.writeLogMessage(self, log)
[8430]243        self.flash(msg)
[8247]244        return
[7929]245
[8247]246    def render(self):
247        self.redirect(self.url(self.context, '@@index'))
248        return
[7929]249
[8247]250class EtranzactQueryHistoryPageApplicant(UtilityView, grok.View):
251    """ Query history of eTranzact payments
252    """
[8253]253    grok.context(ICustomApplicantOnlinePayment)
[8247]254    grok.name('query_history')
255    grok.require('waeup.payApplicant')
256
257    def update(self, confirmation_number=None):
[8430]258        ob_class = self.__implemented__.__name__
[8247]259        if self.context.p_state == 'paid':
260            self.flash(_('This ticket has already been paid.'))
[7929]261            return
[8247]262        applicant = self.context.__parent__
[8430]263        success, msg, log = query_etranzact(confirmation_number,self.context)
[8769]264        applicant.writeLogMessage(self, log)
[8430]265        if not success:
266            self.flash(msg)
267            return
268        success, msg, log = self.context.doAfterApplicantPayment()
269        if log is not None:
[8769]270            applicant.writeLogMessage(self, log)
[8430]271        self.flash(msg)
[7929]272        return
273
274    def render(self):
275        self.redirect(self.url(self.context, '@@index'))
[8259]276        return
[9904]277
278# Disable Interswitch viewlets. This could be avoided by defining the
279# action button viewlets of kofacustom.nigeria.interswitch.browser in the
280# context of INigeriaStudentOnlinePayment or INigeriaApplicantOnlinePayment
281# respectively. But then all interswitch.browser modules have to be extended.
282
283class InterswitchActionButtonStudent(InterswitchActionButtonStudent):
284
285    @property
286    def target_url(self):
287        return ''
288
289class InterswitchRequestWebserviceActionButtonStudent(
290    InterswitchRequestWebserviceActionButtonStudent):
291
292    @property
293    def target_url(self):
294        return ''
295
296class InterswitchActionButtonApplicant(InterswitchActionButtonApplicant):
297
298    @property
299    def target_url(self):
300        return ''
301
302class InterswitchRequestWebserviceActionButtonApplicant(
303    InterswitchRequestWebserviceActionButtonApplicant):
304
305    @property
306    def target_url(self):
307        return ''
Note: See TracBrowser for help on using the repository browser.