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

Last change on this file since 9465 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
Line 
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
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_company = u'etranzact'
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:
161        msg = _('Wrong amount')
162        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
163        payment.p_state = 'failed'
164        return False, msg, log
165    #tcode = payment.p_id
166    #tcode = tcode[len(tcode)-8:len(tcode)]
167    col1 = success_dict.get('COL1')
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')
173        log = 'wrong callback for payment %s: %s' % (payment.p_id, success)
174        payment.p_state = 'failed'
175        return False, msg, log
176    log = 'valid callback for payment %s: %s' % (payment.p_id, success)
177    msg = _('Successful callback received')
178    payment.p_state = 'paid'
179    payment.payment_date = datetime.utcnow()
180    return True, msg, log
181
182class EtranzactEnterPinActionButtonApplicant(APABApplicant):
183    grok.context(ICustomApplicantOnlinePayment)
184    grok.require('waeup.payApplicant')
185    grok.order(3)
186    icon = 'actionicon_call.png'
187    text = _('Query eTranzact History')
188    target = 'enterpin'
189
190class EtranzactEnterPinActionButtonStudent(APABStudent):
191    grok.context(ICustomStudentOnlinePayment)
192    grok.require('waeup.payStudent')
193    grok.order(3)
194    icon = 'actionicon_call.png'
195    text = _('Query eTranzact History')
196    target = 'enterpin'
197
198class EtranzactEnterPinPageStudent(KofaPage):
199    """
200    """
201    grok.context(ICustomStudentOnlinePayment)
202    grok.name('enterpin')
203    grok.template('enterpin')
204    grok.require('waeup.payStudent')
205
206    buttonname = _('Submit to eTranzact')
207    label = _('Requery eTranzact History')
208    action = 'query_history'
209
210class EtranzactEnterPinPageApplicant(EtranzactEnterPinPageStudent):
211    """
212    """
213    grok.require('waeup.payApplicant')
214    grok.context(ICustomApplicantOnlinePayment)
215
216class EtranzactQueryHistoryPageStudent(UtilityView, grok.View):
217    """ Query history of eTranzact payments
218    """
219    grok.context(ICustomStudentOnlinePayment)
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
227        student = self.context.student
228        success, msg, log = query_etranzact(confirmation_number,self.context)
229        student.writeLogMessage(self, log)
230        if not success:
231            self.flash(msg)
232            return
233        success, msg, log = self.context.doAfterStudentPayment()
234        if log is not None:
235            student.writeLogMessage(self, log)
236        self.flash(msg)
237        return
238
239    def render(self):
240        self.redirect(self.url(self.context, '@@index'))
241        return
242
243class EtranzactQueryHistoryPageApplicant(UtilityView, grok.View):
244    """ Query history of eTranzact payments
245    """
246    grok.context(ICustomApplicantOnlinePayment)
247    grok.name('query_history')
248    grok.require('waeup.payApplicant')
249
250    def update(self, confirmation_number=None):
251        ob_class = self.__implemented__.__name__
252        if self.context.p_state == 'paid':
253            self.flash(_('This ticket has already been paid.'))
254            return
255        applicant = self.context.__parent__
256        success, msg, log = query_etranzact(confirmation_number,self.context)
257        applicant.writeLogMessage(self, log)
258        if not success:
259            self.flash(msg)
260            return
261        success, msg, log = self.context.doAfterApplicantPayment()
262        if log is not None:
263            applicant.writeLogMessage(self, log)
264        self.flash(msg)
265        return
266
267    def render(self):
268        self.redirect(self.url(self.context, '@@index'))
269        return
Note: See TracBrowser for help on using the repository browser.