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

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

Add r_company field.

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