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

Last change on this file since 15755 was 15755, checked in by Henrik Bettermann, 5 years ago

Prepare all payment gateway modules for net amount fee configuration. In the future, provider and gateway surcharges will be determined and added just before the data are being send to the gateways for the first time.

File size: 10.8 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(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.__parent__, 'edit'))
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))
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        if self.context.r_company and self.context.r_company != 'remita':
238            return _("Payment ticket has been used for another payment gateway.")
239        now = datetime.utcnow()
240        if self.context.creation_date.tzinfo is not None:
241            # That's bad. Please store timezone-naive datetimes only!
242            now = self.context.creation_date.tzinfo.localize(now)
243        time_delta = now - self.context.creation_date
244        if time_delta.days > 7:
245            return _("This payment ticket is too old. Please create a new ticket.")
246        self.responseurl = self.url(self.context, 'request_payment_status')
247        resp = get_JSON_POST_response(
248            merchantId=self.merchantId,
249            serviceTypeId=self.serviceTypeId,
250            api_key=self.api_key,
251            orderId=self.orderId,
252            amount=self.amount,
253            responseurl=self.responseurl,
254            host=self.host,
255            url=self.init_url,
256            https=self.https,
257            fullname=self.context.__parent__.display_fullname,
258            email=self.context.__parent__.email,
259            lineitems=self.lineitems)
260        if resp.get('error'):
261            return resp.get('error')
262        if resp.get('statuscode') not in ('021', '025', '055'):
263            return 'RRR generation message from Remita: ' + resp.get('status')
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        return
272
273    def update(self):
274        if not module_activated(self.context.__parent__.__parent__.year):
275            return
276        self.orderId = self.context.p_id
277        error = self.init_update()
278        if error:
279            self.flash(error, type='danger')
280            self.redirect(self.url(self.context, '@@index'))
281            return
282        # Already now it becomes a Remita payment. We set the net amount
283        # and add the gateway amount.
284        if not self.context.r_company:
285            self.context.net_amt = self.context.amount_auth
286            self.context.amount_auth += self.gateway_amt
287            self.context.gateway_amt = self.gateway_amt
288            self.context.r_company = u'remita'
289        self.amount_auth = int(100 * self.context.amount_auth)
290        return
Note: See TracBrowser for help on using the repository browser.