source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/remita/browser.py @ 14756

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

Set r_company already after retrieving RRR.

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