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

Last change on this file since 15185 was 14805, checked in by Henrik Bettermann, 7 years ago

Wow, Remita has modified the software. get_JSON_POST_response can only be called once with th esame same orderId to retrieve the RRR. Also the trailing whitespace disappeared. Last but not least lineitems are no longer part of the transaction status request response.

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