1
|
import random
|
2
|
import string
|
3
|
from datetime import datetime as dt
|
4
|
import hashlib
|
5
|
import time
|
6
|
import urllib
|
7
|
|
8
|
from decimal import Decimal
|
9
|
|
10
|
from quixote import (redirect, get_publisher, get_request, get_session,
|
11
|
get_response)
|
12
|
from quixote.directory import Directory
|
13
|
from quixote.html import TemplateIO, htmltext
|
14
|
|
15
|
if not set:
|
16
|
from sets import Set as set
|
17
|
|
18
|
eopayment = None
|
19
|
try:
|
20
|
import eopayment
|
21
|
except ImportError:
|
22
|
pass
|
23
|
|
24
|
from qommon import errors, get_logger, get_cfg, emails
|
25
|
from qommon.storage import StorableObject
|
26
|
from qommon.form import htmltext, StringWidget, TextWidget, SingleSelectWidget, \
|
27
|
WidgetDict
|
28
|
from qommon.misc import simplify
|
29
|
|
30
|
from wcs.formdef import FormDef
|
31
|
from wcs.formdata import Evolution
|
32
|
from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
|
33
|
from wcs.users import User
|
34
|
|
35
|
def is_payment_supported():
|
36
|
if not eopayment:
|
37
|
return False
|
38
|
return get_cfg('aq-permissions', {}).get('payments', None) is not None
|
39
|
|
40
|
|
41
|
class Regie(StorableObject):
|
42
|
_names = 'regies'
|
43
|
|
44
|
label = None
|
45
|
description = None
|
46
|
service = None
|
47
|
service_options = None
|
48
|
|
49
|
def get_payment_object(self):
|
50
|
return eopayment.Payment(kind=self.service,
|
51
|
options=self.service_options)
|
52
|
|
53
|
|
54
|
class Invoice(StorableObject):
|
55
|
_names = 'invoices'
|
56
|
_hashed_indexes = ['user_id', 'user_hash', 'regie_id']
|
57
|
_indexes = ['external_id']
|
58
|
|
59
|
user_id = None
|
60
|
user_hash = None
|
61
|
regie_id = None
|
62
|
formdef_id = None
|
63
|
formdata_id = None
|
64
|
subject = None
|
65
|
details = None
|
66
|
amount = None
|
67
|
date = None
|
68
|
paid = False
|
69
|
paid_date = None
|
70
|
canceled = False
|
71
|
canceled_date = None
|
72
|
canceled_reason = None
|
73
|
next_status = None
|
74
|
external_id = None
|
75
|
request_kwargs = {}
|
76
|
|
77
|
def __init__(self, id=None, regie_id=None, formdef_id=None):
|
78
|
self.id = id
|
79
|
self.regie_id = regie_id
|
80
|
self.formdef_id = formdef_id
|
81
|
if get_publisher() and not self.id:
|
82
|
self.id = self.get_new_id()
|
83
|
|
84
|
def get_user(self):
|
85
|
if self.user_id:
|
86
|
return User.get(self.user_id, ignore_errors=True)
|
87
|
return None
|
88
|
|
89
|
@property
|
90
|
def username(self):
|
91
|
user = self.get_user()
|
92
|
return user.name if user else ''
|
93
|
|
94
|
def get_new_id(self, create=False):
|
95
|
# format : date-regie-formdef-alea-check
|
96
|
r = random.SystemRandom()
|
97
|
self.fresh = True
|
98
|
while True:
|
99
|
id = '-'.join([
|
100
|
dt.now().strftime('%Y%m%d'),
|
101
|
'r%s' % (self.regie_id or 'x'),
|
102
|
'f%s' % (self.formdef_id or 'x'),
|
103
|
''.join([r.choice(string.digits) for x in range(5)])
|
104
|
])
|
105
|
crc = '%0.2d' % (ord(hashlib.md5(id).digest()[0]) % 100)
|
106
|
id = id + '-' + crc
|
107
|
if not self.has_key(id):
|
108
|
return id
|
109
|
|
110
|
def store(self, *args, **kwargs):
|
111
|
if getattr(self, 'fresh', None) is True:
|
112
|
del self.fresh
|
113
|
notify_new_invoice(self)
|
114
|
return super(Invoice, self).store(*args, **kwargs)
|
115
|
|
116
|
def check_crc(cls, id):
|
117
|
try:
|
118
|
return int(id[-2:]) == (ord(hashlib.md5(id[:-3]).digest()[0]) % 100)
|
119
|
except:
|
120
|
return False
|
121
|
check_crc = classmethod(check_crc)
|
122
|
|
123
|
def pay(self):
|
124
|
self.paid = True
|
125
|
self.paid_date = dt.now()
|
126
|
self.store()
|
127
|
get_logger().info(_('invoice %s paid'), self.id)
|
128
|
notify_paid_invoice(self)
|
129
|
|
130
|
def unpay(self):
|
131
|
self.paid = False
|
132
|
self.paid_date = None
|
133
|
self.store()
|
134
|
get_logger().info(_('invoice %s unpaid'), self.id)
|
135
|
|
136
|
def cancel(self, reason=None):
|
137
|
self.canceled = True
|
138
|
self.canceled_date = dt.now()
|
139
|
if reason:
|
140
|
self.canceled_reason = reason
|
141
|
self.store()
|
142
|
notify_canceled_invoice(self)
|
143
|
get_logger().info(_('invoice %s canceled'), self.id)
|
144
|
|
145
|
def payment_url(self):
|
146
|
base_url = get_publisher().get_frontoffice_url()
|
147
|
return '%s/invoices/%s' % (base_url, self.id)
|
148
|
|
149
|
|
150
|
INVOICE_EVO_VIEW = {
|
151
|
'create': N_('Create Invoice <a href="%(url)s">%(id)s</a>: %(subject)s - %(amount)s €'),
|
152
|
'pay': N_('Invoice <a href="%(url)s">%(id)s</a> is paid with transaction number %(transaction_order_id)s'),
|
153
|
'cancel': N_('Cancel Invoice <a href="%(url)s">%(id)s</a>'),
|
154
|
'try': N_('Try paying invoice <a href="%(url)s">%(id)s</a> with transaction number %(transaction_order_id)s'),
|
155
|
}
|
156
|
|
157
|
class InvoiceEvolutionPart:
|
158
|
action = None
|
159
|
id = None
|
160
|
subject = None
|
161
|
amount = None
|
162
|
transaction = None
|
163
|
|
164
|
def __init__(self, action, invoice, transaction=None):
|
165
|
self.action = action
|
166
|
self.id = invoice.id
|
167
|
self.subject = invoice.subject
|
168
|
self.amount = invoice.amount
|
169
|
self.transaction = transaction
|
170
|
|
171
|
def view(self):
|
172
|
vars = {
|
173
|
'url': '%s/invoices/%s' % (get_publisher().get_frontoffice_url(), self.id),
|
174
|
'id': self.id,
|
175
|
'subject': self.subject,
|
176
|
'amount': self.amount,
|
177
|
}
|
178
|
if self.transaction:
|
179
|
vars['transaction_order_id'] = self.transaction.order_id
|
180
|
return htmltext('<p class="invoice-%s">' % self.action + \
|
181
|
_(INVOICE_EVO_VIEW[self.action]) % vars + '</p>')
|
182
|
|
183
|
|
184
|
class Transaction(StorableObject):
|
185
|
_names = 'transactions'
|
186
|
_hashed_indexes = ['invoice_ids']
|
187
|
_indexes = ['order_id']
|
188
|
|
189
|
invoice_ids = None
|
190
|
|
191
|
order_id = None
|
192
|
start = None
|
193
|
end = None
|
194
|
bank_data = None
|
195
|
|
196
|
def __init__(self, *args, **kwargs):
|
197
|
self.invoice_ids = list()
|
198
|
StorableObject.__init__(self, *args, **kwargs)
|
199
|
|
200
|
def get_new_id(cls, create=False):
|
201
|
r = random.SystemRandom()
|
202
|
while True:
|
203
|
id = ''.join([r.choice(string.digits) for x in range(16)])
|
204
|
if not cls.has_key(id):
|
205
|
return id
|
206
|
get_new_id = classmethod(get_new_id)
|
207
|
|
208
|
class PaymentWorkflowStatusItem(WorkflowStatusItem):
|
209
|
description = N_('Payment Creation')
|
210
|
key = 'payment'
|
211
|
endpoint = False
|
212
|
category = ('aq-payment', N_('Payment'))
|
213
|
support_substitution_variables = True
|
214
|
|
215
|
subject = None
|
216
|
details = None
|
217
|
amount = None
|
218
|
regie_id = None
|
219
|
next_status = None
|
220
|
request_kwargs = {}
|
221
|
|
222
|
def is_available(self, workflow=None):
|
223
|
return is_payment_supported()
|
224
|
is_available = classmethod(is_available)
|
225
|
|
226
|
def render_as_line(self):
|
227
|
if self.regie_id:
|
228
|
try:
|
229
|
return _('Payable to %s' % Regie.get(self.regie_id).label)
|
230
|
except KeyError:
|
231
|
return _('Payable (not completed)')
|
232
|
else:
|
233
|
return _('Payable (not completed)')
|
234
|
|
235
|
def get_parameters(self):
|
236
|
return ('subject', 'details', 'amount', 'regie_id', 'next_status',
|
237
|
'request_kwargs')
|
238
|
|
239
|
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
240
|
if 'subject' in parameters:
|
241
|
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
|
242
|
value=self.subject, size=40)
|
243
|
if 'details' in parameters:
|
244
|
form.add(TextWidget, '%sdetails' % prefix, title=_('Details'),
|
245
|
value=self.details, cols=80, rows=10)
|
246
|
if 'amount' in parameters:
|
247
|
form.add(StringWidget, '%samount' % prefix, title=_('Amount'), value=self.amount)
|
248
|
if 'regie_id' in parameters:
|
249
|
form.add(SingleSelectWidget, '%sregie_id' % prefix,
|
250
|
title=_('Regie'), value=self.regie_id,
|
251
|
options = [(None, '---')] + [(x.id, x.label) for x in Regie.select()])
|
252
|
if 'next_status' in parameters:
|
253
|
form.add(SingleSelectWidget, '%snext_status' % prefix,
|
254
|
title=_('Status after validation'), value = self.next_status,
|
255
|
hint=_('Used only if the current status of the form does not contain any "Payment Validation" item'),
|
256
|
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
|
257
|
if 'request_kwargs' in parameters:
|
258
|
keys = ['name', 'email', 'address', 'phone', 'info1', 'info2', 'info3']
|
259
|
hint = ''
|
260
|
hint +=_('If the value starts by = it will be '
|
261
|
'interpreted as a Python expression.')
|
262
|
hint += ' '
|
263
|
hint += _('Standard keys are: %s.') % (', '.join(keys))
|
264
|
form.add(WidgetDict, 'request_kwargs',
|
265
|
title=_('Parameters for the payment system'),
|
266
|
hint=hint,
|
267
|
value = self.request_kwargs)
|
268
|
|
269
|
def perform(self, formdata):
|
270
|
invoice = Invoice(regie_id=self.regie_id, formdef_id=formdata.formdef.id)
|
271
|
invoice.user_id = formdata.user_id
|
272
|
invoice.user_hash = formdata.user_hash
|
273
|
invoice.formdata_id = formdata.id
|
274
|
invoice.next_status = self.next_status
|
275
|
if self.subject:
|
276
|
invoice.subject = template_on_formdata(formdata, self.compute(self.subject))
|
277
|
else:
|
278
|
invoice.subject = _('%(form_name)s #%(formdata_id)s') % {
|
279
|
'form_name': formdata.formdef.name,
|
280
|
'formdata_id': formdata.id }
|
281
|
invoice.details = template_on_formdata(formdata, self.compute(self.details))
|
282
|
invoice.amount = Decimal(self.compute(self.amount))
|
283
|
invoice.date = dt.now()
|
284
|
invoice.request_kwargs = {}
|
285
|
if self.request_kwargs:
|
286
|
for key, value in self.request_kwargs.iteritems():
|
287
|
invoice.request_kwargs[key] = self.compute(value)
|
288
|
invoice.store()
|
289
|
# add a message in formdata.evolution
|
290
|
evo = Evolution()
|
291
|
evo.time = time.localtime()
|
292
|
evo.status = formdata.status
|
293
|
evo.add_part(InvoiceEvolutionPart('create', invoice))
|
294
|
if not formdata.evolution:
|
295
|
formdata.evolution = []
|
296
|
formdata.evolution.append(evo)
|
297
|
formdata.store()
|
298
|
# redirect the user to "my invoices"
|
299
|
return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
|
300
|
|
301
|
register_item_class(PaymentWorkflowStatusItem)
|
302
|
|
303
|
class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
|
304
|
description = N_('Payment Cancel')
|
305
|
key = 'payment-cancel'
|
306
|
endpoint = False
|
307
|
category = ('aq-payment', N_('Payment'))
|
308
|
|
309
|
reason = None
|
310
|
regie_id = None
|
311
|
|
312
|
def is_available(self, workflow=None):
|
313
|
return is_payment_supported()
|
314
|
is_available = classmethod(is_available)
|
315
|
|
316
|
def render_as_line(self):
|
317
|
if self.regie_id:
|
318
|
if self.regie_id == '_all':
|
319
|
return _('Cancel all Payments')
|
320
|
else:
|
321
|
try:
|
322
|
return _('Cancel Payments for %s' % Regie.get(self.regie_id).label)
|
323
|
except KeyError:
|
324
|
return _('Cancel Payments (non completed)')
|
325
|
else:
|
326
|
return _('Cancel Payments (non completed)')
|
327
|
|
328
|
def get_parameters(self):
|
329
|
return ('reason', 'regie_id')
|
330
|
|
331
|
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
332
|
if 'reason' in parameters:
|
333
|
form.add(StringWidget, '%sreason' % prefix, title=_('Reason'),
|
334
|
value=self.reason, size=40)
|
335
|
if 'regie_id' in parameters:
|
336
|
form.add(SingleSelectWidget, '%sregie_id' % prefix,
|
337
|
title=_('Regie'), value=self.regie_id,
|
338
|
options = [(None, '---'), ('_all', _('All Regies'))] + \
|
339
|
[(x.id, x.label) for x in Regie.select()])
|
340
|
|
341
|
def perform(self, formdata):
|
342
|
invoices_id = []
|
343
|
# get all invoices for the formdata and the selected regie
|
344
|
for evo in [evo for evo in formdata.evolution if evo.parts]:
|
345
|
for part in [part for part in evo.parts if isinstance(part, InvoiceEvolutionPart)]:
|
346
|
if part.action == 'create':
|
347
|
invoices_id.append(part.id)
|
348
|
elif part.id in invoices_id:
|
349
|
invoices_id.remove(part.id)
|
350
|
invoices = [Invoice.get(id) for id in invoices_id]
|
351
|
# select invoices for the selected regie (if not "all regies")
|
352
|
if self.regie_id != '_all':
|
353
|
invoices = [i for i in invoices if i.regie_id == self.regie_id]
|
354
|
# security filter: check user
|
355
|
invoices = [i for i in invoices if (i.user_id == formdata.user_id) \
|
356
|
or (i.user_hash == formdata.user_hash)]
|
357
|
# security filter: check formdata & formdef
|
358
|
invoices = [i for i in invoices if (i.formdata_id == formdata.id) \
|
359
|
and (i.formdef_id == formdata.formdef.id)]
|
360
|
evo = Evolution()
|
361
|
evo.time = time.localtime()
|
362
|
for invoice in invoices:
|
363
|
if not (invoice.paid or invoice.canceled):
|
364
|
invoice.cancel(self.reason)
|
365
|
evo.add_part(InvoiceEvolutionPart('cancel', invoice))
|
366
|
if not formdata.evolution:
|
367
|
formdata.evolution = []
|
368
|
formdata.evolution.append(evo)
|
369
|
formdata.store()
|
370
|
return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
|
371
|
|
372
|
register_item_class(PaymentCancelWorkflowStatusItem)
|
373
|
|
374
|
|
375
|
def request_payment(invoice_ids, url, add_regie=True):
|
376
|
for invoice_id in invoice_ids:
|
377
|
if not Invoice.check_crc(invoice_id):
|
378
|
raise KeyError()
|
379
|
invoices = [ Invoice.get(invoice_id) for invoice_id in invoice_ids ]
|
380
|
invoices = [ i for i in invoices if not (i.paid or i.canceled) ]
|
381
|
regie_ids = set([invoice.regie_id for invoice in invoices])
|
382
|
# Do not apply if more than one regie is used or no invoice is not paid or canceled
|
383
|
if len(invoices) == 0 or len(regie_ids) != 1:
|
384
|
url = get_publisher().get_frontoffice_url()
|
385
|
if get_session().user:
|
386
|
# FIXME: add error messages
|
387
|
url += '/myspace/invoices/'
|
388
|
return redirect(url)
|
389
|
if add_regie:
|
390
|
url = '%s%s' % (url, list(regie_ids)[0])
|
391
|
|
392
|
transaction = Transaction()
|
393
|
transaction.store()
|
394
|
transaction.invoice_ids = invoice_ids
|
395
|
transaction.start = dt.now()
|
396
|
|
397
|
amount = Decimal(0)
|
398
|
for invoice in invoices:
|
399
|
amount += Decimal(invoice.amount)
|
400
|
|
401
|
regie = Regie.get(invoice.regie_id)
|
402
|
payment = regie.get_payment_object()
|
403
|
# initialize request_kwargs using informations from the first invoice
|
404
|
# and update using current user informations
|
405
|
request_kwargs = getattr(invoices[0], 'request_kwargs', {})
|
406
|
request = get_request()
|
407
|
if request.user and request.user.email:
|
408
|
request_kwargs['email'] = request.user.email
|
409
|
if request.user and request.user.display_name:
|
410
|
request_kwargs['name'] = simplify(request.user.display_name)
|
411
|
(order_id, kind, data) = payment.request(amount, next_url=url, **request_kwargs)
|
412
|
transaction.order_id = order_id
|
413
|
transaction.store()
|
414
|
|
415
|
for invoice in invoices:
|
416
|
if invoice.formdef_id and invoice.formdata_id:
|
417
|
formdef = FormDef.get(invoice.formdef_id)
|
418
|
formdata = formdef.data_class().get(invoice.formdata_id)
|
419
|
evo = Evolution()
|
420
|
evo.time = time.localtime()
|
421
|
evo.status = formdata.status
|
422
|
evo.add_part(InvoiceEvolutionPart('try', invoice,
|
423
|
transaction=transaction))
|
424
|
if not formdata.evolution:
|
425
|
formdata.evolution = []
|
426
|
formdata.evolution.append(evo)
|
427
|
formdata.store()
|
428
|
|
429
|
if kind == eopayment.URL:
|
430
|
return redirect(data)
|
431
|
elif kind == eopayment.FORM:
|
432
|
return return_eopayment_form(data)
|
433
|
else:
|
434
|
raise NotImplementedError()
|
435
|
|
436
|
def return_eopayment_form(form):
|
437
|
r = TemplateIO(html=True)
|
438
|
r += htmltext('<html><body onload="document.payform.submit()">')
|
439
|
r += htmltext('<form action="%s" method="%s" name="payform">') % (form.url, form.method)
|
440
|
for field in form.fields:
|
441
|
r += htmltext('<input type="%s" name="%s" value="%s"/>') % (
|
442
|
field['type'],
|
443
|
field['name'],
|
444
|
field['value'])
|
445
|
r += htmltext('<input type="submit" name="submit" value="%s"/>') % _('Pay')
|
446
|
r += htmltext('</body></html>')
|
447
|
return r.getvalue()
|
448
|
|
449
|
|
450
|
class PaymentValidationWorkflowStatusItem(WorkflowStatusItem):
|
451
|
description = N_('Payment Validation')
|
452
|
key = 'payment-validation'
|
453
|
endpoint = False
|
454
|
category = ('aq-payment', N_('Payment'))
|
455
|
|
456
|
next_status = None
|
457
|
|
458
|
def is_available(self, workflow=None):
|
459
|
return is_payment_supported()
|
460
|
is_available = classmethod(is_available)
|
461
|
|
462
|
def render_as_line(self):
|
463
|
return _('Wait for payment validation')
|
464
|
|
465
|
def get_parameters(self):
|
466
|
return ('next_status',)
|
467
|
|
468
|
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
469
|
if 'next_status' in parameters:
|
470
|
form.add(SingleSelectWidget, '%snext_status' % prefix,
|
471
|
title=_('Status once validated'), value = self.next_status,
|
472
|
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
|
473
|
|
474
|
register_item_class(PaymentValidationWorkflowStatusItem)
|
475
|
|
476
|
|
477
|
class PublicPaymentRegieBackDirectory(Directory):
|
478
|
def __init__(self, asynchronous):
|
479
|
self.asynchronous = asynchronous
|
480
|
|
481
|
def _q_lookup(self, component):
|
482
|
logger = get_logger()
|
483
|
request = get_request()
|
484
|
query_string = get_request().get_query()
|
485
|
if request.get_method() == 'POST' and query_string == '':
|
486
|
query_string = urllib.urlencode(request.form)
|
487
|
try:
|
488
|
regie = Regie.get(component)
|
489
|
except KeyError:
|
490
|
raise errors.TraversalError()
|
491
|
if self.asynchronous:
|
492
|
logger.debug('received asynchronous notification %r' % query_string)
|
493
|
payment = regie.get_payment_object()
|
494
|
payment_response = payment.response(query_string)
|
495
|
logger.debug('payment response %r', payment_response)
|
496
|
order_id = payment_response.order_id
|
497
|
bank_data = payment_response.bank_data
|
498
|
|
499
|
transaction = Transaction.get_on_index(order_id, 'order_id', ignore_errors=True)
|
500
|
if transaction is None:
|
501
|
raise errors.TraversalError()
|
502
|
commit = False
|
503
|
if not transaction.end:
|
504
|
commit = True
|
505
|
transaction.end = dt.now()
|
506
|
transaction.bank_data = bank_data
|
507
|
transaction.store()
|
508
|
if payment_response.signed and payment_response.is_paid() and commit:
|
509
|
logger.info('transaction %s successful, bankd_id:%s bank_data:%s' % (
|
510
|
order_id, payment_response.transaction_id, bank_data))
|
511
|
|
512
|
for invoice_id in transaction.invoice_ids:
|
513
|
# all invoices are now paid
|
514
|
invoice = Invoice.get(invoice_id)
|
515
|
invoice.pay()
|
516
|
|
517
|
# workflow for each related formdata
|
518
|
if invoice.formdef_id and invoice.formdata_id:
|
519
|
next_status = invoice.next_status
|
520
|
formdef = FormDef.get(invoice.formdef_id)
|
521
|
formdata = formdef.data_class().get(invoice.formdata_id)
|
522
|
wf_status = formdata.get_status()
|
523
|
for item in wf_status.items:
|
524
|
if isinstance(item, PaymentValidationWorkflowStatusItem):
|
525
|
next_status = item.next_status
|
526
|
break
|
527
|
if next_status is not None:
|
528
|
formdata.status = 'wf-%s' % next_status
|
529
|
evo = Evolution()
|
530
|
evo.time = time.localtime()
|
531
|
evo.status = formdata.status
|
532
|
evo.add_part(InvoiceEvolutionPart('pay', invoice,
|
533
|
transaction=transaction))
|
534
|
if not formdata.evolution:
|
535
|
formdata.evolution = []
|
536
|
formdata.evolution.append(evo)
|
537
|
formdata.store()
|
538
|
# performs the items of the new status
|
539
|
formdata.perform_workflow()
|
540
|
|
541
|
elif payment_response.is_error() and commit:
|
542
|
logger.error('transaction %s finished with failure, bank_data:%s' % (
|
543
|
order_id, bank_data))
|
544
|
elif commit:
|
545
|
logger.info('transaction %s is in intermediate state, bank_data:%s' % (
|
546
|
order_id, bank_data))
|
547
|
if payment_response.return_content != None and self.asynchronous:
|
548
|
get_response().set_content_type('text/plain')
|
549
|
return payment_response.return_content
|
550
|
else:
|
551
|
if payment_response.is_error():
|
552
|
# TODO: here return failure message
|
553
|
get_session().message = ('info', _('Payment failed'))
|
554
|
else:
|
555
|
# TODO: Here return success message
|
556
|
get_session().message = ('error', _('Payment succeeded'))
|
557
|
url = get_publisher().get_frontoffice_url()
|
558
|
if get_session().user:
|
559
|
url += '/myspace/invoices/'
|
560
|
return redirect(url)
|
561
|
|
562
|
class PublicPaymentDirectory(Directory):
|
563
|
_q_exports = ['', 'init', 'back', 'back_asynchronous']
|
564
|
|
565
|
back = PublicPaymentRegieBackDirectory(False)
|
566
|
back_asynchronous = PublicPaymentRegieBackDirectory(True)
|
567
|
|
568
|
|
569
|
def init(self):
|
570
|
invoice_ids = get_request().form.get('invoice_ids').split(' ')
|
571
|
|
572
|
for invoice_id in invoice_ids:
|
573
|
if not Invoice.check_crc(invoice_id):
|
574
|
raise KeyError()
|
575
|
|
576
|
url = get_publisher().get_frontoffice_url() + '/payment/back/'
|
577
|
|
578
|
return request_payment(invoice_ids, url)
|
579
|
|
580
|
def notify_new_invoice(invoice):
|
581
|
notify_invoice(invoice, 'payment-new-invoice-email')
|
582
|
|
583
|
def notify_paid_invoice(invoice):
|
584
|
notify_invoice(invoice, 'payment-invoice-paid-email')
|
585
|
|
586
|
def notify_canceled_invoice(invoice):
|
587
|
notify_invoice(invoice, 'payment-invoice-canceled-email')
|
588
|
|
589
|
def notify_invoice(invoice, template):
|
590
|
user = invoice.get_user()
|
591
|
assert user is not None
|
592
|
regie = Regie.get(id=invoice.regie_id)
|
593
|
emails.custom_ezt_email(template, {
|
594
|
'user': user,
|
595
|
'invoice': invoice,
|
596
|
'regie': regie,
|
597
|
'invoice_url': invoice.payment_url()
|
598
|
}, user.email, fire_and_forget = True)
|
599
|
|