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

Last change on this file since 17930 was 17248, checked in by Henrik Bettermann, 23 months 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.