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

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

Disable QUERY_STRING processing until eTranzact has replied to Uli's mail.

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