source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/etranzact/applicantsbrowser.py @ 16827

Last change on this file since 16827 was 16379, checked in by Henrik Bettermann, 4 years ago

Do not allow to requery failed transactions.

  • Property svn:keywords set to Id
File size: 8.6 KB
RevLine 
[15586]1## $Id: applicantsbrowser.py 16379 2021-01-20 10:13:07Z henrik $
[15585]2##
3## Copyright (C) 2017 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##
18import grok
19import hashlib
20from datetime import datetime, timedelta
21from zope.component import getUtility
22from zope.security import checkPermission
23from waeup.kofa.interfaces import IKofaUtils
24from waeup.kofa.utils.helpers import to_timezone
25from waeup.kofa.browser.layout import UtilityView, KofaPage
26from waeup.kofa.browser.viewlets import ManageActionButton
[15589]27from kofacustom.nigeria.etranzact.helpers import (
28    write_payments_log, process_response, query_history)
[15600]29from kofacustom.nigeria.applicants.browser import NigeriaOnlinePaymentDisplayFormPage as NOPDPApplicant
[15585]30from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
31from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
32from kofacustom.nigeria.interfaces import MessageFactory as _
[15772]33from kofacustom.nigeria.etranzact.studentsbrowser import webconnect_module_activated
[15585]34from kofacustom.nigeria.etranzact.tests import (
[15755]35    TERMINAL_ID, HOST, HTTPS, SECRET_KEY, LOGO_URL, GATEWAY_AMT)
[15585]36
37grok.templatedir('browser_templates')
38
39class EtranzactActionButtonApplicant(ManageActionButton):
40    grok.order(1)
41    grok.context(INigeriaOnlinePayment)
[15600]42    grok.view(NOPDPApplicant)
[15585]43    grok.require('waeup.payApplicant')
44    icon = 'actionicon_pay.png'
[15796]45    text = _('Pay via Etranzact WebConnect')
[15585]46    target = 'goto_etranzact'
47
48    @property
49    def target_url(self):
[15772]50        if not webconnect_module_activated(
[15770]51            self.context.__parent__.__parent__.year, self.context):
[15585]52            return ''
53        if self.context.p_state != 'unpaid':
54            return ''
55        return self.view.url(self.view.context, self.target)
56
[15596]57class EtranzactRequeryActionButtonApplicant(ManageActionButton):
58    grok.order(2)
59    grok.context(INigeriaOnlinePayment)
[15600]60    grok.view(NOPDPApplicant)
[15596]61    grok.require('waeup.payApplicant')
62    icon = 'actionicon_call.png'
[15796]63    text = _('Requery Etranzact WebConnect History')
[15596]64    target = 'requery_history'
65
66    @property
67    def target_url(self):
[15772]68        if not webconnect_module_activated(
[15770]69            self.context.__parent__.__parent__.year, self.context):
[15596]70            return ''
[16379]71        if self.context.p_state in ('paid', 'waived', 'scholarship', 'failed'):
[15596]72            return ''
73        return self.view.url(self.view.context, self.target)
74
[15585]75class EtranzactPageApplicant(KofaPage):
[15702]76    """ View which sends a POST request to the Etranzact payment gateway.
[15585]77    """
78    grok.context(INigeriaApplicantOnlinePayment)
79    grok.name('goto_etranzact')
80    grok.template('goto_etranzact')
81    grok.require('waeup.payApplicant')
[15702]82    label = _('Pay via Etranzact')
[15585]83    submit_button = _('Pay now')
84
85    host = HOST
86    https = HTTPS
87    secret_key = SECRET_KEY
88    terminal_id = TERMINAL_ID
89    logo_url = LOGO_URL
[15755]90    gateway_amt = GATEWAY_AMT
[15585]91
92    @property
93    def action(self):
94        if self.https:
95            return 'https://' + self.host + '/webconnect/v3/caller.jsp'
96        return 'http://' + self.host + '/webconnect/v3/caller.jsp'
97
98    def init_update(self):
99        if self.context.p_state == 'paid':
[15702]100            return _("Payment ticket can't be re-sent to Etranzact.")
[15585]101        now = datetime.utcnow()
102        if self.context.creation_date.tzinfo is not None:
103            # That's bad. Please store timezone-naive datetimes only!
104            now = self.context.creation_date.tzinfo.localize(now)
105        time_delta = now - self.context.creation_date
106        if time_delta.days > 7:
107            return _("This payment ticket is too old. Please create a new ticket.")
[15596]108        # In contrast to the procedure in the Remita and Interswitch modules,
109        # we do not call requery_history but receive and evaluate
[15702]110        # the response form from Etranzact directly. This is possible
111        # because Etranzact provides the FINAL_CHECKSUM hash value
[15596]112        # which authenticates the response.
[15589]113        self.responseurl = self.url(self.context, 'receive_etranzact')
[15755]114        self.transaction_id = self.context.p_id
[16271]115        self.description = "%s - (%s)" % (
116            self.context.p_category, self.context.p_item)
[15755]117        hashargs =      self.amount + self.terminal_id + self.transaction_id \
[15585]118            + self.responseurl + self.secret_key
119        self.hashvalue = hashlib.md5(hashargs).hexdigest()
120        self.customer = self.context.__parent__
121        return
122
123    def update(self):
[15974]124        if not webconnect_module_activated(
125            self.context.__parent__.__parent__.year, self.context):
126            self.flash(_('Forbidden'), type='danger')
127            self.redirect(self.url(self.context, '@@index'))
128            return
[15755]129        # Already now it becomes an Etranzact payment. We set the net amount
130        # and add the gateway amount.
131        if not self.context.r_company:
132            self.context.net_amt = self.context.amount_auth
133            self.context.amount_auth += self.gateway_amt
134            self.context.gateway_amt = self.gateway_amt
135            self.context.r_company = u'etranzact'
[15585]136        self.amount = "%.1f" % self.context.amount_auth
137        error = self.init_update()
138        if error:
139            self.flash(error, type='danger')
140            self.redirect(self.url(self.context, '@@index'))
141            return
142        return
[15589]143
[15600]144class EtranzactReceiveResponseApplicant(NOPDPApplicant):
[15589]145    """ View that receives the response from eTrantact payment gateway.
146    """
147    grok.name('receive_etranzact')
148
149    secret_key = SECRET_KEY
150    terminal_id = TERMINAL_ID
151
152    def update(self):
153        super(EtranzactReceiveResponseApplicant, self).update()
[15772]154        if not webconnect_module_activated(
[15770]155            self.context.__parent__.__parent__.year, self.context):
[15974]156            self.flash(_('Forbidden'), type='danger')
157            self.redirect(self.url(self.context, '@@index'))
[15589]158            return
159        applicant = self.context.__parent__
160        form = self.request.form
161        verify = False
162        if self.context.p_state == 'paid':
163            verify = True
164        success, msg, log = process_response(self.context, form, self, verify)
165        applicant.writeLogMessage(self, log)
166        if not success:
167            self.flash(msg, type='danger')
168            return
169        write_payments_log(applicant.applicant_id, self.context)
170        flashtype, msg, log = self.context.doAfterApplicantPayment()
171        if log is not None:
172            applicant.writeLogMessage(self, log)
173        self.flash(msg, type=flashtype)
174        return
175
[15596]176class EtranzactRequestPaymentStatusPageApplicant(UtilityView, grok.View):
[15702]177    """ Request webservice view for the Etranzact gateway.
[15589]178    """
179    grok.context(INigeriaApplicantOnlinePayment)
180    grok.name('requery_history')
181    grok.require('waeup.payApplicant')
182
183    host = HOST
184    https = HTTPS
185    secret_key = SECRET_KEY
186    terminal_id = TERMINAL_ID
187    logo_url = LOGO_URL
188
189    def update(self):
[15772]190        if not webconnect_module_activated(
[15770]191            self.context.__parent__.__parent__.year, self.context):
[15974]192            self.flash(_('Forbidden'), type='danger')
193            self.redirect(self.url(self.context, '@@index'))
[15589]194            return
[16379]195        if self.context.p_state in ('paid', 'waived', 'scholarship', 'failed'):
196            self.flash(_('This ticket has already been processed.'), type='danger')
[15596]197            return
198        applicant = self.context.__parent__
199        verify = False
200        raw, form = query_history(self.host, self.terminal_id,
201                                  self.context.p_id, self.https)
202        success, msg, log = process_response(self.context, form, self, verify)
203        applicant.writeLogMessage(self, log)
204        if not success:
205            self.flash(msg, type='danger')
206            return
207        write_payments_log(applicant.applicant_id, self.context)
208        flashtype, msg, log = self.context.doAfterApplicantPayment()
209        if log is not None:
210            applicant.writeLogMessage(self, log)
211        self.flash(msg, type=flashtype)
[15589]212        return
[15596]213
214    def render(self):
[15599]215        self.redirect(self.url(self.context))
[15596]216        return
Note: See TracBrowser for help on using the repository browser.