root/extra/modules/payments.py @ 8c9a8c4e
| cfb41dcc | Frédéric Péters | import random
|
|
import string
|
|||
| 391939ac | Benjamin Dauvergne | from datetime import datetime as dt
|
|
| 3e1fe4cf | Thomas Noël | import hashlib
|
|
| fc8e3469 | Benjamin Dauvergne | import time
|
|
| f1ab9b59 | Benjamin Dauvergne | import urllib
|
|
| c43d0b77 | Frédéric Péters | ||
| cfb41dcc | Frédéric Péters | from decimal import Decimal
|
|
| fc8e3469 | Benjamin Dauvergne | from quixote import (redirect, get_publisher, get_request, get_session,
|
|
get_response)
|
|||
| c43d0b77 | Frédéric Péters | from quixote.directory import Directory
|
|
| 391939ac | Benjamin Dauvergne | if not set:
|
|
from sets import Set as set
|
|||
| fc8e3469 | Benjamin Dauvergne | eopayment = None
|
|
| 0bd673f1 | Frédéric Péters | try:
|
|
import eopayment
|
|||
except ImportError:
|
|||
| fc8e3469 | Benjamin Dauvergne | pass
|
|
| 0bd673f1 | Frédéric Péters | ||
| 90c63943 | Benjamin Dauvergne | from qommon import errors, get_logger, get_cfg, emails
|
|
| c43d0b77 | Frédéric Péters | from qommon.storage import StorableObject
|
|
| 700cf8e8 | Benjamin Dauvergne | from qommon.form import htmltext, StringWidget, TextWidget, SingleSelectWidget, \
|
|
WidgetDict
|
|||
| c43d0b77 | Frédéric Péters | ||
| cfb41dcc | Frédéric Péters | from wcs.formdef import FormDef
|
|
| c43d0b77 | Frédéric Péters | from wcs.formdata import Evolution
|
|
| c8b173d6 | Thomas Noël | from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
|
|
| bc4e32b1 | Benjamin Dauvergne | from wcs.users import User
|
|
| c43d0b77 | Frédéric Péters | ||
| 0bd673f1 | Frédéric Péters | def is_payment_supported():
|
|
if not eopayment:
|
|||
return False
|
|||
return get_cfg('aq-permissions', {}).get('payments', None) is not None
|
|||
| c43d0b77 | Frédéric Péters | class Regie(StorableObject):
|
|
_names = 'regies'
|
|||
label = None
|
|||
description = None
|
|||
| 3c0aeb99 | Frédéric Péters | service = None
|
|
service_options = None
|
|||
def get_payment_object(self):
|
|||
return eopayment.Payment(kind=self.service,
|
|||
| 46bdb4f7 | Benjamin Dauvergne | options=self.service_options,
|
|
logger=get_logger())
|
|||
| c43d0b77 | Frédéric Péters | ||
class Invoice(StorableObject):
|
|||
_names = 'invoices'
|
|||
| c6ba8333 | Thomas Noël | _hashed_indexes = ['user_id', 'user_hash', 'regie_id']
|
|
| 0eb3d025 | Benjamin Dauvergne | _indexes = ['external_id']
|
|
| c43d0b77 | Frédéric Péters | ||
user_id = None
|
|||
| c6ba8333 | Thomas Noël | user_hash = None
|
|
| c43d0b77 | Frédéric Péters | regie_id = None
|
|
formdef_id = None
|
|||
formdata_id = None
|
|||
| 9d9d1ebd | Thomas Noël | subject = None
|
|
| c8b173d6 | Thomas Noël | details = None
|
|
| c43d0b77 | Frédéric Péters | amount = None
|
|
date = None
|
|||
paid = False
|
|||
paid_date = None
|
|||
| c6ba8333 | Thomas Noël | canceled = False
|
|
canceled_date = None
|
|||
canceled_reason = None
|
|||
| f0a875e0 | Thomas Noël | next_status = None
|
|
| 0eb3d025 | Benjamin Dauvergne | external_id = None
|
|
| 700cf8e8 | Benjamin Dauvergne | request_kwargs = {}
|
|
| c43d0b77 | Frédéric Péters | ||
| 3e1fe4cf | Thomas Noël | def __init__(self, id=None, regie_id=None, formdef_id=None):
|
|
self.id = id
|
|||
self.regie_id = regie_id
|
|||
self.formdef_id = formdef_id
|
|||
if get_publisher() and not self.id:
|
|||
self.id = self.get_new_id()
|
|||
| bc4e32b1 | Benjamin Dauvergne | def get_user(self):
|
|
if self.user_id:
|
|||
return User.get(self.user_id, ignore_errors=True)
|
|||
return None
|
|||
@property
|
|||
def username(self):
|
|||
user = self.get_user()
|
|||
return user.name if user else ''
|
|||
| 3e1fe4cf | Thomas Noël | def get_new_id(self, create=False):
|
|
# format : date-regie-formdef-alea-check
|
|||
r = random.SystemRandom()
|
|||
| 90c63943 | Benjamin Dauvergne | self.fresh = True
|
|
| 3e1fe4cf | Thomas Noël | while True:
|
|
id = '-'.join([
|
|||
dt.now().strftime('%Y%m%d'),
|
|||
'r%s' % (self.regie_id or 'x'),
|
|||
'f%s' % (self.formdef_id or 'x'),
|
|||
''.join([r.choice(string.digits) for x in range(5)])
|
|||
])
|
|||
crc = '%0.2d' % (ord(hashlib.md5(id).digest()[0]) % 100)
|
|||
id = id + '-' + crc
|
|||
if not self.has_key(id):
|
|||
return id
|
|||
| 90c63943 | Benjamin Dauvergne | def store(self, *args, **kwargs):
|
|
if getattr(self, 'fresh', None) is True:
|
|||
del self.fresh
|
|||
notify_new_invoice(self)
|
|||
return super(Invoice, self).store(*args, **kwargs)
|
|||
| 3e1fe4cf | Thomas Noël | def check_crc(cls, id):
|
|
try:
|
|||
return int(id[-2:]) == (ord(hashlib.md5(id[:-3]).digest()[0]) % 100)
|
|||
except:
|
|||
return False
|
|||
check_crc = classmethod(check_crc)
|
|||
| 90c63943 | Benjamin Dauvergne | def pay(self):
|
|
self.paid = True
|
|||
self.paid_date = dt.now()
|
|||
self.store()
|
|||
get_logger().info(_('invoice %s paid'), self.id)
|
|||
notify_paid_invoice(self)
|
|||
def unpay(self):
|
|||
self.paid = False
|
|||
self.paid_date = None
|
|||
self.store()
|
|||
get_logger().info(_('invoice %s unpaid'), self.id)
|
|||
| c6ba8333 | Thomas Noël | def cancel(self, reason=None):
|
|
self.canceled = True
|
|||
self.canceled_date = dt.now()
|
|||
if reason:
|
|||
self.canceled_reason = reason
|
|||
self.store()
|
|||
| 90c63943 | Benjamin Dauvergne | notify_canceled_invoice(self)
|
|
get_logger().info(_('invoice %s canceled'), self.id)
|
|||
def payment_url(self):
|
|||
base_url = get_publisher().get_frontoffice_url()
|
|||
return '%s/invoices/%s' % (base_url, self.id)
|
|||
| c6ba8333 | Thomas Noël | ||
INVOICE_EVO_VIEW = {
|
|||
'create': N_('Create Invoice <a href="%(url)s">%(id)s</a>: %(subject)s - %(amount)s €'),
|
|||
| 56756adc | Benjamin Dauvergne | 'pay': N_('Invoice <a href="%(url)s">%(id)s</a> is paid with transaction number %(transaction_order_id)s'),
|
|
| c6ba8333 | Thomas Noël | 'cancel': N_('Cancel Invoice <a href="%(url)s">%(id)s</a>'),
|
|
| 56756adc | Benjamin Dauvergne | 'try': N_('Try paying invoice <a href="%(url)s">%(id)s</a> with transaction number %(transaction_order_id)s'),
|
|
| c6ba8333 | Thomas Noël | }
|
|
class InvoiceEvolutionPart:
|
|||
action = None
|
|||
id = None
|
|||
subject = None
|
|||
amount = None
|
|||
| 56756adc | Benjamin Dauvergne | transaction = None
|
|
| c6ba8333 | Thomas Noël | ||
| 56756adc | Benjamin Dauvergne | def __init__(self, action, invoice, transaction=None):
|
|
| c6ba8333 | Thomas Noël | self.action = action
|
|
self.id = invoice.id
|
|||
self.subject = invoice.subject
|
|||
self.amount = invoice.amount
|
|||
| 56756adc | Benjamin Dauvergne | self.transaction = transaction
|
|
| c6ba8333 | Thomas Noël | ||
def view(self):
|
|||
vars = {
|
|||
'url': '%s/invoices/%s' % (get_publisher().get_frontoffice_url(), self.id),
|
|||
'id': self.id,
|
|||
'subject': self.subject,
|
|||
'amount': self.amount,
|
|||
}
|
|||
| 56756adc | Benjamin Dauvergne | if self.transaction:
|
|
vars['transaction_order_id'] = self.transaction.order_id
|
|||
| c6ba8333 | Thomas Noël | return htmltext('<p class="invoice-%s">' % self.action + \
|
|
_(INVOICE_EVO_VIEW[self.action]) % vars + '</p>')
|
|||
| c43d0b77 | Frédéric Péters | ||
| cfb41dcc | Frédéric Péters | class Transaction(StorableObject):
|
|
_names = 'transactions'
|
|||
| 391939ac | Benjamin Dauvergne | _hashed_indexes = ['invoice_ids']
|
|
| f0a875e0 | Thomas Noël | _indexes = ['order_id']
|
|
| cfb41dcc | Frédéric Péters | ||
invoice_ids = None
|
|||
| f0a875e0 | Thomas Noël | order_id = None
|
|
| cfb41dcc | Frédéric Péters | start = None
|
|
| 391939ac | Benjamin Dauvergne | end = None
|
|
| cfb41dcc | Frédéric Péters | bank_data = None
|
|
| 391939ac | Benjamin Dauvergne | def __init__(self, *args, **kwargs):
|
|
self.invoice_ids = list()
|
|||
StorableObject.__init__(self, *args, **kwargs)
|
|||
| 3e1fe4cf | Thomas Noël | def get_new_id(cls, create=False):
|
|
| cfb41dcc | Frédéric Péters | r = random.SystemRandom()
|
|
while True:
|
|||
id = ''.join([r.choice(string.digits) for x in range(16)])
|
|||
| 3e1fe4cf | Thomas Noël | if not cls.has_key(id):
|
|
| cfb41dcc | Frédéric Péters | return id
|
|
get_new_id = classmethod(get_new_id)
|
|||
| c43d0b77 | Frédéric Péters | class PaymentWorkflowStatusItem(WorkflowStatusItem):
|
|
| b29b079c | Thomas Noël | description = N_('Payment Creation')
|
|
| c43d0b77 | Frédéric Péters | key = 'payment'
|
|
endpoint = False
|
|||
| 315a95c4 | Frédéric Péters | category = ('aq-payment', N_('Payment'))
|
|
| c8b173d6 | Thomas Noël | support_substitution_variables = True
|
|
| c43d0b77 | Frédéric Péters | ||
| 9d9d1ebd | Thomas Noël | subject = None
|
|
| c8b173d6 | Thomas Noël | details = None
|
|
| c43d0b77 | Frédéric Péters | amount = None
|
|
regie_id = None
|
|||
| f0a875e0 | Thomas Noël | next_status = None
|
|
| 700cf8e8 | Benjamin Dauvergne | request_kwargs = {}
|
|
| c43d0b77 | Frédéric Péters | ||
| 54a05fc5 | Thomas NOEL | def is_available(self):
|
|
return is_payment_supported()
|
|||
is_available = classmethod(is_available)
|
|||
| c43d0b77 | Frédéric Péters | def render_as_line(self):
|
|
| 69360cf9 | Thomas Noël | if self.regie_id:
|
|
return _('Payable to %s' % Regie.get(self.regie_id).label)
|
|||
| c43d0b77 | Frédéric Péters | else:
|
|
return _('Payable (not completed)')
|
|||
def get_parameters(self):
|
|||
| 700cf8e8 | Benjamin Dauvergne | return ('subject', 'details', 'amount', 'regie_id', 'next_status',
|
|
'request_kwargs')
|
|||
| c43d0b77 | Frédéric Péters | ||
| 4d7104fb | Frédéric Péters | def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
|
| 9d9d1ebd | Thomas Noël | if 'subject' in parameters:
|
|
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
|
|||
value=self.subject, size=40)
|
|||
| c8b173d6 | Thomas Noël | if 'details' in parameters:
|
|
form.add(TextWidget, '%sdetails' % prefix, title=_('Details'),
|
|||
value=self.details, cols=80, rows=10)
|
|||
| c43d0b77 | Frédéric Péters | if 'amount' in parameters:
|
|
form.add(StringWidget, '%samount' % prefix, title=_('Amount'), value=self.amount)
|
|||
if 'regie_id' in parameters:
|
|||
form.add(SingleSelectWidget, '%sregie_id' % prefix,
|
|||
title=_('Regie'), value=self.regie_id,
|
|||
options = [(None, '---')] + [(x.id, x.label) for x in Regie.select()])
|
|||
| f0a875e0 | Thomas Noël | if 'next_status' in parameters:
|
|
form.add(SingleSelectWidget, '%snext_status' % prefix,
|
|||
title=_('Status after validation'), value = self.next_status,
|
|||
hint=_('Used only if the current status of the form does not contain any "Payment Validation" item'),
|
|||
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
|
|||
| 700cf8e8 | Benjamin Dauvergne | if 'request_kwargs' in parameters:
|
|
keys = ['name', 'email', 'address', 'phone', 'info1', 'info2', 'info3']
|
|||
hint = ''
|
|||
hint +=_('If the value starts by = it will be '
|
|||
'interpreted as a Python expression.')
|
|||
hint += ' '
|
|||
hint += _('Standard keys are: %s.') % (', '.join(keys))
|
|||
form.add(WidgetDict, 'request_kwargs',
|
|||
title=_('Parameters for the payment system'),
|
|||
hint=hint,
|
|||
value = self.request_kwargs)
|
|||
| c43d0b77 | Frédéric Péters | ||
def perform(self, formdata):
|
|||
| 3e1fe4cf | Thomas Noël | invoice = Invoice(regie_id=self.regie_id, formdef_id=formdata.formdef.id)
|
|
| c6ba8333 | Thomas Noël | invoice.user_id = formdata.user_id
|
|
invoice.user_hash = formdata.user_hash
|
|||
| c43d0b77 | Frédéric Péters | invoice.formdata_id = formdata.id
|
|
| f0a875e0 | Thomas Noël | invoice.next_status = self.next_status
|
|
| 9d9d1ebd | Thomas Noël | if self.subject:
|
|
invoice.subject = template_on_formdata(formdata, self.compute(self.subject))
|
|||
| c8b173d6 | Thomas Noël | else:
|
|
| 9d9d1ebd | Thomas Noël | invoice.subject = _('%(form_name)s #%(formdata_id)s') % {
|
|
| c8b173d6 | Thomas Noël | 'form_name': formdata.formdef.name,
|
|
'formdata_id': formdata.id }
|
|||
invoice.details = template_on_formdata(formdata, self.compute(self.details))
|
|||
| 403b9210 | Benjamin Dauvergne | invoice.amount = Decimal(self.compute(self.amount))
|
|
| 391939ac | Benjamin Dauvergne | invoice.date = dt.now()
|
|
| 700cf8e8 | Benjamin Dauvergne | invoice.request_kwargs = {}
|
|
| 90c63943 | Benjamin Dauvergne | if self.request_kwargs:
|
|
for key, value in self.request_kwargs.iteritems():
|
|||
invoice.request_kwargs[key] = self.compute(value)
|
|||
| c43d0b77 | Frédéric Péters | invoice.store()
|
|
| c6ba8333 | Thomas Noël | # add a message in formdata.evolution
|
|
evo = Evolution()
|
|||
evo.time = time.localtime()
|
|||
evo.status = formdata.status
|
|||
evo.add_part(InvoiceEvolutionPart('create', invoice))
|
|||
if not formdata.evolution:
|
|||
formdata.evolution = []
|
|||
formdata.evolution.append(evo)
|
|||
formdata.store()
|
|||
# redirect the user to "my invoices"
|
|||
| 3e1fe4cf | Thomas Noël | return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
|
|
| c43d0b77 | Frédéric Péters | ||
register_item_class(PaymentWorkflowStatusItem)
|
|||
| c6ba8333 | Thomas Noël | class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
|
|
description = N_('Payment Cancel')
|
|||
key = 'payment-cancel'
|
|||
endpoint = False
|
|||
| 315a95c4 | Frédéric Péters | category = ('aq-payment', N_('Payment'))
|
|
| c6ba8333 | Thomas Noël | ||
reason = None
|
|||
regie_id = None
|
|||
| 54a05fc5 | Thomas NOEL | def is_available(self):
|
|
return is_payment_supported()
|
|||
is_available = classmethod(is_available)
|
|||
| c6ba8333 | Thomas Noël | def render_as_line(self):
|
|
if self.regie_id:
|
|||
if self.regie_id == '_all':
|
|||
return _('Cancel all Payments')
|
|||
else:
|
|||
return _('Cancel Payments for %s' % Regie.get(self.regie_id).label)
|
|||
else:
|
|||
return _('Cancel Payments (non completed)')
|
|||
def get_parameters(self):
|
|||
return ('reason', 'regie_id')
|
|||
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
|||
if 'reason' in parameters:
|
|||
form.add(StringWidget, '%sreason' % prefix, title=_('Reason'),
|
|||
value=self.reason, size=40)
|
|||
if 'regie_id' in parameters:
|
|||
form.add(SingleSelectWidget, '%sregie_id' % prefix,
|
|||
title=_('Regie'), value=self.regie_id,
|
|||
options = [(None, '---'), ('_all', _('All Regies'))] + \
|
|||
[(x.id, x.label) for x in Regie.select()])
|
|||
def perform(self, formdata):
|
|||
invoices_id = []
|
|||
# get all invoices for the formdata and the selected regie
|
|||
for evo in [evo for evo in formdata.evolution if evo.parts]:
|
|||
for part in [part for part in evo.parts if isinstance(part, InvoiceEvolutionPart)]:
|
|||
if part.action == 'create':
|
|||
invoices_id.append(part.id)
|
|||
elif part.id in invoices_id:
|
|||
invoices_id.remove(part.id)
|
|||
invoices = [Invoice.get(id) for id in invoices_id]
|
|||
# select invoices for the selected regie (if not "all regies")
|
|||
if self.regie_id != '_all':
|
|||
invoices = [i for i in invoices if i.regie_id == self.regie_id]
|
|||
# security filter: check user
|
|||
invoices = [i for i in invoices if (i.user_id == formdata.user_id) \
|
|||
or (i.user_hash == formdata.user_hash)]
|
|||
# security filter: check formdata & formdef
|
|||
invoices = [i for i in invoices if (i.formdata_id == formdata.id) \
|
|||
and (i.formdef_id == formdata.formdef.id)]
|
|||
evo = Evolution()
|
|||
evo.time = time.localtime()
|
|||
for invoice in invoices:
|
|||
if not (invoice.paid or invoice.canceled):
|
|||
invoice.cancel(self.reason)
|
|||
evo.add_part(InvoiceEvolutionPart('cancel', invoice))
|
|||
if not formdata.evolution:
|
|||
formdata.evolution = []
|
|||
formdata.evolution.append(evo)
|
|||
formdata.store()
|
|||
return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
|
|||
register_item_class(PaymentCancelWorkflowStatusItem)
|
|||
| 5955b92a | Benjamin Dauvergne | def request_payment(invoice_ids, url, add_regie=True):
|
|
| 3e1fe4cf | Thomas Noël | for invoice_id in invoice_ids:
|
|
if not Invoice.check_crc(invoice_id):
|
|||
raise KeyError()
|
|||
| 391939ac | Benjamin Dauvergne | invoices = [ Invoice.get(invoice_id) for invoice_id in invoice_ids ]
|
|
| c6ba8333 | Thomas Noël | invoices = [ i for i in invoices if not (i.paid or i.canceled) ]
|
|
| 391939ac | Benjamin Dauvergne | regie_ids = set([invoice.regie_id for invoice in invoices])
|
|
| c6ba8333 | Thomas Noël | # Do not apply if more than one regie is used or no invoice is not paid or canceled
|
|
| 391939ac | Benjamin Dauvergne | if len(invoices) == 0 or len(regie_ids) != 1:
|
|
| 3e1fe4cf | Thomas Noël | url = get_publisher().get_frontoffice_url()
|
|
if get_session().user:
|
|||
# FIXME: add error messages
|
|||
url += '/myspace/invoices/'
|
|||
return redirect(url)
|
|||
| 5955b92a | Benjamin Dauvergne | if add_regie:
|
|
url = '%s%s' % (url, list(regie_ids)[0])
|
|||
| 391939ac | Benjamin Dauvergne | ||
transaction = Transaction()
|
|||
transaction.store()
|
|||
transaction.invoice_ids = invoice_ids
|
|||
transaction.start = dt.now()
|
|||
amount = Decimal(0)
|
|||
for invoice in invoices:
|
|||
amount += Decimal(invoice.amount)
|
|||
regie = Regie.get(invoice.regie_id)
|
|||
payment = regie.get_payment_object()
|
|||
| 700cf8e8 | Benjamin Dauvergne | # initialize request_kwargs using informations from the first invoice
|
|
# and update using current user informations
|
|||
request_kwargs = getattr(invoices[0], 'request_kwargs', {})
|
|||
request = get_request()
|
|||
if request.user and request.user.email:
|
|||
request_kwargs['email'] = request.user.email
|
|||
if request.user and request.user.display_name:
|
|||
request_kwargs['name'] = request.user.display_name
|
|||
(order_id, kind, data) = payment.request(amount, next_url=url, **request_kwargs)
|
|||
| f0a875e0 | Thomas Noël | transaction.order_id = order_id
|
|
| 391939ac | Benjamin Dauvergne | transaction.store()
|
|
| 47698115 | Benjamin Dauvergne | for invoice in invoices:
|
|
if invoice.formdef_id and invoice.formdata_id:
|
|||
formdef = FormDef.get(invoice.formdef_id)
|
|||
formdata = formdef.data_class().get(invoice.formdata_id)
|
|||
evo = Evolution()
|
|||
evo.time = time.localtime()
|
|||
evo.status = formdata.status
|
|||
evo.add_part(InvoiceEvolutionPart('try', invoice,
|
|||
transaction=transaction))
|
|||
if not formdata.evolution:
|
|||
formdata.evolution = []
|
|||
formdata.evolution.append(evo)
|
|||
formdata.store()
|
|||
| 391939ac | Benjamin Dauvergne | if kind == eopayment.URL:
|
|
return redirect(data)
|
|||
elif kind == eopayment.FORM:
|
|||
raise NotImplementedError()
|
|||
else:
|
|||
raise NotImplementedError()
|
|||
| c43d0b77 | Frédéric Péters | ||
class PaymentValidationWorkflowStatusItem(WorkflowStatusItem):
|
|||
description = N_('Payment Validation')
|
|||
key = 'payment-validation'
|
|||
endpoint = False
|
|||
| 315a95c4 | Frédéric Péters | category = ('aq-payment', N_('Payment'))
|
|
| c43d0b77 | Frédéric Péters | ||
next_status = None
|
|||
| 54a05fc5 | Thomas NOEL | def is_available(self):
|
|
return is_payment_supported()
|
|||
is_available = classmethod(is_available)
|
|||
| c43d0b77 | Frédéric Péters | def render_as_line(self):
|
|
return _('Wait for payment validation')
|
|||
def get_parameters(self):
|
|||
return ('next_status',)
|
|||
| 4d7104fb | Frédéric Péters | def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
|
| c43d0b77 | Frédéric Péters | if 'next_status' in parameters:
|
|
form.add(SingleSelectWidget, '%snext_status' % prefix,
|
|||
title=_('Status once validated'), value = self.next_status,
|
|||
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
|
|||
register_item_class(PaymentValidationWorkflowStatusItem)
|
|||
| cfb41dcc | Frédéric Péters | ||
| 3c0aeb99 | Frédéric Péters | class PublicPaymentRegieBackDirectory(Directory):
|
|
| 46bdb4f7 | Benjamin Dauvergne | def __init__(self, asynchronous):
|
|
self.asynchronous = asynchronous
|
|||
| 3c0aeb99 | Frédéric Péters | def _q_lookup(self, component):
|
|
| 391939ac | Benjamin Dauvergne | logger = get_logger()
|
|
| f1ab9b59 | Benjamin Dauvergne | request = get_request()
|
|
query_string = get_request().get_query()
|
|||
if request.get_method() == 'POST' and query_string == '':
|
|||
query_string = urllib.urlencode(request.form)
|
|||
| 3c0aeb99 | Frédéric Péters | try:
|
|
regie = Regie.get(component)
|
|||
except KeyError:
|
|||
raise errors.TraversalError()
|
|||
| 46bdb4f7 | Benjamin Dauvergne | if self.asynchronous:
|
|
logger.debug('received asynchronous notification %r' % query_string)
|
|||
| 3c0aeb99 | Frédéric Péters | payment = regie.get_payment_object()
|
|
| 6caf12b6 | Benjamin Dauvergne | payment_response = payment.response(query_string)
|
|
| 46bdb4f7 | Benjamin Dauvergne | logger.debug('payment response %r', payment_response)
|
|
| 6caf12b6 | Benjamin Dauvergne | order_id = payment_response.order_id
|
|
bank_data = payment_response.bank_data
|
|||
| b99fb0fa | Benjamin Dauvergne | transaction = Transaction.get_on_index(order_id, 'order_id', ignore_errors=True)
|
|
if transaction is None:
|
|||
raise errors.TraversalError()
|
|||
commit = False
|
|||
if not transaction.end:
|
|||
commit = True
|
|||
| 391939ac | Benjamin Dauvergne | transaction.end = dt.now()
|
|
transaction.bank_data = bank_data
|
|||
transaction.store()
|
|||
| b99fb0fa | Benjamin Dauvergne | if payment_response.signed and payment_response.is_paid() and commit:
|
|
logger.info('transaction %s successful, bankd_id:%s bank_data:%s' % (
|
|||
order_id, payment_response.transaction_id, bank_data))
|
|||
| 391939ac | Benjamin Dauvergne | ||
for invoice_id in transaction.invoice_ids:
|
|||
| f0a875e0 | Thomas Noël | # all invoices are now paid
|
|
| 391939ac | Benjamin Dauvergne | invoice = Invoice.get(invoice_id)
|
|
| 90c63943 | Benjamin Dauvergne | invoice.pay()
|
|
| f0a875e0 | Thomas Noël | ||
# workflow for each related formdata
|
|||
| 3e1fe4cf | Thomas Noël | if invoice.formdef_id and invoice.formdata_id:
|
|
| f0a875e0 | Thomas Noël | next_status = invoice.next_status
|
|
| 3e1fe4cf | Thomas Noël | formdef = FormDef.get(invoice.formdef_id)
|
|
formdata = formdef.data_class().get(invoice.formdata_id)
|
|||
| 39e48d52 | Thomas NOEL | wf_status = formdata.get_status()
|
|
| 3e1fe4cf | Thomas Noël | for item in wf_status.items:
|
|
if isinstance(item, PaymentValidationWorkflowStatusItem):
|
|||
| f0a875e0 | Thomas Noël | next_status = item.next_status
|
|
break
|
|||
if next_status is not None:
|
|||
formdata.status = 'wf-%s' % next_status
|
|||
evo = Evolution()
|
|||
evo.time = time.localtime()
|
|||
evo.status = formdata.status
|
|||
| d0cbe280 | Benjamin Dauvergne | evo.add_part(InvoiceEvolutionPart('pay', invoice,
|
|
transaction=transaction))
|
|||
| f0a875e0 | Thomas Noël | if not formdata.evolution:
|
|
formdata.evolution = []
|
|||
formdata.evolution.append(evo)
|
|||
formdata.store()
|
|||
# performs the items of the new status
|
|||
formdata.perform_workflow()
|
|||
| b99fb0fa | Benjamin Dauvergne | elif payment_response.is_error() and commit:
|
|
| 6caf12b6 | Benjamin Dauvergne | logger.error('transaction %s finished with failure, bank_data:%s' % (
|
|
order_id, bank_data))
|
|||
| b99fb0fa | Benjamin Dauvergne | elif commit:
|
|
| 46bdb4f7 | Benjamin Dauvergne | logger.info('transaction %s is in intermediate state, bank_data:%s' % (
|
|
order_id, bank_data))
|
|||
if payment_response.return_content != None and self.asynchronous:
|
|||
| 6caf12b6 | Benjamin Dauvergne | get_response().set_content_type('text/plain')
|
|
return payment_response.return_content
|
|||
else:
|
|||
| 46bdb4f7 | Benjamin Dauvergne | if payment_response.is_error():
|
|
| 6caf12b6 | Benjamin Dauvergne | # TODO: here return failure message
|
|
get_session().message = ('info', _('Payment failed'))
|
|||
| 46bdb4f7 | Benjamin Dauvergne | else:
|
|
# TODO: Here return success message
|
|||
get_session().message = ('error', _('Payment succeeded'))
|
|||
| 6caf12b6 | Benjamin Dauvergne | url = get_publisher().get_frontoffice_url()
|
|
if get_session().user:
|
|||
url += '/myspace/invoices/'
|
|||
return redirect(url)
|
|||
| 3c0aeb99 | Frédéric Péters | ||
| cfb41dcc | Frédéric Péters | class PublicPaymentDirectory(Directory):
|
|
| 46bdb4f7 | Benjamin Dauvergne | _q_exports = ['', 'init', 'back', 'back_asynchronous']
|
|
back = PublicPaymentRegieBackDirectory(False)
|
|||
back_asynchronous = PublicPaymentRegieBackDirectory(True)
|
|||
| cfb41dcc | Frédéric Péters | ||
| 3c0aeb99 | Frédéric Péters | ||
| cfb41dcc | Frédéric Péters | def init(self):
|
|
| 3e1fe4cf | Thomas Noël | invoice_ids = get_request().form.get('invoice_ids').split(' ')
|
|
for invoice_id in invoice_ids:
|
|||
if not Invoice.check_crc(invoice_id):
|
|||
raise KeyError()
|
|||
url = get_publisher().get_frontoffice_url() + '/payment/back/'
|
|||
| cfb41dcc | Frédéric Péters | ||
| 391939ac | Benjamin Dauvergne | return request_payment(invoice_ids, url)
|
|
| 90c63943 | Benjamin Dauvergne | ||
def notify_new_invoice(invoice):
|
|||
notify_invoice(invoice, 'payment-new-invoice-email')
|
|||
def notify_paid_invoice(invoice):
|
|||
notify_invoice(invoice, 'payment-invoice-paid-email')
|
|||
def notify_canceled_invoice(invoice):
|
|||
notify_invoice(invoice, 'payment-invoice-deleted-email')
|
|||
def notify_invoice(invoice, template):
|
|||
user = invoice.get_user()
|
|||
assert user is not None
|
|||
regie = Regie.get(id=invoice.regie_id)
|
|||
emails.custom_ezt_email(template, {
|
|||
'user': user,
|
|||
'invoice': invoice,
|
|||
'regie': regie,
|
|||
'invoice_url': invoice.payment_url()
|
|||
}, user.email, fire_and_forget = True)
|