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

Last change on this file since 17508 was 17248, checked in by Henrik Bettermann, 2 years ago

Enable Paypal only for USD payments.
Enable all other gateway services only for NGN payments.

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