tipi: implement automatic generation of REFDET (fixes #30272)

This commit is contained in:
Benjamin Dauvergne 2019-01-31 16:57:02 +01:00
parent 51a51d1201
commit 7ac96b4f1e
2 changed files with 91 additions and 21 deletions

View File

@ -1,4 +1,7 @@
import re
import random
from decimal import Decimal, ROUND_DOWN
from .common import (PaymentCommon, PaymentResponse, URL, PAID, DENIED,
CANCELLED, ERROR, ResponseError)
from six.moves.urllib.parse import urlencode, parse_qs
@ -64,6 +67,11 @@ class Payment(PaymentCommon):
],
}
REFDET_RE = re.compile('^[a-zA-Z0-9]{6,30}$')
def _generate_refdet(self):
return '%s%10d' % (isonow(), random.randint(1, 1000000000))
def request(self, amount, next_url=None, exer=None, orderid=None,
refdet=None, objet=None, email=None, saisie=None, **kwargs):
try:
@ -95,22 +103,30 @@ class Payment(PaymentCommon):
raise ValueError()
except ValueError:
raise ValueError('EXER format invalide')
try:
refdet = orderid or refdet
refdet = str(refdet)
if 6 > len(refdet) > 30:
raise ValueError('len(REFDET) < 6 or > 30')
except Exception as e:
raise ValueError('REFDET format invalide, %r' % refdet, e)
assert not (orderid and refdet), 'orderid and refdet cannot be used together'
# check or generate refdet
if refdet:
try:
if not self.REFDET_RE.match(refdet):
raise ValueError
except (TypeError, ValueError):
raise ValueError('refdet must be 6 to 30 alphanumeric characters string')
if orderid:
if self.REFDET_RE.match(orderid):
refdet = orderid
else:
objet = orderid + (' ' + objet) if objet else ''
if not refdet:
refdet = self._generate_refdet()
transaction_id = refdet
else:
transaction_id = '%s_%s' % (refdet, random.randint(1, 1000000000))
# check objet or fix objet
if objet is not None:
try:
objet = str(objet)
objet = objet.encode('ascii')
except Exception as e:
raise ValueError('OBJET must be a string', e)
if not objet.replace(' ', '').isalnum():
raise ValueError('OBJECT must only contains alphanumeric characters, %r' % objet)
if len(objet) > 99:
raise ValueError('OBJET length must be less than 100')
raise ValueError('OBJET must be an alphanumeric string', e)
try:
mel = str(email)
if '@' not in mel:
@ -120,27 +136,22 @@ class Payment(PaymentCommon):
except Exception as e:
raise ValueError('MEL is not a valid email, %r' % mel, e)
# check saisie
saisie = saisie or self.saisie
if saisie not in ('M', 'T', 'X', 'A'):
raise ValueError('SAISIE invalid format, %r, must be M, T, X or A' % saisie)
iso_now = isonow()
transaction_id = '%s_%s' % (iso_now, refdet)
if objet:
objet = objet[:100 - len(iso_now) - 2] + ' ' + iso_now
else:
objet = iso_now
params = {
'numcli': self.numcli,
'refdet': refdet,
'montant': montant,
'mel': mel,
'saisie': saisie,
'objet': objet,
}
if exer:
params['exer'] = exer
if objet:
params['objet'] = objet
if automatic_return_url:
params['urlcl'] = automatic_return_url
url = '%s?%s' % (self.service_url, urlencode(params))

View File

@ -1,7 +1,9 @@
import datetime
from decimal import Decimal
from six.moves.urllib.parse import urlparse, parse_qs
import eopayment
import eopayment.tipi
import pytest
@ -32,3 +34,60 @@ def test_tipi():
with pytest.raises(eopayment.ResponseError, match='missing refdet or resultrans'):
p.response('foo=bar')
def test_tipi_no_orderid_no_refdet():
p = eopayment.Payment('tipi', {'numcli': '12345'})
payment_id, kind, url = p.request(
amount=Decimal('123.12'),
exer=9999,
email='info@entrouvert.com',
saisie='T')
parsed_qs = parse_qs(urlparse(url).query)
assert 'objet' not in parsed_qs
assert parsed_qs['montant'] == ['12312']
assert parsed_qs['saisie'] == ['T']
assert parsed_qs['mel'] == ['info@entrouvert.com']
assert parsed_qs['numcli'] == ['12345']
assert parsed_qs['exer'] == ['9999']
assert parsed_qs['refdet'][0].startswith(datetime.datetime.now().strftime('%Y%m%d'))
def test_tipi_orderid_refdef_compatible():
p = eopayment.Payment('tipi', {'numcli': '12345', 'saisie': 'A'})
payment_id, kind, url = p.request(
amount=Decimal('123.12'),
email='info@entrouvert.com',
orderid='F121212')
expected_url = urlparse(eopayment.tipi.TIPI_URL)
parsed_url = urlparse(url)
assert parsed_url[:3] == expected_url[:3]
parsed_qs = parse_qs(parsed_url.query)
assert 'objet' not in parsed_qs
assert 'exer' not in parsed_qs
assert parsed_qs['montant'] == ['12312']
assert parsed_qs['saisie'] == ['A']
assert parsed_qs['mel'] == ['info@entrouvert.com']
assert parsed_qs['numcli'] == ['12345']
assert parsed_qs['refdet'] == ['F121212']
def test_tipi_orderid_not_refdef_compatible():
p = eopayment.Payment('tipi', {'numcli': '12345', 'saisie': 'A'})
payment_id, kind, url = p.request(
amount=Decimal('123.12'),
email='info@entrouvert.com',
objet='coucou',
orderid='F12-12-12')
expected_url = urlparse(eopayment.tipi.TIPI_URL)
parsed_url = urlparse(url)
assert parsed_url[:3] == expected_url[:3]
parsed_qs = parse_qs(parsed_url.query)
assert 'exer' not in parsed_qs
assert parsed_qs['montant'] == ['12312']
assert parsed_qs['saisie'] == ['A']
assert parsed_qs['mel'] == ['info@entrouvert.com']
assert parsed_qs['numcli'] == ['12345']
assert parsed_qs['refdet'][0].startswith(datetime.datetime.now().strftime('%Y%m%d'))
assert 'coucou' in parsed_qs['objet'][0]
assert 'F12-12-12' in parsed_qs['objet'][0]