source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/remita/studentsbrowser.py @ 15945

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

Show correct buttons if payment state in ('paid', 'waived', 'scholarship').

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