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

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

Do it right.

  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1## $Id: studentsbrowser.py 14798 2017-08-14 06:20:37Z 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        error = self.init_update()
293        if error:
294            self.flash(error, type='danger')
295            self.redirect(self.url(self.context, '@@index'))
296            return
297        return
Note: See TracBrowser for help on using the repository browser.