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

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

Wow, Remita has modified the software. get_JSON_POST_response can only be called once with th esame same orderId to retrieve the RRR. Also the trailing whitespace disappeared. Last but not least lineitems are no longer part of the transaction status request response.

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