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

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

Merged with waeup.uniben 8740:HEAD.

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