source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/remita/applicantsbrowser.py @ 17146

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

Don't allow resending ticket to Remita if RRR has already been generated.

File size: 11.4 KB
Line 
1## $Id: browser.py 14759 2017-08-03 09:09:54Z henrik $
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
27from waeup.kofa.applicants.browser import OnlinePaymentDisplayFormPage as OPDPApplicant
28from kofacustom.nigeria.remita.helpers import (
29    get_JSON_POST_response, query_remita, write_payments_log)
30from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
31from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
32from kofacustom.nigeria.interfaces import MessageFactory as _
33from kofacustom.nigeria.remita.studentsbrowser import module_activated
34
35from kofacustom.nigeria.remita.tests import (
36    MERCHANTID, HOST, HTTPS, API_KEY, SERVICETYPEID, GATEWAY_AMT)
37
38grok.templatedir('browser_templates')
39
40# Buttons
41
42class RemitaActionButtonApplicant(ManageActionButton):
43    grok.order(1)
44    grok.context(INigeriaOnlinePayment)
45    grok.view(OPDPApplicant)
46    grok.require('waeup.payApplicant')
47    icon = 'actionicon_pay.png'
48    text = _('Pay via Remita')
49    target = 'goto_remita'
50
51    @property
52    def target_url(self):
53        if not module_activated(
54            self.context.__parent__.__parent__.year, self.context):
55            return ''
56        if self.context.p_state != 'unpaid':
57            return ''
58        return self.view.url(self.view.context, self.target)
59
60class RemitaRequestPaymentStatusActionButtonApplicant(ManageActionButton):
61    grok.order(2)
62    grok.context(INigeriaOnlinePayment)
63    grok.view(OPDPApplicant)
64    grok.require('waeup.payApplicant')
65    icon = 'actionicon_call.png'
66    text = _('Requery Remita Payment Status')
67    target = 'request_payment_status'
68
69    @property
70    def target_url(self):
71        if not module_activated(
72            self.context.__parent__.__parent__.year, self.context):
73            return ''
74        if self.context.p_state in ('paid', 'waived', 'scholarship'):
75            return ''
76        return self.view.url(self.view.context, self.target)
77
78class RemitaVerifyPaymentStatusActionButtonApplicant(ManageActionButton):
79    grok.order(3)
80    grok.context(INigeriaOnlinePayment)
81    grok.view(OPDPApplicant)
82    grok.require('waeup.manageApplication')
83    icon = 'actionicon_call.png'
84    text = _('Verify Remita Payment Status')
85    target = 'verify_payment_status'
86
87    @property
88    def target_url(self):
89        if not module_activated(
90            self.context.__parent__.__parent__.year, self.context):
91            return ''
92        if self.context.p_state != 'paid' \
93            or self.context.r_company != u'remita':
94            return ''
95        return self.view.url(self.view.context, self.target)
96
97# Webservice request views
98
99class RemitaRequestPaymentStatusPageApplicant(UtilityView, grok.View):
100    """ Request webservice view for the Remita gateway.
101    """
102    grok.context(INigeriaApplicantOnlinePayment)
103    grok.name('request_payment_status')
104    grok.require('waeup.payApplicant')
105
106    merchantId = MERCHANTID
107    host = HOST
108    https = HTTPS
109    api_key = API_KEY
110
111    def update(self):
112        if not module_activated(
113            self.context.__parent__.__parent__.year, self.context):
114            self.flash(_('Forbidden'), type='danger')
115            self.redirect(self.url(self.context, '@@index'))
116            return
117        if self.context.p_state in ('paid', 'waived', 'scholarship'):
118            self.flash(_('This ticket has already been paid.'), type='danger')
119            return
120        applicant = self.context.__parent__
121        RRR = self.context.r_pay_reference
122        if not RRR:
123            self.flash(_('Remita Retrieval Reference not found.'), type='danger')
124            return
125        # Remita sends a POST request which may contain more information
126        # if a payment was not successful.
127        resp = self.request.form
128        if resp and resp.get('statuscode') not in (None, '025', '00', '01'):
129            self.flash('Transaction status message from Remita: %s'
130                % resp.get('status'), type='warning')
131        success, msg, log = query_remita(
132            self.context,
133            self.merchantId,
134            self.api_key,
135            RRR,
136            self.host,
137            self.https,
138            False)
139        applicant.writeLogMessage(self, log)
140        if not success:
141            self.flash(msg, type='danger')
142            return
143        write_payments_log(applicant.applicant_id, self.context)
144        flashtype, msg, log = self.context.doAfterApplicantPayment()
145        if log is not None:
146            applicant.writeLogMessage(self, log)
147        self.flash(msg, type=flashtype)
148        return
149
150    def render(self):
151        self.redirect(self.url(self.context.__parent__, 'edit'))
152        return
153
154class RemitaVerifyPaymentStatusPageApplicant(UtilityView, grok.View):
155    """ Request webservice view for the Remita gateway.
156    """
157    grok.context(INigeriaApplicantOnlinePayment)
158    grok.name('verify_payment_status')
159    grok.require('waeup.manageApplication')
160
161    merchantId = MERCHANTID
162    host = HOST
163    https = HTTPS
164    api_key = API_KEY
165
166    def update(self):
167        if not module_activated(
168            self.context.__parent__.__parent__.year, self.context):
169            self.flash(_('Forbidden'), type='danger')
170            self.redirect(self.url(self.context, '@@index'))
171            return
172        if self.context.p_state  != 'paid' \
173            or self.context.r_company != u'remita':
174            self.flash(_('This ticket has not been paid.'), type='danger')
175            return
176        applicant = self.context.__parent__
177        RRR = self.context.r_pay_reference
178        if not RRR:
179            self.flash(_('Remita Retrieval Reference not found.'), type='danger')
180            return
181        # Remita sends a POST request which may contain more information
182        # if a payment was not successful.
183        resp = self.request.form
184        if resp and resp.get('statuscode') not in (None, '025', '00', '01'):
185            self.flash('Transaction status message from Remita: %s'
186                % resp.get('status'), type='warning')
187        success, msg, log = query_remita(
188            self.context,
189            self.merchantId,
190            self.api_key,
191            RRR,
192            self.host,
193            self.https,
194            True)
195        applicant.writeLogMessage(self, log)
196        if not success:
197            self.flash(msg, type='danger')
198            return
199        self.flash(msg)
200        return
201
202    def render(self):
203        self.redirect(self.url(self.context))
204        return
205
206
207# Forwarding pages
208
209class RemitaPageApplicant(KofaPage):
210    """ View which sends a POST request to the Remita payment gateway.
211    """
212    grok.context(INigeriaApplicantOnlinePayment)
213    grok.name('goto_remita')
214    grok.template('goto_remita')
215    grok.require('waeup.payApplicant')
216    label = _('Pay via Remita')
217    submit_button = _('Pay now')
218
219    merchantId = MERCHANTID
220    host = HOST
221    https = HTTPS
222    api_key = API_KEY
223    serviceTypeId = SERVICETYPEID
224
225    #orderId = '3456346346'
226    init_url = '/remita/ecomm/split/init.reg'
227    amount='1000'
228    lineitems = (
229                  {"lineItemsId":"itemid1","beneficiaryName":"Klaus Mueller",
230                  "beneficiaryAccount":"6020067886","bankCode":"011",
231                  "beneficiaryAmount":"500","deductFeeFrom":"1"},
232                  {"lineItemsId":"itemid2","beneficiaryName":"Werner Rumm",
233                  "beneficiaryAccount":"0360883515","bankCode":"050",
234                  "beneficiaryAmount":"500","deductFeeFrom":"0"}
235                )
236
237    @property
238    def action(self):
239        if self.https:
240            return 'https://' + self.host + '/remita/ecomm/finalize.reg'
241        return 'http://' + self.host + '/remita/ecomm/finalize.reg'
242
243    def init_update(self):
244        if self.context.r_pay_reference or self.context.p_state == 'paid':
245            return _("Payment ticket can't be re-sent to Remita.")
246        if self.context.r_company and self.context.r_company != 'remita':
247            return _("Payment ticket has been used for another payment gateway.")
248        now = datetime.utcnow()
249        if self.context.creation_date.tzinfo is not None:
250            # That's bad. Please store timezone-naive datetimes only!
251            now = self.context.creation_date.tzinfo.localize(now)
252        time_delta = now - self.context.creation_date
253        if time_delta.days > 7:
254            return _("This payment ticket is too old. Please create a new ticket.")
255        self.responseurl = self.url(self.context, 'request_payment_status')
256        resp = get_JSON_POST_response(
257            merchantId=self.merchantId,
258            serviceTypeId=self.serviceTypeId,
259            api_key=self.api_key,
260            orderId=self.orderId,
261            amount=self.amount,
262            responseurl=self.responseurl,
263            host=self.host,
264            url=self.init_url,
265            https=self.https,
266            fullname=self.context.__parent__.display_fullname,
267            email=self.context.__parent__.email,
268            lineitems=self.lineitems)
269        if resp.get('error'):
270            return resp.get('error')
271        if resp.get('statuscode') not in ('021', '025', '055'):
272            return 'RRR generation message from Remita: ' + resp.get('status')
273        self.rrr = self.context.r_pay_reference = resp['RRR'].rstrip()
274        hashargs =      self.merchantId + self.rrr + self.api_key
275        self.hashvalue = hashlib.sha512(hashargs).hexdigest()
276        self.customer = self.context.__parent__
277        self.customer.writeLogMessage(self,
278            'RRR retrieved: %s, ServiceTypeId: %s'
279            % (self.rrr, self.serviceTypeId))
280        return
281
282    def update(self):
283        if not module_activated(
284            self.context.__parent__.__parent__.year, self.context):
285            self.flash(_('Forbidden'), type='danger')
286            self.redirect(self.url(self.context, '@@index'))
287            return
288        self.orderId = self.context.p_id
289        error = self.init_update()
290        if error:
291            self.flash(error, type='danger')
292            self.redirect(self.url(self.context, '@@index'))
293            return
294        # Already now it becomes a Remita payment. We set the net amount
295        # and add the gateway amount.
296        if not self.context.r_company:
297            self.context.net_amt = self.context.amount_auth
298            self.context.amount_auth += self.gateway_amt
299            self.context.gateway_amt = self.gateway_amt
300            self.context.r_company = u'remita'
301        self.amount_auth = int(100 * self.context.amount_auth)
302        return
Note: See TracBrowser for help on using the repository browser.