payfip_ws: add new request() parameters (#48135)
The added parameters are : * subject, to pass the description of a payment, * orderid, to pass the order number for the payment, * transaction_id, to identify a payment with an external identifier, * exer, custom field for PayFiP.
This commit is contained in:
parent
5bf49c33ba
commit
4ba0534d01
|
@ -18,10 +18,11 @@ from __future__ import print_function, unicode_literals
|
|||
|
||||
import copy
|
||||
import datetime
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import functools
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from gettext import gettext as _
|
||||
|
@ -37,7 +38,7 @@ import zeep.exceptions
|
|||
from .systempayv2 import isonow
|
||||
from .common import (PaymentCommon, PaymentResponse, URL, PAID, DENIED,
|
||||
CANCELLED, ERROR, ResponseError, PaymentException,
|
||||
WAITING, EXPIRED)
|
||||
WAITING, EXPIRED, force_text)
|
||||
|
||||
WSDL_URL = 'https://www.tipi.budget.gouv.fr/tpa/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService?wsdl' # noqa: E501
|
||||
|
||||
|
@ -45,6 +46,8 @@ SERVICE_URL = 'https://www.tipi.budget.gouv.fr/tpa/services/securite' # noqa: E
|
|||
|
||||
PAYMENT_URL = 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web'
|
||||
|
||||
REFDET_RE = re.compile(r'^[A-Za-z0-9]{1,30}$')
|
||||
|
||||
|
||||
def clear_namespace(element):
|
||||
def helper(element):
|
||||
|
@ -58,6 +61,19 @@ def clear_namespace(element):
|
|||
return element
|
||||
|
||||
|
||||
def normalize_objet(objet):
|
||||
'''Make objet a string of 100 chars in alphabet [A-Za-z0-9 ]'''
|
||||
if not objet:
|
||||
return objet
|
||||
|
||||
objet = force_text(objet)
|
||||
objet = unicodedata.normalize('NFKD', objet).encode('ascii', 'ignore').decode()
|
||||
objet = re.sub(r'[\'-]', ' ', objet).strip()
|
||||
objet = re.sub(r'[^A-Za-z0-9 ]', '', objet).strip()
|
||||
objet = re.sub(r'[\s]+', ' ', objet)
|
||||
return objet[:100]
|
||||
|
||||
|
||||
class PayFiPError(PaymentException):
|
||||
def __init__(self, code, message, origin=None):
|
||||
self.code = code
|
||||
|
@ -200,18 +216,40 @@ class Payment(PaymentCommon):
|
|||
def _generate_refdet(self):
|
||||
return '%s%010d' % (isonow(), random.randint(1, 1000000000))
|
||||
|
||||
def request(self, amount, email, **kwargs):
|
||||
def request(self, amount, email, refdet=None, exer=None, orderid=None,
|
||||
subject=None, transaction_id=None, **kwargs):
|
||||
montant = self.clean_amount(amount, max_amount=100000)
|
||||
|
||||
numcli = self.numcli
|
||||
urlnotif = self.automatic_return_url
|
||||
urlredirect = self.normal_return_url
|
||||
exer = str(datetime.date.today().year)
|
||||
refdet = kwargs.get('refdet', self._generate_refdet())
|
||||
|
||||
if not exer:
|
||||
exer = str(datetime.date.today().year)
|
||||
|
||||
if refdet:
|
||||
pass
|
||||
elif transaction_id and REFDET_RE.match(transaction_id):
|
||||
refdet = transaction_id
|
||||
elif orderid and REFDET_RE.match(orderid):
|
||||
refdet = orderid
|
||||
else:
|
||||
refdet = self._generate_refdet()
|
||||
|
||||
objet_parts = []
|
||||
if orderid and refdet != orderid:
|
||||
objet_parts.extend(['O', orderid])
|
||||
if subject:
|
||||
if objet_parts:
|
||||
objet_parts.append('S')
|
||||
objet_parts.append(subject)
|
||||
if transaction_id and refdet != transaction_id:
|
||||
objet_parts.extend(['T', transaction_id])
|
||||
objet = normalize_objet(' '.join(objet_parts))
|
||||
|
||||
mel = email
|
||||
if hasattr(mel, 'decode'):
|
||||
mel = email.decode('ascii')
|
||||
|
||||
try:
|
||||
if '@' not in mel:
|
||||
raise ValueError('no @ in MEL')
|
||||
|
@ -227,6 +265,7 @@ class Payment(PaymentCommon):
|
|||
|
||||
idop = self.payfip.get_idop(numcli=numcli, saisie=saisie, exer=exer,
|
||||
refdet=refdet, montant=montant, mel=mel,
|
||||
objet=objet or None,
|
||||
url_notification=urlnotif,
|
||||
url_redirect=urlredirect)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from __future__ import print_function, unicode_literals
|
|||
import datetime
|
||||
import json
|
||||
import lxml.etree as ET
|
||||
import mock
|
||||
|
||||
import pytz
|
||||
|
||||
|
@ -30,7 +31,16 @@ import pytest
|
|||
from zeep.plugins import HistoryPlugin
|
||||
|
||||
import eopayment
|
||||
from eopayment.payfip_ws import PayFiP, PayFiPError
|
||||
from eopayment.payfip_ws import PayFiP, PayFiPError, normalize_objet
|
||||
|
||||
|
||||
NUMCLI = '090909'
|
||||
NOTIF_URL = 'https://notif.payfip.example.com/'
|
||||
REDIRECT_URL = 'https://redirect.payfip.example.com/'
|
||||
MEL = 'john.doe@example.com'
|
||||
EXER = '2019'
|
||||
REFDET = '201912261758460053903194'
|
||||
REFDET_GEN = '201912261758460053903195'
|
||||
|
||||
|
||||
def xmlindent(content):
|
||||
|
@ -38,8 +48,6 @@ def xmlindent(content):
|
|||
content = ET.fromstring(content)
|
||||
return ET.tostring(content, pretty_print=True).decode('utf-8', 'ignore')
|
||||
|
||||
NUMCLI = '090909'
|
||||
|
||||
|
||||
# freeze time to fix EXER field to 2019
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -48,15 +56,6 @@ def freezer(freezer):
|
|||
return freezer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backend(history):
|
||||
return eopayment.Payment('payfip_ws', {
|
||||
'numcli': '090909',
|
||||
'automatic_return_url': NOTIF_URL,
|
||||
'normal_return_url': REDIRECT_URL,
|
||||
})
|
||||
|
||||
|
||||
class PayFiPHTTMock(object):
|
||||
def __init__(self, history_name):
|
||||
history_path = 'tests/data/payfip-%s.json' % history_name
|
||||
|
@ -87,6 +86,24 @@ def history(history_name, request):
|
|||
yield None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def get_idop():
|
||||
with mock.patch('eopayment.payfip_ws.PayFiP.get_idop') as get_idop:
|
||||
get_idop.return_value = 'idop-1234'
|
||||
yield get_idop
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backend(request):
|
||||
with mock.patch('eopayment.payfip_ws.Payment._generate_refdet') as _generate_refdet:
|
||||
_generate_refdet.return_value = REFDET_GEN
|
||||
yield eopayment.Payment('payfip_ws', {
|
||||
'numcli': '090909',
|
||||
'automatic_return_url': NOTIF_URL,
|
||||
'normal_return_url': REDIRECT_URL,
|
||||
})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def payfip(history, history_name, request):
|
||||
history = HistoryPlugin()
|
||||
|
@ -130,9 +147,6 @@ def test_get_client_info(payfip):
|
|||
assert result.numcli == NUMCLI
|
||||
assert result.libelleN2 == 'POUETPOUET'
|
||||
|
||||
NOTIF_URL = 'https://notif.payfip.example.com/'
|
||||
REDIRECT_URL = 'https://redirect.payfip.example.com/'
|
||||
|
||||
|
||||
def test_get_idop_ok(payfip):
|
||||
result = payfip.get_idop(
|
||||
|
@ -140,7 +154,7 @@ def test_get_idop_ok(payfip):
|
|||
exer='2019',
|
||||
refdet='ABCDEFGH',
|
||||
montant='1000',
|
||||
mel='john.doe@example.com',
|
||||
mel=MEL,
|
||||
objet='coucou',
|
||||
url_notification=NOTIF_URL,
|
||||
url_redirect=REDIRECT_URL,
|
||||
|
@ -155,7 +169,7 @@ def test_get_idop_refdet_error(payfip):
|
|||
exer='2019',
|
||||
refdet='ABCD',
|
||||
montant='1000',
|
||||
mel='john.doe@example.com',
|
||||
mel=MEL,
|
||||
objet='coucou',
|
||||
url_notification='https://notif.payfip.example.com/',
|
||||
url_redirect='https://redirect.payfip.example.com/',
|
||||
|
@ -183,7 +197,7 @@ def test_get_info_paiement_ok(payfip):
|
|||
'exer': '20',
|
||||
'heurtrans': '1311',
|
||||
'idOp': 'cc0cb210-1cd4-11ea-8cca-0213ad91a103',
|
||||
'mel': 'john.doe@example.com',
|
||||
'mel': MEL,
|
||||
'montant': '1000',
|
||||
'numauto': '112233445566-tip',
|
||||
'numcli': NUMCLI,
|
||||
|
@ -201,27 +215,27 @@ def test_get_info_paiement_P1(payfip, freezer):
|
|||
|
||||
|
||||
@set_history_name('test_get_info_paiement_P1')
|
||||
def test_P1_and_payment_status(payfip, backend, freezer):
|
||||
def test_P1_and_payment_status(history, backend):
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103')
|
||||
assert response.result == eopayment.EXPIRED
|
||||
|
||||
|
||||
@set_history_name('test_get_info_paiement_P1')
|
||||
def test_P1_and_payment_status_utc_aware_now(payfip, backend, freezer):
|
||||
def test_P1_and_payment_status_utc_aware_now(history, backend):
|
||||
utc_now = datetime.datetime.now(pytz.utc)
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=utc_now)
|
||||
assert response.result == eopayment.EXPIRED
|
||||
|
||||
|
||||
@set_history_name('test_get_info_paiement_P1')
|
||||
def test_P1_and_payment_status_utc_naive_now(payfip, backend, freezer):
|
||||
def test_P1_and_payment_status_utc_naive_now(history, backend):
|
||||
now = datetime.datetime.now()
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=now)
|
||||
assert response.result == eopayment.EXPIRED
|
||||
|
||||
|
||||
@set_history_name('test_get_info_paiement_P1')
|
||||
def test_P1_and_payment_status_utc_aware_now_later(payfip, backend, freezer):
|
||||
def test_P1_and_payment_status_utc_aware_now_later(history, backend, freezer):
|
||||
utc_now = datetime.datetime.now(pytz.utc)
|
||||
freezer.move_to(datetime.timedelta(minutes=25))
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=utc_now)
|
||||
|
@ -281,7 +295,7 @@ def test_P5_and_payment_status_utc_naive_now_later(payfip, backend, freezer):
|
|||
def test_payment_ok(payfip, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email='john.doe@example.com',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
refdet='201912261758460053903194')
|
||||
|
||||
|
@ -298,17 +312,17 @@ def test_payment_ok(payfip, backend):
|
|||
|
||||
|
||||
@set_history_name('test_payment_ok')
|
||||
def test_payment_status_ok(backend, freezer, history):
|
||||
def test_payment_status_ok(history, backend, freezer):
|
||||
history.counter = 1 # only the response
|
||||
now = datetime.datetime.now()
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=now)
|
||||
assert response.result == eopayment.PAID
|
||||
|
||||
|
||||
def test_payment_denied(backend):
|
||||
def test_payment_denied(history, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email='john.doe@example.com',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
refdet='201912261758460053903194')
|
||||
|
||||
|
@ -324,17 +338,17 @@ def test_payment_denied(backend):
|
|||
|
||||
|
||||
@set_history_name('test_payment_denied')
|
||||
def test_payment_status_denied(backend, freezer, history):
|
||||
def test_payment_status_denied(history, backend, freezer):
|
||||
history.counter = 1 # only the response
|
||||
now = datetime.datetime.now()
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=now)
|
||||
assert response.result == eopayment.DENIED
|
||||
|
||||
|
||||
def test_payment_cancelled(backend):
|
||||
def test_payment_cancelled(history, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email='john.doe@example.com',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
refdet='201912261758460053903194')
|
||||
|
||||
|
@ -350,8 +364,118 @@ def test_payment_cancelled(backend):
|
|||
|
||||
|
||||
@set_history_name('test_payment_cancelled')
|
||||
def test_payment_status_cancelled(backend, freezer, history):
|
||||
def test_payment_status_cancelled(history, backend, freezer):
|
||||
history.counter = 1 # only the response
|
||||
now = datetime.datetime.now()
|
||||
response = backend.payment_status(transaction_id='cc0cb210-1cd4-11ea-8cca-0213ad91a103', transaction_date=now)
|
||||
assert response.result == eopayment.CANCELLED
|
||||
|
||||
|
||||
def test_normalize_objet():
|
||||
assert normalize_objet(None) is None
|
||||
assert (
|
||||
normalize_objet('18/09/2020 Établissement attestation hors-sol n#1234')
|
||||
== '18092020 Etablissement attestation hors sol n1234'
|
||||
)
|
||||
|
||||
|
||||
def test_refdet_exer(get_idop, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
exer=EXER,
|
||||
refdet=REFDET)
|
||||
|
||||
assert payment_id == 'idop-1234'
|
||||
kwargs = get_idop.call_args[1]
|
||||
|
||||
assert kwargs == {
|
||||
'exer': EXER,
|
||||
'refdet': REFDET,
|
||||
'montant': '1000',
|
||||
'objet': None,
|
||||
'mel': MEL,
|
||||
'numcli': NUMCLI,
|
||||
'saisie': 'T',
|
||||
'url_notification': NOTIF_URL,
|
||||
'url_redirect': REDIRECT_URL,
|
||||
}
|
||||
|
||||
|
||||
def test_transaction_id_orderid_subject(get_idop, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
exer=EXER,
|
||||
transaction_id='TR12345',
|
||||
orderid='F20190003',
|
||||
subject='Précompte famille #1234')
|
||||
|
||||
assert payment_id == 'idop-1234'
|
||||
kwargs = get_idop.call_args[1]
|
||||
|
||||
assert kwargs == {
|
||||
'exer': EXER,
|
||||
'refdet': 'TR12345',
|
||||
'montant': '1000',
|
||||
'objet': 'O F20190003 S Precompte famille 1234',
|
||||
'mel': MEL,
|
||||
'numcli': NUMCLI,
|
||||
'saisie': 'T',
|
||||
'url_notification': NOTIF_URL,
|
||||
'url_redirect': REDIRECT_URL,
|
||||
}
|
||||
|
||||
|
||||
def test_invalid_transaction_id_valid_orderid(get_idop, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
exer=EXER,
|
||||
transaction_id='TR-12345',
|
||||
orderid='F20190003',
|
||||
subject='Précompte famille #1234')
|
||||
|
||||
assert payment_id == 'idop-1234'
|
||||
kwargs = get_idop.call_args[1]
|
||||
|
||||
assert kwargs == {
|
||||
'exer': EXER,
|
||||
'refdet': 'F20190003',
|
||||
'montant': '1000',
|
||||
'objet': 'Precompte famille 1234 T TR 12345',
|
||||
'mel': MEL,
|
||||
'numcli': NUMCLI,
|
||||
'saisie': 'T',
|
||||
'url_notification': NOTIF_URL,
|
||||
'url_redirect': REDIRECT_URL,
|
||||
}
|
||||
|
||||
|
||||
def test_invalid_transaction_id_invalid_orderid(get_idop, backend):
|
||||
payment_id, kind, url = backend.request(
|
||||
amount='10.00',
|
||||
email=MEL,
|
||||
# make test deterministic
|
||||
exer=EXER,
|
||||
transaction_id='TR-12345',
|
||||
orderid='F/20190003',
|
||||
subject='Précompte famille #1234')
|
||||
|
||||
assert payment_id == 'idop-1234'
|
||||
kwargs = get_idop.call_args[1]
|
||||
|
||||
assert kwargs == {
|
||||
'exer': EXER,
|
||||
'refdet': REFDET_GEN,
|
||||
'montant': '1000',
|
||||
'objet': 'O F20190003 S Precompte famille 1234 T TR 12345',
|
||||
'mel': MEL,
|
||||
'numcli': NUMCLI,
|
||||
'saisie': 'T',
|
||||
'url_notification': NOTIF_URL,
|
||||
'url_redirect': REDIRECT_URL,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue