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

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

Switch to live environment.

  • Property svn:keywords set to Id
File size: 9.8 KB
Line 
1## $Id: browser.py 8776 2012-06-22 21:11:40Z 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
23from zope.component import getUtility
24from zope.catalog.interfaces import ICatalog
25from waeup.kofa.interfaces import IUniversity
26from waeup.kofa.payments.interfaces import IPaymentWebservice
27from waeup.kofa.browser.layout import KofaPage, UtilityView
28from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
29from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
30from waeup.aaue.interfaces import academic_sessions_vocab
31from waeup.aaue.interfaces import MessageFactory as _
32from waeup.aaue.students.interfaces import ICustomStudentOnlinePayment
33from waeup.aaue.applicants.interfaces import ICustomApplicantOnlinePayment
34
35#ACCEPTED_IP = ('195.219.3.181', '195.219.3.184')
36ACCEPTED_IP = None
37
38
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):
47        real_ip = self.request.get('HTTP_X_FORWARDED_FOR', None)
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)
52        if real_ip  and ACCEPTED_IP:
53            if real_ip not in  ACCEPTED_IP:
54                self.output = '-4'
55                return
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
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'
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)
103        return
104
105    def render(self):
106        return self.output
107
108
109# Requerying eTranzact payments
110
111TERMINAL_ID = '0330000046'
112QUERY_URL =   'https://www.etranzact.net/Query/queryPayoutletTransaction.jsp'
113
114# Test environment
115#QUERY_URL =   'http://demo.etranzact.com:8080/WebConnect/queryPayoutletTransaction.jsp'
116#TERMINAL_ID = '5009892289'
117
118def query_etranzact(confirmation_number, payment):
119   
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)
125    payment.conf_number = confirmation_number
126    try:
127        f = urllib.urlopen(url=QUERY_URL, data=data)
128        success = f.read()
129        success = success.replace('\r\n','')
130        if 'COL6' not in success:
131            msg = _('Invalid or unsuccessful callback: ${a}',
132                mapping = {'a': success})
133            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
134            payment.p_state = 'failed'
135            return False, msg, log
136        success = success.replace('%20',' ').split('&')
137        # We expect at least two parameters
138        if len(success) < 2:
139            msg = _('Invalid callback: ${a}', mapping = {'a': success})
140            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
141            payment.p_state = 'failed'
142            return False, msg, log
143        try:
144            success_dict = dict([tuple(i.split('=')) for i in success])
145        except ValueError:
146            msg = _('Invalid callback: ${a}', mapping = {'a': success})
147            log = 'invalid callback for payment %s: %s' % (payment.p_id, success)
148            payment.p_state = 'failed'
149            return False, msg, log
150    except IOError:
151        msg = _('eTranzact IOError')
152        log = 'eTranzact IOError'
153        return False, msg, log
154    payment.r_code = u'ET'
155    payment.r_desc = u'%s' % success_dict.get('TRANS_DESCR')
156    payment.r_amount_approved = float(success_dict.get('TRANS_AMOUNT',0.0))
157    payment.r_card_num = None
158    payment.r_pay_reference = u'%s' % success_dict.get('RECEIPT_NO')
159    if payment.r_amount_approved != payment.amount_auth:
160        msg = _('Wrong amount')
161        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
162        payment.p_state = 'failed'
163        return False, msg, log
164    #tcode = payment.p_id
165    #tcode = tcode[len(tcode)-8:len(tcode)]
166    col1 = success_dict.get('COL1')
167    #col1 = col1[len(col1)-8:len(col1)]
168    #if tcode != col1:
169    if payment.p_id != col1:
170        #msg = _('Wrong transaction code')
171        msg = _('Wrong payment id')
172        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
173        payment.p_state = 'failed'
174        return False, msg, log
175    log = 'valid callback for payment %s: %s' % (payment.p_id, success)
176    msg = _('Successful callback received')
177    payment.p_state = 'paid'
178    payment.payment_date = datetime.utcnow()
179    return True, msg, log
180
181class EtranzactEnterPinActionButtonApplicant(APABApplicant):
182    grok.context(ICustomApplicantOnlinePayment)
183    grok.require('waeup.payApplicant')
184    grok.order(3)
185    icon = 'actionicon_call.png'
186    text = _('Query eTranzact History')
187    target = 'enterpin'
188
189class EtranzactEnterPinActionButtonStudent(APABStudent):
190    grok.context(ICustomStudentOnlinePayment)
191    grok.require('waeup.payStudent')
192    grok.order(3)
193    icon = 'actionicon_call.png'
194    text = _('Query eTranzact History')
195    target = 'enterpin'
196
197class EtranzactEnterPinPageStudent(KofaPage):
198    """
199    """
200    grok.context(ICustomStudentOnlinePayment)
201    grok.name('enterpin')
202    grok.template('enterpin')
203    grok.require('waeup.payStudent')
204
205    buttonname = _('Submit to eTranzact')
206    label = _('Requery eTranzact History')
207    action = 'query_history'
208
209class EtranzactEnterPinPageApplicant(EtranzactEnterPinPageStudent):
210    """
211    """
212    grok.require('waeup.payApplicant')
213    grok.context(ICustomApplicantOnlinePayment)
214
215class EtranzactQueryHistoryPageStudent(UtilityView, grok.View):
216    """ Query history of eTranzact payments
217    """
218    grok.context(ICustomStudentOnlinePayment)
219    grok.name('query_history')
220    grok.require('waeup.payStudent')
221
222    def update(self, confirmation_number=None):
223        if self.context.p_state == 'paid':
224            self.flash(_('This ticket has already been paid.'))
225            return
226        student = self.context.student
227        success, msg, log = query_etranzact(confirmation_number,self.context)
228        student.writeLogMessage(self, log)
229        if not success:
230            self.flash(msg)
231            return
232        success, msg, log = self.context.doAfterStudentPayment()
233        if log is not None:
234            student.writeLogMessage(self, log)
235        self.flash(msg)
236        return
237
238    def render(self):
239        self.redirect(self.url(self.context, '@@index'))
240        return
241
242class EtranzactQueryHistoryPageApplicant(UtilityView, grok.View):
243    """ Query history of eTranzact payments
244    """
245    grok.context(ICustomApplicantOnlinePayment)
246    grok.name('query_history')
247    grok.require('waeup.payApplicant')
248
249    def update(self, confirmation_number=None):
250        ob_class = self.__implemented__.__name__
251        if self.context.p_state == 'paid':
252            self.flash(_('This ticket has already been paid.'))
253            return
254        applicant = self.context.__parent__
255        success, msg, log = query_etranzact(confirmation_number,self.context)
256        applicant.writeLogMessage(self, log)
257        if not success:
258            self.flash(msg)
259            return
260        success, msg, log = self.context.doAfterApplicantPayment()
261        if log is not None:
262            applicant.writeLogMessage(self, log)
263        self.flash(msg)
264        return
265
266    def render(self):
267        self.redirect(self.url(self.context, '@@index'))
268        return
Note: See TracBrowser for help on using the repository browser.