0002-start-support-for-PayFiP-Regie-web-service-38405.patch
MANIFEST.in | ||
---|---|---|
5 | 5 |
include README.txt |
6 | 6 |
include eopayment/request |
7 | 7 |
include eopayment/response |
8 |
include eopayment/resource/PaiementSecuriseService.wsdl |
|
9 |
include eopayment/resource/PaiementSecuriseService1.xsd |
|
10 |
include eopayment/resource/PaiementSecuriseService2.xsd |
|
11 |
include eopayment/resource/PaiementSecuriseService3.xsd |
README.txt | ||
---|---|---|
37 | 37 | |
38 | 38 |
For other backends, the order and transaction ids, separated by '!' are sent in |
39 | 39 |
order id field, so they can be matched in backoffice. |
40 | ||
41 |
PayFiP |
|
42 |
====== |
|
43 | ||
44 |
You can test your PayFiP regie web-service connection with an integrated CLI utility: |
|
45 | ||
46 |
$ python3 -m eopayment.payfip_ws info-client --help |
|
47 |
Usage: payfip_ws.py info-client [OPTIONS] NUMCLI |
|
48 | ||
49 |
Options: |
|
50 |
--help Show this message and exit. |
|
51 | ||
52 |
$ python3 -m eopayment.payfip_ws get-idop --help |
|
53 |
Usage: payfip_ws.py get-idop [OPTIONS] NUMCLI |
|
54 | ||
55 |
Options: |
|
56 |
--saisie [T|X|W] [required] |
|
57 |
--exer TEXT [required] |
|
58 |
--montant INTEGER [required] |
|
59 |
--refdet TEXT [required] |
|
60 |
--mel TEXT [required] |
|
61 |
--url-notification TEXT [required] |
|
62 |
--url-redirect TEXT [required] |
|
63 |
--objet TEXT |
|
64 |
--help Show this message and exit. |
|
65 | ||
66 |
$ python3 -m eopayment.payfip_ws info-paiement --help |
|
67 |
Usage: payfip_ws.py info-paiement [OPTIONS] IDOP |
|
68 | ||
69 |
Options: |
|
70 |
--help Show this message and exit. |
eopayment/payfip_ws.py | ||
---|---|---|
1 |
# eopayment - online payment library |
|
2 |
# Copyright (C) 2011-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from __future__ import print_function, unicode_literals |
|
18 | ||
19 |
import copy |
|
20 |
import datetime |
|
21 |
from decimal import Decimal, ROUND_DOWN |
|
22 |
import functools |
|
23 |
import os |
|
24 |
import random |
|
25 |
import xml.etree.ElementTree as ET |
|
26 | ||
27 |
from gettext import gettext as _ |
|
28 | ||
29 |
import six |
|
30 |
from six.moves.urllib.parse import parse_qs |
|
31 | ||
32 |
import zeep |
|
33 |
import zeep.exceptions |
|
34 | ||
35 |
from .systempayv2 import isonow |
|
36 |
from .common import (PaymentCommon, PaymentResponse, URL, PAID, DENIED, |
|
37 |
CANCELLED, ERROR, ResponseError, PaymentException) |
|
38 | ||
39 |
WSDL_URL = 'https://www.tipi.budget.gouv.fr/tpa/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService?wsdl' # noqa: E501 |
|
40 | ||
41 |
SERVICE_URL = 'https://www.tipi.budget.gouv.fr/tpa/services/securite' # noqa: E501 |
|
42 | ||
43 |
PAYMENT_URL = 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=%s' |
|
44 | ||
45 | ||
46 |
def clear_namespace(element): |
|
47 |
def helper(element): |
|
48 |
if element.tag.startswith('{'): |
|
49 |
element.tag = element.tag[element.tag.index('}') + 1:] |
|
50 |
for subelement in element: |
|
51 |
helper(subelement) |
|
52 | ||
53 |
element = copy.deepcopy(element) |
|
54 |
helper(element) |
|
55 |
return element |
|
56 | ||
57 | ||
58 |
class PayFiPError(PaymentException): |
|
59 |
def __init__(self, code, message, origin=None): |
|
60 |
self.code = code |
|
61 |
self.message = message |
|
62 |
self.origin = origin |
|
63 |
args = [code, message] |
|
64 |
if origin: |
|
65 |
args.append(origin) |
|
66 |
super(PayFiPError, self).__init__(*args) |
|
67 | ||
68 | ||
69 |
class PayFiP(object): |
|
70 |
'''Encapsulate SOAP web-services of PayFiP''' |
|
71 | ||
72 |
def __init__(self, wsdl_url=None, service_url=None, zeep_client_kwargs=None): |
|
73 |
self.client = zeep.Client(wsdl_url or WSDL_URL, **(zeep_client_kwargs or {})) |
|
74 |
# distribued WSDL is wrong :/ |
|
75 |
self.client.service._binding_options['address'] = service_url or SERVICE_URL |
|
76 | ||
77 |
def fault_to_exception(self, fault): |
|
78 |
if fault.message != 'fr.gouv.finances.cp.tpa.webservice.exceptions.FonctionnelleErreur' or fault.detail is None: |
|
79 |
return |
|
80 |
detail = clear_namespace(fault.detail) |
|
81 |
code = detail.find('FonctionnelleErreur/code') |
|
82 |
if code is None or not code.text: |
|
83 |
return PayFiPError('inconnu', ET.tostring(detail)) |
|
84 |
descriptif = detail.find('FonctionnelleErreur/descriptif') |
|
85 |
libelle = detail.find('FonctionnelleErreur/libelle') |
|
86 |
return PayFiPError( |
|
87 |
code=code.text, |
|
88 |
message=(descriptif is not None and descriptif.text) |
|
89 |
or (libelle is not None and libelle.text) |
|
90 |
or '') |
|
91 | ||
92 |
def _perform(self, request_qname, operation, **kwargs): |
|
93 |
RequestType = self.client.get_type(request_qname) # noqa: E501 |
|
94 |
try: |
|
95 |
return getattr(self.client.service, operation)(RequestType(**kwargs)) |
|
96 |
except zeep.exceptions.Fault as fault: |
|
97 |
raise self.fault_to_exception(fault) or PayFiPError('unknown', fault.message, fault) |
|
98 |
except zeep.exceptions.Error as zeep_error: |
|
99 |
raise PayFiPError('erreur-soap', str(zeep_error), zeep_error) |
|
100 | ||
101 |
def get_info_client(self, numcli): |
|
102 |
return self._perform( |
|
103 |
'{http://securite.service.tpa.cp.finances.gouv.fr/reponse}RecupererDetailClientRequest', |
|
104 |
'recupererDetailClient', |
|
105 |
numCli=numcli) |
|
106 | ||
107 |
def get_idop(self, numcli, saisie, exer, refdet, montant, mel, url_notification, url_redirect, objet=None): |
|
108 |
return self._perform( |
|
109 |
'{http://securite.service.tpa.cp.finances.gouv.fr/requete}CreerPaiementSecuriseRequest', |
|
110 |
'creerPaiementSecurise', |
|
111 |
numcli=numcli, |
|
112 |
saisie=saisie, |
|
113 |
exer=exer, |
|
114 |
montant=montant, |
|
115 |
refdet=refdet, |
|
116 |
mel=mel, |
|
117 |
urlnotif=url_notification, |
|
118 |
urlredirect=url_redirect, |
|
119 |
objet=objet) |
|
120 | ||
121 |
def get_info_paiement(self, idop): |
|
122 |
return self._perform( |
|
123 |
'{http://securite.service.tpa.cp.finances.gouv.fr/reponse}RecupererDetailPaiementSecuriseRequest', |
|
124 |
'recupererDetailPaiementSecurise', |
|
125 |
idOp=idop) |
|
126 | ||
127 | ||
128 |
class Payment(PaymentCommon): |
|
129 |
'''Produce requests for and verify response from the TIPI online payment |
|
130 |
processor from the French Finance Ministry. |
|
131 | ||
132 |
''' |
|
133 | ||
134 |
description = { |
|
135 |
'caption': 'TIPI, Titres Payables par Internet', |
|
136 |
'parameters': [ |
|
137 |
{ |
|
138 |
'name': 'numcli', |
|
139 |
'caption': _(u'Client number'), |
|
140 |
'help_text': _(u'6 digits number provided by DGFIP'), |
|
141 |
'validation': lambda s: str.isdigit(s) and len(s) == 6, |
|
142 |
'required': True, |
|
143 |
}, |
|
144 |
{ |
|
145 |
'name': 'service_url', |
|
146 |
'default': SERVICE_URL, |
|
147 |
'caption': _(u'PayFIP WS service URL'), |
|
148 |
'help_text': _(u'do not modify if you do not know'), |
|
149 |
'validation': lambda x: x.startswith('http'), |
|
150 |
}, |
|
151 |
{ |
|
152 |
'name': 'wsdl_url', |
|
153 |
'default': WSDL_URL, |
|
154 |
'caption': _(u'PayFIP WS WSDL URL'), |
|
155 |
'help_text': _(u'do not modify if you do not know'), |
|
156 |
'validation': lambda x: x.startswith('http'), |
|
157 |
}, |
|
158 |
{ |
|
159 |
'name': 'saisie', |
|
160 |
'caption': _('Payment type'), |
|
161 |
'default': 'T', |
|
162 |
'choices': [ |
|
163 |
('T', _('test')), |
|
164 |
('X', _('activation')), |
|
165 |
('W', _('production')), |
|
166 |
], |
|
167 |
}, |
|
168 |
{ |
|
169 |
'name': 'normal_return_url', |
|
170 |
'caption': _('User return URL'), |
|
171 |
'required': True, |
|
172 |
}, |
|
173 |
{ |
|
174 |
'name': 'automatic_return_url', |
|
175 |
'caption': _('Asynchronous return URL'), |
|
176 |
'required': True, |
|
177 |
}, |
|
178 |
], |
|
179 |
} |
|
180 | ||
181 |
def __init__(self, *args, **kwargs): |
|
182 |
super(Payment, self).__init__(*args, **kwargs) |
|
183 |
wsdl_url = self.wsdl_url |
|
184 |
# use cached WSDL |
|
185 |
if wsdl_url == WSDL_URL: |
|
186 |
base_path = os.path.join(os.path.dirname(__file__), 'resource', 'PaiementSecuriseService.wsdl') |
|
187 |
wsdl_url = 'file://%s' % base_path |
|
188 |
self.payfip = PayFiP(wsdl_url=wsdl_url, service_url=self.service_url) |
|
189 | ||
190 |
def _generate_refdet(self): |
|
191 |
return '%s%010d' % (isonow(), random.randint(1, 1000000000)) |
|
192 | ||
193 |
def request(self, amount, email, **kwargs): |
|
194 |
try: |
|
195 |
montant = Decimal(amount) |
|
196 |
# MONTANT must be sent as centimes |
|
197 |
montant = montant * Decimal('100') |
|
198 |
montant = montant.to_integral_value(ROUND_DOWN) |
|
199 |
if not (Decimal('0') < montant <= Decimal('10000000')): |
|
200 |
raise ValueError('MONTANT > 100000 euros or < 0') |
|
201 |
montant = str(montant) |
|
202 |
except ValueError: |
|
203 |
raise ValueError( |
|
204 |
'MONTANT invalid format, must be ' |
|
205 |
'a decimal integer with less than 4 digits ' |
|
206 |
'before and 2 digits after the decimal point ' |
|
207 |
', here it is %s' % repr(amount)) |
|
208 | ||
209 |
numcli = self.numcli |
|
210 |
urlnotif = self.automatic_return_url |
|
211 |
urlredirect = self.normal_return_url |
|
212 |
exer = str(datetime.date.today().year) |
|
213 |
refdet = kwargs.get('refdet', self._generate_refdet()) |
|
214 |
mel = email |
|
215 |
if hasattr(mel, 'decode'): |
|
216 |
mel = email.decode('ascii') |
|
217 | ||
218 |
try: |
|
219 |
if '@' not in mel: |
|
220 |
raise ValueError('no @ in MEL') |
|
221 |
if not (6 <= len(mel) <= 80): |
|
222 |
raise ValueError('len(MEL) is invalid, must be between 6 and 80') |
|
223 |
except Exception as e: |
|
224 |
raise ValueError('MEL is not a valid email, %r' % mel, e) |
|
225 | ||
226 |
# check saisie |
|
227 |
saisie = self.saisie |
|
228 |
if saisie not in ('T', 'X', 'W'): |
|
229 |
raise ValueError('SAISIE invalid format, %r, must be M, T, X or A' % saisie) |
|
230 | ||
231 |
idop = self.payfip.get_idop(numcli=numcli, saisie=saisie, exer=exer, |
|
232 |
refdet=refdet, montant=montant, mel=mel, |
|
233 |
url_notification=urlnotif, |
|
234 |
url_redirect=urlredirect) |
|
235 | ||
236 |
return str(idop), URL, PAYMENT_URL % idop |
|
237 | ||
238 |
def response(self, query_string, **kwargs): |
|
239 |
fields = parse_qs(query_string, True) |
|
240 |
idop = (fields.get('idop') or [None])[0] |
|
241 | ||
242 |
if not idop: |
|
243 |
raise ResponseError('missing idop parameter in query string') |
|
244 | ||
245 |
try: |
|
246 |
response = self.payfip.get_info_paiement(idop) |
|
247 |
except PayFiPError as e: |
|
248 |
raise ResponseError('invalid return from payfip', e) |
|
249 | ||
250 |
if response.resultrans == 'P': |
|
251 |
result = PAID |
|
252 |
bank_status = '' |
|
253 |
elif response.resultrans == 'R': |
|
254 |
result = DENIED |
|
255 |
bank_status = 'refused' |
|
256 |
elif response.resultrans == 'A': |
|
257 |
result = CANCELLED |
|
258 |
bank_status = 'cancelled' |
|
259 |
else: |
|
260 |
result = ERROR |
|
261 |
bank_status = 'unknown result code: %r' % response.resultrans |
|
262 | ||
263 |
transaction_id = response.refdet |
|
264 |
transaction_id += ' ' + idop |
|
265 |
if response.numauto: |
|
266 |
transaction_id += ' ' + response.numauto |
|
267 | ||
268 |
return PaymentResponse( |
|
269 |
result=result, |
|
270 |
bank_status=bank_status, |
|
271 |
signed=True, |
|
272 |
bank_data={k: response[k] for k in response}, |
|
273 |
order_id=idop, |
|
274 |
transaction_id=transaction_id, |
|
275 |
test=response.saisie == 'T') |
|
276 | ||
277 | ||
278 |
if __name__ == '__main__': |
|
279 |
import click |
|
280 | ||
281 |
def show_payfip_error(func): |
|
282 |
@functools.wraps(func) |
|
283 |
def f(*args, **kwargs): |
|
284 |
try: |
|
285 |
return func(*args, **kwargs) |
|
286 |
except PayFiPError as e: |
|
287 |
click.echo(click.style('PayFiP ERROR : %s "%s"' % (e.code, e.message), fg='red')) |
|
288 |
return f |
|
289 | ||
290 |
@click.group() |
|
291 |
@click.option('--wsdl-url', default=None) |
|
292 |
@click.option('--service-url', default=None) |
|
293 |
@click.pass_context |
|
294 |
def main(ctx, wsdl_url, service_url): |
|
295 |
import logging |
|
296 |
logging.basicConfig(level=logging.INFO) |
|
297 |
# hide warning from zeep |
|
298 |
logging.getLogger('zeep.wsdl.bindings.soap').level = logging.ERROR |
|
299 | ||
300 |
ctx.obj = PayFiP(wsdl_url=wsdl_url, service_url=service_url) |
|
301 | ||
302 |
def numcli(ctx, param, value): |
|
303 |
if not isinstance(value, six.string_types) or len(value) != 6 or not value.isdigit(): |
|
304 |
raise click.BadParameter('numcli must a 6 digits number') |
|
305 |
return value |
|
306 | ||
307 |
@main.command() |
|
308 |
@click.argument('numcli', callback=numcli, type=str) |
|
309 |
@click.pass_obj |
|
310 |
@show_payfip_error |
|
311 |
def info_client(payfip, numcli): |
|
312 |
response = payfip.get_info_client(numcli) |
|
313 |
for key in response: |
|
314 |
print('%15s:' % key, response[key]) |
|
315 | ||
316 |
@main.command() |
|
317 |
@click.argument('numcli', callback=numcli, type=str) |
|
318 |
@click.option('--saisie', type=click.Choice(['T', 'X', 'W']), required=True) |
|
319 |
@click.option('--exer', type=str, required=True) |
|
320 |
@click.option('--montant', type=int, required=True) |
|
321 |
@click.option('--refdet', type=str, required=True) |
|
322 |
@click.option('--mel', type=str, required=True) |
|
323 |
@click.option('--url-notification', type=str, required=True) |
|
324 |
@click.option('--url-redirect', type=str, required=True) |
|
325 |
@click.option('--objet', default=None, type=str) |
|
326 |
@click.pass_obj |
|
327 |
@show_payfip_error |
|
328 |
def get_idop(payfip, numcli, saisie, exer, montant, refdet, mel, objet, url_notification, url_redirect): |
|
329 |
idop = payfip.get_idop(numcli=numcli, saisie=saisie, exer=exer, |
|
330 |
montant=montant, refdet=refdet, mel=mel, |
|
331 |
objet=objet, url_notification=url_notification, |
|
332 |
url_redirect=url_redirect) |
|
333 |
print('idOp:', idop) |
|
334 |
print(PAYMENT_URL % idop) |
|
335 | ||
336 |
@main.command() |
|
337 |
@click.argument('idop', type=str) |
|
338 |
@click.pass_obj |
|
339 |
@show_payfip_error |
|
340 |
def info_paiement(payfip, idop): |
|
341 |
print(payfip.get_info_paiement(idop)) |
|
342 | ||
343 |
main() |
|
344 | ||
345 | ||
346 |
eopayment/resource/PaiementSecuriseService.wsdl | ||
---|---|---|
1 |
<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.8 svn-revision#13980. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.7-b01-. --><definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService" name="PaiementSecuriseService"> |
|
2 |
<types> |
|
3 |
<xsd:schema> |
|
4 |
<xsd:import namespace="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService" schemaLocation="PaiementSecuriseService1.xsd"/> |
|
5 |
</xsd:schema> |
|
6 |
<xsd:schema> |
|
7 |
<xsd:import namespace="http://securite.service.tpa.cp.finances.gouv.fr/requete" schemaLocation="PaiementSecuriseService2.xsd"/> |
|
8 |
</xsd:schema> |
|
9 |
<xsd:schema> |
|
10 |
<xsd:import namespace="http://securite.service.tpa.cp.finances.gouv.fr/reponse" schemaLocation="PaiementSecuriseService3.xsd"/> |
|
11 |
</xsd:schema> |
|
12 |
</types> |
|
13 |
<message name="creerPaiementSecurise"> |
|
14 |
<part name="parameters" element="tns:creerPaiementSecurise"/> |
|
15 |
</message> |
|
16 |
<message name="creerPaiementSecuriseResponse"> |
|
17 |
<part name="parameters" element="tns:creerPaiementSecuriseResponse"/> |
|
18 |
</message> |
|
19 |
<message name="FonctionnelleErreur"> |
|
20 |
<part name="fault" element="tns:FonctionnelleErreur"/> |
|
21 |
</message> |
|
22 |
<message name="TechDysfonctionnementErreur"> |
|
23 |
<part name="fault" element="tns:TechDysfonctionnementErreur"/> |
|
24 |
</message> |
|
25 |
<message name="TechIndisponibiliteErreur"> |
|
26 |
<part name="fault" element="tns:TechIndisponibiliteErreur"/> |
|
27 |
</message> |
|
28 |
<message name="TechProtocolaireErreur"> |
|
29 |
<part name="fault" element="tns:TechProtocolaireErreur"/> |
|
30 |
</message> |
|
31 |
<message name="recupererDetailClient"> |
|
32 |
<part name="parameters" element="tns:recupererDetailClient"/> |
|
33 |
</message> |
|
34 |
<message name="recupererDetailClientResponse"> |
|
35 |
<part name="parameters" element="tns:recupererDetailClientResponse"/> |
|
36 |
</message> |
|
37 |
<message name="recupererDetailPaiementSecurise"> |
|
38 |
<part name="parameters" element="tns:recupererDetailPaiementSecurise"/> |
|
39 |
</message> |
|
40 |
<message name="recupererDetailPaiementSecuriseResponse"> |
|
41 |
<part name="parameters" element="tns:recupererDetailPaiementSecuriseResponse"/> |
|
42 |
</message> |
|
43 |
<portType name="PaiementSecuriseService"> |
|
44 |
<operation name="creerPaiementSecurise"> |
|
45 |
<input message="tns:creerPaiementSecurise"/> |
|
46 |
<output message="tns:creerPaiementSecuriseResponse"/> |
|
47 |
<fault message="tns:FonctionnelleErreur" name="FonctionnelleErreur"/> |
|
48 |
<fault message="tns:TechDysfonctionnementErreur" name="TechDysfonctionnementErreur"/> |
|
49 |
<fault message="tns:TechIndisponibiliteErreur" name="TechIndisponibiliteErreur"/> |
|
50 |
<fault message="tns:TechProtocolaireErreur" name="TechProtocolaireErreur"/> |
|
51 |
</operation> |
|
52 |
<operation name="recupererDetailClient"> |
|
53 |
<input message="tns:recupererDetailClient"/> |
|
54 |
<output message="tns:recupererDetailClientResponse"/> |
|
55 |
<fault message="tns:FonctionnelleErreur" name="FonctionnelleErreur"/> |
|
56 |
<fault message="tns:TechDysfonctionnementErreur" name="TechDysfonctionnementErreur"/> |
|
57 |
<fault message="tns:TechIndisponibiliteErreur" name="TechIndisponibiliteErreur"/> |
|
58 |
<fault message="tns:TechProtocolaireErreur" name="TechProtocolaireErreur"/> |
|
59 |
</operation> |
|
60 |
<operation name="recupererDetailPaiementSecurise"> |
|
61 |
<input message="tns:recupererDetailPaiementSecurise"/> |
|
62 |
<output message="tns:recupererDetailPaiementSecuriseResponse"/> |
|
63 |
<fault message="tns:FonctionnelleErreur" name="FonctionnelleErreur"/> |
|
64 |
<fault message="tns:TechDysfonctionnementErreur" name="TechDysfonctionnementErreur"/> |
|
65 |
<fault message="tns:TechIndisponibiliteErreur" name="TechIndisponibiliteErreur"/> |
|
66 |
<fault message="tns:TechProtocolaireErreur" name="TechProtocolaireErreur"/> |
|
67 |
</operation> |
|
68 |
</portType> |
|
69 |
<binding name="PaiementSecuriseServicePortBinding" type="tns:PaiementSecuriseService"> |
|
70 |
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> |
|
71 |
<operation name="creerPaiementSecurise"> |
|
72 |
<soap:operation soapAction=""/> |
|
73 |
<input> |
|
74 |
<soap:body use="literal"/> |
|
75 |
</input> |
|
76 |
<output> |
|
77 |
<soap:body use="literal"/> |
|
78 |
</output> |
|
79 |
<fault name="FonctionnelleErreur"> |
|
80 |
<soap:fault name="FonctionnelleErreur" use="literal"/> |
|
81 |
</fault> |
|
82 |
<fault name="TechDysfonctionnementErreur"> |
|
83 |
<soap:fault name="TechDysfonctionnementErreur" use="literal"/> |
|
84 |
</fault> |
|
85 |
<fault name="TechIndisponibiliteErreur"> |
|
86 |
<soap:fault name="TechIndisponibiliteErreur" use="literal"/> |
|
87 |
</fault> |
|
88 |
<fault name="TechProtocolaireErreur"> |
|
89 |
<soap:fault name="TechProtocolaireErreur" use="literal"/> |
|
90 |
</fault> |
|
91 |
</operation> |
|
92 |
<operation name="recupererDetailClient"> |
|
93 |
<soap:operation soapAction=""/> |
|
94 |
<input> |
|
95 |
<soap:body use="literal"/> |
|
96 |
</input> |
|
97 |
<output> |
|
98 |
<soap:body use="literal"/> |
|
99 |
</output> |
|
100 |
<fault name="FonctionnelleErreur"> |
|
101 |
<soap:fault name="FonctionnelleErreur" use="literal"/> |
|
102 |
</fault> |
|
103 |
<fault name="TechDysfonctionnementErreur"> |
|
104 |
<soap:fault name="TechDysfonctionnementErreur" use="literal"/> |
|
105 |
</fault> |
|
106 |
<fault name="TechIndisponibiliteErreur"> |
|
107 |
<soap:fault name="TechIndisponibiliteErreur" use="literal"/> |
|
108 |
</fault> |
|
109 |
<fault name="TechProtocolaireErreur"> |
|
110 |
<soap:fault name="TechProtocolaireErreur" use="literal"/> |
|
111 |
</fault> |
|
112 |
</operation> |
|
113 |
<operation name="recupererDetailPaiementSecurise"> |
|
114 |
<soap:operation soapAction=""/> |
|
115 |
<input> |
|
116 |
<soap:body use="literal"/> |
|
117 |
</input> |
|
118 |
<output> |
|
119 |
<soap:body use="literal"/> |
|
120 |
</output> |
|
121 |
<fault name="FonctionnelleErreur"> |
|
122 |
<soap:fault name="FonctionnelleErreur" use="literal"/> |
|
123 |
</fault> |
|
124 |
<fault name="TechDysfonctionnementErreur"> |
|
125 |
<soap:fault name="TechDysfonctionnementErreur" use="literal"/> |
|
126 |
</fault> |
|
127 |
<fault name="TechIndisponibiliteErreur"> |
|
128 |
<soap:fault name="TechIndisponibiliteErreur" use="literal"/> |
|
129 |
</fault> |
|
130 |
<fault name="TechProtocolaireErreur"> |
|
131 |
<soap:fault name="TechProtocolaireErreur" use="literal"/> |
|
132 |
</fault> |
|
133 |
</operation> |
|
134 |
</binding> |
|
135 |
<service name="PaiementSecuriseService"> |
|
136 |
<port name="PaiementSecuriseServicePort" binding="tns:PaiementSecuriseServicePortBinding"> |
|
137 |
<soap:address location="http://www.tipi.budget.gouv.fr:80/tpa/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService"/> |
|
138 |
</port> |
|
139 |
</service> |
|
140 |
</definitions> |
eopayment/resource/PaiementSecuriseService1.xsd | ||
---|---|---|
1 |
<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.8 svn-revision#13980. --><xs:schema xmlns:ns2="http://securite.service.tpa.cp.finances.gouv.fr/reponse" xmlns:ns1="http://securite.service.tpa.cp.finances.gouv.fr/requete" xmlns:tns="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService"> |
|
2 | ||
3 |
<xs:import namespace="http://securite.service.tpa.cp.finances.gouv.fr/requete" schemaLocation="PaiementSecuriseService2.xsd"/> |
|
4 | ||
5 |
<xs:import namespace="http://securite.service.tpa.cp.finances.gouv.fr/reponse" schemaLocation="PaiementSecuriseService3.xsd"/> |
|
6 | ||
7 |
<xs:element name="FonctionnelleErreur" type="tns:FonctionnelleErreur"/> |
|
8 | ||
9 |
<xs:element name="TechDysfonctionnementErreur" type="tns:TechDysfonctionnementErreur"/> |
|
10 | ||
11 |
<xs:element name="TechIndisponibiliteErreur" type="tns:TechIndisponibiliteErreur"/> |
|
12 | ||
13 |
<xs:element name="TechProtocolaireErreur" type="tns:TechProtocolaireErreur"/> |
|
14 | ||
15 |
<xs:element name="creerPaiementSecurise" type="tns:creerPaiementSecurise"/> |
|
16 | ||
17 |
<xs:element name="creerPaiementSecuriseResponse" type="tns:creerPaiementSecuriseResponse"/> |
|
18 | ||
19 |
<xs:element name="recupererDetailClient" type="tns:recupererDetailClient"/> |
|
20 | ||
21 |
<xs:element name="recupererDetailClientResponse" type="tns:recupererDetailClientResponse"/> |
|
22 | ||
23 |
<xs:element name="recupererDetailPaiementSecurise" type="tns:recupererDetailPaiementSecurise"/> |
|
24 | ||
25 |
<xs:element name="recupererDetailPaiementSecuriseResponse" type="tns:recupererDetailPaiementSecuriseResponse"/> |
|
26 | ||
27 |
<xs:complexType name="creerPaiementSecurise"> |
|
28 |
<xs:sequence> |
|
29 |
<xs:element name="arg0" type="ns1:CreerPaiementSecuriseRequest" minOccurs="0"/> |
|
30 |
</xs:sequence> |
|
31 |
</xs:complexType> |
|
32 | ||
33 |
<xs:complexType name="creerPaiementSecuriseResponse"> |
|
34 |
<xs:sequence> |
|
35 |
<xs:element name="return" type="ns2:CreerPaiementSecuriseResponse" minOccurs="0"/> |
|
36 |
</xs:sequence> |
|
37 |
</xs:complexType> |
|
38 | ||
39 |
<xs:complexType name="FonctionnelleErreur"> |
|
40 |
<xs:sequence> |
|
41 |
<xs:element name="code" type="xs:string" minOccurs="0"/> |
|
42 |
<xs:element name="descriptif" type="xs:string" minOccurs="0"/> |
|
43 |
<xs:element name="libelle" type="xs:string" minOccurs="0"/> |
|
44 |
<xs:element name="message" type="xs:string" minOccurs="0"/> |
|
45 |
<xs:element name="severite" type="xs:int"/> |
|
46 |
<xs:element name="suppressed" type="tns:throwable" nillable="true" minOccurs="0" maxOccurs="unbounded"/> |
|
47 |
</xs:sequence> |
|
48 |
</xs:complexType> |
|
49 | ||
50 |
<xs:complexType name="throwable"> |
|
51 |
<xs:sequence> |
|
52 |
<xs:element name="stackTrace" type="tns:stackTraceElement" nillable="true" minOccurs="0" maxOccurs="unbounded"/> |
|
53 |
</xs:sequence> |
|
54 |
</xs:complexType> |
|
55 | ||
56 |
<xs:complexType name="stackTraceElement" final="extension restriction"> |
|
57 |
<xs:sequence/> |
|
58 |
</xs:complexType> |
|
59 | ||
60 |
<xs:complexType name="TechDysfonctionnementErreur"> |
|
61 |
<xs:sequence> |
|
62 |
<xs:element name="code" type="xs:string" minOccurs="0"/> |
|
63 |
<xs:element name="descriptif" type="xs:string" minOccurs="0"/> |
|
64 |
<xs:element name="libelle" type="xs:string" minOccurs="0"/> |
|
65 |
<xs:element name="message" type="xs:string" minOccurs="0"/> |
|
66 |
<xs:element name="severite" type="xs:int"/> |
|
67 |
<xs:element name="suppressed" type="tns:throwable" nillable="true" minOccurs="0" maxOccurs="unbounded"/> |
|
68 |
</xs:sequence> |
|
69 |
</xs:complexType> |
|
70 | ||
71 |
<xs:complexType name="TechIndisponibiliteErreur"> |
|
72 |
<xs:sequence> |
|
73 |
<xs:element name="code" type="xs:string" minOccurs="0"/> |
|
74 |
<xs:element name="descriptif" type="xs:string" minOccurs="0"/> |
|
75 |
<xs:element name="libelle" type="xs:string" minOccurs="0"/> |
|
76 |
<xs:element name="message" type="xs:string" minOccurs="0"/> |
|
77 |
<xs:element name="severite" type="xs:int"/> |
|
78 |
<xs:element name="suppressed" type="tns:throwable" nillable="true" minOccurs="0" maxOccurs="unbounded"/> |
|
79 |
</xs:sequence> |
|
80 |
</xs:complexType> |
|
81 | ||
82 |
<xs:complexType name="TechProtocolaireErreur"> |
|
83 |
<xs:sequence> |
|
84 |
<xs:element name="code" type="xs:string" minOccurs="0"/> |
|
85 |
<xs:element name="descriptif" type="xs:string" minOccurs="0"/> |
|
86 |
<xs:element name="libelle" type="xs:string" minOccurs="0"/> |
|
87 |
<xs:element name="message" type="xs:string" minOccurs="0"/> |
|
88 |
<xs:element name="severite" type="xs:int"/> |
|
89 |
<xs:element name="suppressed" type="tns:throwable" nillable="true" minOccurs="0" maxOccurs="unbounded"/> |
|
90 |
</xs:sequence> |
|
91 |
</xs:complexType> |
|
92 | ||
93 |
<xs:complexType name="recupererDetailClient"> |
|
94 |
<xs:sequence> |
|
95 |
<xs:element name="arg0" type="ns2:RecupererDetailClientRequest" minOccurs="0"/> |
|
96 |
</xs:sequence> |
|
97 |
</xs:complexType> |
|
98 | ||
99 |
<xs:complexType name="recupererDetailClientResponse"> |
|
100 |
<xs:sequence> |
|
101 |
<xs:element name="return" type="ns2:RecupererDetailClientResponse" minOccurs="0"/> |
|
102 |
</xs:sequence> |
|
103 |
</xs:complexType> |
|
104 | ||
105 |
<xs:complexType name="recupererDetailPaiementSecurise"> |
|
106 |
<xs:sequence> |
|
107 |
<xs:element name="arg0" type="ns2:RecupererDetailPaiementSecuriseRequest" minOccurs="0"/> |
|
108 |
</xs:sequence> |
|
109 |
</xs:complexType> |
|
110 | ||
111 |
<xs:complexType name="recupererDetailPaiementSecuriseResponse"> |
|
112 |
<xs:sequence> |
|
113 |
<xs:element name="return" type="ns2:RecupererDetailPaiementSecuriseResponse" minOccurs="0"/> |
|
114 |
</xs:sequence> |
|
115 |
</xs:complexType> |
|
116 |
</xs:schema> |
eopayment/resource/PaiementSecuriseService2.xsd | ||
---|---|---|
1 |
<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.8 svn-revision#13980. --><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://securite.service.tpa.cp.finances.gouv.fr/requete"> |
|
2 | ||
3 |
<xs:complexType name="CreerPaiementSecuriseRequest"> |
|
4 |
<xs:sequence> |
|
5 |
<xs:element name="exer" type="xs:string" minOccurs="0"/> |
|
6 |
<xs:element name="mel" type="xs:string" minOccurs="0"/> |
|
7 |
<xs:element name="montant" type="xs:string" minOccurs="0"/> |
|
8 |
<xs:element name="numcli" type="xs:string" minOccurs="0"/> |
|
9 |
<xs:element name="objet" type="xs:string" minOccurs="0"/> |
|
10 |
<xs:element name="refdet" type="xs:string" minOccurs="0"/> |
|
11 |
<xs:element name="saisie" type="xs:string" minOccurs="0"/> |
|
12 |
<xs:element name="urlnotif" type="xs:string" minOccurs="0"/> |
|
13 |
<xs:element name="urlredirect" type="xs:string" minOccurs="0"/> |
|
14 |
</xs:sequence> |
|
15 |
</xs:complexType> |
|
16 |
</xs:schema> |
eopayment/resource/PaiementSecuriseService3.xsd | ||
---|---|---|
1 |
<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.8 svn-revision#13980. --><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://securite.service.tpa.cp.finances.gouv.fr/reponse"> |
|
2 | ||
3 |
<xs:complexType name="CreerPaiementSecuriseResponse"> |
|
4 |
<xs:sequence> |
|
5 |
<xs:element name="idOp" type="xs:string" minOccurs="0"/> |
|
6 |
</xs:sequence> |
|
7 |
</xs:complexType> |
|
8 | ||
9 |
<xs:complexType name="RecupererDetailClientRequest"> |
|
10 |
<xs:sequence> |
|
11 |
<xs:element name="numCli" type="xs:string" minOccurs="0"/> |
|
12 |
</xs:sequence> |
|
13 |
</xs:complexType> |
|
14 | ||
15 |
<xs:complexType name="RecupererDetailClientResponse"> |
|
16 |
<xs:sequence> |
|
17 |
<xs:element name="libelleN1" type="xs:string" minOccurs="0"/> |
|
18 |
<xs:element name="libelleN2" type="xs:string" minOccurs="0"/> |
|
19 |
<xs:element name="libelleN3" type="xs:string" minOccurs="0"/> |
|
20 |
<xs:element name="numcli" type="xs:string" minOccurs="0"/> |
|
21 |
<xs:element name="IdentifiantGen" type="xs:string" minOccurs="0"/> |
|
22 |
</xs:sequence> |
|
23 |
</xs:complexType> |
|
24 | ||
25 |
<xs:complexType name="RecupererDetailPaiementSecuriseRequest"> |
|
26 |
<xs:sequence> |
|
27 |
<xs:element name="idOp" type="xs:string" minOccurs="0"/> |
|
28 |
</xs:sequence> |
|
29 |
</xs:complexType> |
|
30 | ||
31 |
<xs:complexType name="RecupererDetailPaiementSecuriseResponse"> |
|
32 |
<xs:sequence> |
|
33 |
<xs:element name="dattrans" type="xs:string" minOccurs="0"/> |
|
34 |
<xs:element name="exer" type="xs:string" minOccurs="0"/> |
|
35 |
<xs:element name="heurtrans" type="xs:string" minOccurs="0"/> |
|
36 |
<xs:element name="idOp" type="xs:string" minOccurs="0"/> |
|
37 |
<xs:element name="mel" type="xs:string" minOccurs="0"/> |
|
38 |
<xs:element name="montant" type="xs:string" minOccurs="0"/> |
|
39 |
<xs:element name="numauto" type="xs:string" minOccurs="0"/> |
|
40 |
<xs:element name="numcli" type="xs:string" minOccurs="0"/> |
|
41 |
<xs:element name="objet" type="xs:string" minOccurs="0"/> |
|
42 |
<xs:element name="refdet" type="xs:string" minOccurs="0"/> |
|
43 |
<xs:element name="resultrans" type="xs:string" minOccurs="0"/> |
|
44 |
<xs:element name="saisie" type="xs:string" minOccurs="0"/> |
|
45 |
</xs:sequence> |
|
46 |
</xs:complexType> |
|
47 |
</xs:schema> |
setup.py | ||
---|---|---|
123 | 123 |
'pytz', |
124 | 124 |
'requests', |
125 | 125 |
'six', |
126 |
'click', |
|
127 |
'zeep', |
|
126 | 128 |
], |
127 | 129 |
cmdclass={ |
128 | 130 |
'sdist': eo_sdist, |
tests/data/payfip-test_get_client_info.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailClient xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <numCli>090909</numCli>\n </arg0>\n </ns0:recupererDetailClient>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:recupererDetailClientResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <libelleN1>RR COMPOSTEURS INDIVIDUELS</libelleN1>\n <libelleN2>POUETPOUET</libelleN2>\n <libelleN3>COLLECTE VALORISATION DECHETS</libelleN3>\n <numcli>090909</numcli>\n </return>\n </ns2:recupererDetailClientResponse>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_idop_adresse_mel_incorrect.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doeexample.com</mel>\n <montant>9990000001</montant>\n <numcli>090909</numcli>\n <objet>coucou</objet>\n <refdet>ABCDEF</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <S:Fault xmlns:ns4=\"http://www.w3.org/2003/05/soap-envelope\">\n <faultcode>S:Server</faultcode>\n <faultstring>fr.gouv.finances.cp.tpa.webservice.exceptions.FonctionnelleErreur</faultstring>\n <detail>\n <ns2:FonctionnelleErreur xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <code>A2</code>\n <descriptif/>\n <libelle>Adresse mél incorrecte. </libelle>\n <severite>2</severite>\n </ns2:FonctionnelleErreur>\n </detail>\n </S:Fault>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_idop_ok.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doe@example.com</mel>\n <montant>1000</montant>\n <numcli>090909</numcli>\n <objet>coucou</objet>\n <refdet>ABCDEFGH</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:creerPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </return>\n </ns2:creerPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_idop_refdet_error.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doe@example.com</mel>\n <montant>1000</montant>\n <numcli>090909</numcli>\n <objet>coucou</objet>\n <refdet>ABCD</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <S:Fault xmlns:ns4=\"http://www.w3.org/2003/05/soap-envelope\">\n <faultcode>S:Server</faultcode>\n <faultstring>fr.gouv.finances.cp.tpa.webservice.exceptions.FonctionnelleErreur</faultstring>\n <detail>\n <ns2:FonctionnelleErreur xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <code>R3</code>\n <descriptif/>\n <libelle>Le format du paramètre REFDET n'est pas conforme</libelle>\n <severite>2</severite>\n </ns2:FonctionnelleErreur>\n </detail>\n </S:Fault>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_info_paiement_P1.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <S:Fault xmlns:ns4=\"http://www.w3.org/2003/05/soap-envelope\">\n <faultcode>S:Server</faultcode>\n <faultstring>fr.gouv.finances.cp.tpa.webservice.exceptions.FonctionnelleErreur</faultstring>\n <detail>\n <ns2:FonctionnelleErreur xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <code>P1</code>\n <descriptif/>\n <libelle>IdOp incorrect.</libelle>\n <severite>2</severite>\n </ns2:FonctionnelleErreur>\n </detail>\n </S:Fault>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_info_paiement_P5.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <S:Fault xmlns:ns4=\"http://www.w3.org/2003/05/soap-envelope\">\n <faultcode>S:Server</faultcode>\n <faultstring>fr.gouv.finances.cp.tpa.webservice.exceptions.FonctionnelleErreur</faultstring>\n <detail>\n <ns2:FonctionnelleErreur xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <code>P5</code><descriptif />\n <libelle>Résultat de la transaction non connu.</libelle><severite>2</severite>\n </ns2:FonctionnelleErreur>\n </detail>\n </S:Fault>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_get_info_paiement_ok.json | ||
---|---|---|
1 |
[["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:recupererDetailPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <dattrans>12122019</dattrans><exer>20</exer><heurtrans>1311</heurtrans><idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp><mel>john.doe@example.com</mel><montant>1000</montant><numauto>112233445566-tip</numauto><numcli>090909</numcli><objet>coucou</objet><refdet>EFEFAEFG</refdet><resultrans>V</resultrans><saisie>T</saisie>\n </return>\n </ns2:recupererDetailPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"]] |
tests/data/payfip-test_payment_cancelled.json | ||
---|---|---|
1 |
[ |
|
2 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doe@example.com</mel>\n <montant>1000</montant>\n <numcli>090909</numcli>\n <refdet>201912261758460053903194</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:creerPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </return>\n </ns2:creerPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"], |
|
3 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:recupererDetailPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <dattrans>12122019</dattrans><exer>20</exer><heurtrans>1311</heurtrans><idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp><mel>john.doe@example.com</mel><montant>1000</montant><numcli>090909</numcli><refdet>201912261758460053903194</refdet><resultrans>A</resultrans><saisie>T</saisie>\n </return>\n </ns2:recupererDetailPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"] |
|
4 |
] |
tests/data/payfip-test_payment_denied.json | ||
---|---|---|
1 |
[ |
|
2 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doe@example.com</mel>\n <montant>1000</montant>\n <numcli>090909</numcli>\n <refdet>201912261758460053903194</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:creerPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </return>\n </ns2:creerPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"], |
|
3 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:recupererDetailPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <dattrans>12122019</dattrans><exer>20</exer><heurtrans>1311</heurtrans><idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp><mel>john.doe@example.com</mel><montant>1000</montant><numcli>090909</numcli><refdet>201912261758460053903194</refdet><resultrans>R</resultrans><saisie>T</saisie>\n </return>\n </ns2:recupererDetailPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"] |
|
4 |
] |
tests/data/payfip-test_payment_ok.json | ||
---|---|---|
1 |
[ |
|
2 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:creerPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <exer>2019</exer>\n <mel>john.doe@example.com</mel>\n <montant>1000</montant>\n <numcli>090909</numcli>\n <refdet>201912261758460053903194</refdet>\n <saisie>T</saisie>\n <urlnotif>https://notif.payfip.example.com/</urlnotif>\n <urlredirect>https://redirect.payfip.example.com/</urlredirect>\n </arg0>\n </ns0:creerPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:creerPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </return>\n </ns2:creerPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"], |
|
3 |
["<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <soap-env:Body>\n <ns0:recupererDetailPaiementSecurise xmlns:ns0=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <arg0>\n <idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp>\n </arg0>\n </ns0:recupererDetailPaiementSecurise>\n </soap-env:Body>\n</soap-env:Envelope>\n", "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <S:Body>\n <ns2:recupererDetailPaiementSecuriseResponse xmlns:ns2=\"http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/contrat_paiement_securise/PaiementSecuriseService\">\n <return>\n <dattrans>12122019</dattrans><exer>20</exer><heurtrans>1311</heurtrans><idOp>cc0cb210-1cd4-11ea-8cca-0213ad91a103</idOp><mel>john.doe@example.com</mel><montant>1000</montant><numauto>112233445566-tip</numauto><numcli>090909</numcli><refdet>201912261758460053903194</refdet><resultrans>P</resultrans><saisie>T</saisie>\n </return>\n </ns2:recupererDetailPaiementSecuriseResponse>\n </S:Body>\n</S:Envelope>\n"] |
|
4 |
] |
tests/test_payfip_ws.py | ||
---|---|---|
1 |
# coding: utf-8 |
|
2 |
# |
|
3 |
# eopayment - online payment library |
|
4 |
# Copyright (C) 2011-2019 Entr'ouvert |
|
5 |
# |
|
6 |
# This program is free software: you can redistribute it and/or modify it |
|
7 |
# under the terms of the GNU Affero General Public License as published |
|
8 |
# by the Free Software Foundation, either version 3 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU Affero General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU Affero General Public License |
|
17 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
18 | ||
19 |
from __future__ import print_function, unicode_literals |
|
20 | ||
21 |
import json |
|
22 |
import lxml.etree as ET |
|
23 | ||
24 |
import httmock |
|
25 |
import pytest |
|
26 | ||
27 |
from zeep.plugins import HistoryPlugin |
|
28 | ||
29 |
import eopayment |
|
30 |
from eopayment.payfip_ws import PayFiP, PayFiPError |
|
31 | ||
32 | ||
33 |
def xmlindent(content): |
|
34 |
if hasattr(content, 'encode') or hasattr(content, 'decode'): |
|
35 |
content = ET.fromstring(content) |
|
36 |
return ET.tostring(content, pretty_print=True).decode('utf-8', 'ignore') |
|
37 | ||
38 |
NUMCLI = '090909' |
|
39 | ||
40 | ||
41 |
# freeze time to fix EXER field to 2019 |
|
42 |
@pytest.fixture(autouse=True) |
|
43 |
def freezer(freezer): |
|
44 |
freezer.move_to('2019-12-12') |
|
45 | ||
46 | ||
47 |
class PayFiPHTTMock(object): |
|
48 |
def __init__(self, request): |
|
49 |
history_path = 'tests/data/payfip-%s.json' % request.function.__name__ |
|
50 |
with open(history_path) as fd: |
|
51 |
self.history = json.load(fd) |
|
52 |
self.counter = 0 |
|
53 | ||
54 |
@httmock.urlmatch() |
|
55 |
def mock(self, url, request): |
|
56 |
request_content, response_content = self.history[self.counter] |
|
57 |
self.counter += 1 |
|
58 |
assert xmlindent(request.body) == request_content |
|
59 |
return response_content |
|
60 | ||
61 | ||
62 |
@pytest.fixture |
|
63 |
def payfip(request): |
|
64 |
history = HistoryPlugin() |
|
65 | ||
66 |
@httmock.urlmatch() |
|
67 |
def raise_on_request(url, request): |
|
68 |
# ensure we do not access network |
|
69 |
from requests.exceptions import RequestException |
|
70 |
raise RequestException('huhu') |
|
71 | ||
72 |
with httmock.HTTMock(raise_on_request): |
|
73 |
payfip = PayFiP(wsdl_url='file://eopayment/resource/PaiementSecuriseService.wsdl', |
|
74 |
zeep_client_kwargs={'plugins': [history]}) |
|
75 |
try: |
|
76 |
if 'update_data' not in request.keywords: |
|
77 |
with httmock.HTTMock(PayFiPHTTMock(request).mock): |
|
78 |
yield payfip |
|
79 |
else: |
|
80 |
yield payfip |
|
81 |
finally: |
|
82 |
# add @pytest.mark.update_data to test to update fixtures data |
|
83 |
if 'update_data' in request.keywords: |
|
84 |
history_path = 'tests/data/payfip-%s.json' % request.function.__name__ |
|
85 |
d = [ |
|
86 |
(xmlindent(exchange['sent']['envelope']), |
|
87 |
xmlindent(exchange['received']['envelope'])) |
|
88 |
for exchange in history._buffer |
|
89 |
] |
|
90 |
content = json.dumps(d) |
|
91 |
with open(history_path, 'wb') as fd: |
|
92 |
fd.write(content) |
|
93 | ||
94 |
# pytestmark = pytest.mark.update_data |
|
95 | ||
96 | ||
97 |
def test_get_client_info(payfip): |
|
98 |
result = payfip.get_info_client(NUMCLI) |
|
99 |
assert result.numcli == NUMCLI |
|
100 |
assert result.libelleN2 == 'POUETPOUET' |
|
101 | ||
102 |
NOTIF_URL = 'https://notif.payfip.example.com/' |
|
103 |
REDIRECT_URL = 'https://redirect.payfip.example.com/' |
|
104 | ||
105 | ||
106 |
def test_get_idop_ok(payfip): |
|
107 |
result = payfip.get_idop( |
|
108 |
numcli=NUMCLI, |
|
109 |
exer='2019', |
|
110 |
refdet='ABCDEFGH', |
|
111 |
montant='1000', |
|
112 |
mel='john.doe@example.com', |
|
113 |
objet='coucou', |
|
114 |
url_notification=NOTIF_URL, |
|
115 |
url_redirect=REDIRECT_URL, |
|
116 |
saisie='T') |
|
117 |
assert result == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
118 | ||
119 | ||
120 |
def test_get_idop_refdet_error(payfip): |
|
121 |
with pytest.raises(PayFiPError, match='.*R3.*Le format.*REFDET.*conforme'): |
|
122 |
payfip.get_idop( |
|
123 |
numcli=NUMCLI, |
|
124 |
exer='2019', |
|
125 |
refdet='ABCD', |
|
126 |
montant='1000', |
|
127 |
mel='john.doe@example.com', |
|
128 |
objet='coucou', |
|
129 |
url_notification='https://notif.payfip.example.com/', |
|
130 |
url_redirect='https://redirect.payfip.example.com/', |
|
131 |
saisie='T') |
|
132 | ||
133 | ||
134 |
def test_get_idop_adresse_mel_incorrect(payfip): |
|
135 |
with pytest.raises(PayFiPError, match='.*A2.*Adresse.*incorrecte'): |
|
136 |
payfip.get_idop( |
|
137 |
numcli=NUMCLI, |
|
138 |
exer='2019', |
|
139 |
refdet='ABCDEF', |
|
140 |
montant='9990000001', |
|
141 |
mel='john.doeexample.com', |
|
142 |
objet='coucou', |
|
143 |
url_notification='https://notif.payfip.example.com/', |
|
144 |
url_redirect='https://redirect.payfip.example.com/', |
|
145 |
saisie='T') |
|
146 | ||
147 | ||
148 |
def test_get_info_paiement_ok(payfip): |
|
149 |
result = payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103') |
|
150 |
assert {k: result[k] for k in result} == { |
|
151 |
'dattrans': '12122019', |
|
152 |
'exer': '20', |
|
153 |
'heurtrans': '1311', |
|
154 |
'idOp': 'cc0cb210-1cd4-11ea-8cca-0213ad91a103', |
|
155 |
'mel': 'john.doe@example.com', |
|
156 |
'montant': '1000', |
|
157 |
'numauto': '112233445566-tip', |
|
158 |
'numcli': NUMCLI, |
|
159 |
'objet': 'coucou', |
|
160 |
'refdet': 'EFEFAEFG', |
|
161 |
'resultrans': 'V', |
|
162 |
'saisie': 'T' |
|
163 |
} |
|
164 | ||
165 | ||
166 |
def test_get_info_paiement_P1(payfip): |
|
167 |
# idop par pas encore reçu par la plate-forme ou déjà nettoyé (la nuit) |
|
168 |
with pytest.raises(PayFiPError, match='.*P1.*IdOp incorrect.*'): |
|
169 |
payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103') |
|
170 | ||
171 | ||
172 |
def test_get_info_paiement_P5(payfip): |
|
173 |
# idop reçu par la plate-forme mais transaction en cours |
|
174 |
with pytest.raises(PayFiPError, match='.*P5.*sultat de la transaction non connu.*'): |
|
175 |
payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103') |
|
176 | ||
177 | ||
178 |
def test_payment_ok(request): |
|
179 |
payment = eopayment.Payment('payfip_ws', { |
|
180 |
'numcli': '090909', |
|
181 |
'automatic_return_url': NOTIF_URL, |
|
182 |
'normal_return_url': REDIRECT_URL, |
|
183 |
}) |
|
184 | ||
185 |
with httmock.HTTMock(PayFiPHTTMock(request).mock): |
|
186 |
payment_id, kind, url = payment.request( |
|
187 |
amount='10.00', |
|
188 |
email='john.doe@example.com', |
|
189 |
# make test deterministic |
|
190 |
refdet='201912261758460053903194') |
|
191 | ||
192 |
assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
193 |
assert kind == eopayment.URL |
|
194 |
assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
195 | ||
196 |
response = payment.response('idop=%s' % payment_id) |
|
197 |
assert response.result == eopayment.PAID |
|
198 |
assert response.bank_status == '' |
|
199 |
assert response.order_id == payment_id |
|
200 |
assert response.transaction_id == ( |
|
201 |
'201912261758460053903194 cc0cb210-1cd4-11ea-8cca-0213ad91a103 112233445566-tip') |
|
202 | ||
203 | ||
204 |
def test_payment_denied(request): |
|
205 |
payment = eopayment.Payment('payfip_ws', { |
|
206 |
'numcli': '090909', |
|
207 |
'automatic_return_url': NOTIF_URL, |
|
208 |
'normal_return_url': REDIRECT_URL, |
|
209 |
}) |
|
210 | ||
211 |
with httmock.HTTMock(PayFiPHTTMock(request).mock): |
|
212 |
payment_id, kind, url = payment.request( |
|
213 |
amount='10.00', |
|
214 |
email='john.doe@example.com', |
|
215 |
# make test deterministic |
|
216 |
refdet='201912261758460053903194') |
|
217 | ||
218 |
assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
219 |
assert kind == eopayment.URL |
|
220 |
assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
221 | ||
222 |
response = payment.response('idop=%s' % payment_id) |
|
223 |
assert response.result == eopayment.DENIED |
|
224 |
assert response.bank_status == 'refused' |
|
225 |
assert response.order_id == payment_id |
|
226 |
assert response.transaction_id == '201912261758460053903194 cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
227 | ||
228 | ||
229 |
def test_payment_cancelled(request): |
|
230 |
payment = eopayment.Payment('payfip_ws', { |
|
231 |
'numcli': '090909', |
|
232 |
'automatic_return_url': NOTIF_URL, |
|
233 |
'normal_return_url': REDIRECT_URL, |
|
234 |
}) |
|
235 | ||
236 |
with httmock.HTTMock(PayFiPHTTMock(request).mock): |
|
237 |
payment_id, kind, url = payment.request( |
|
238 |
amount='10.00', |
|
239 |
email='john.doe@example.com', |
|
240 |
# make test deterministic |
|
241 |
refdet='201912261758460053903194') |
|
242 | ||
243 |
assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
244 |
assert kind == eopayment.URL |
|
245 |
assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
|
246 | ||
247 |
response = payment.response('idop=%s' % payment_id) |
|
248 |
assert response.result == eopayment.CANCELLED |
|
249 |
assert response.bank_status == 'cancelled' |
|
250 |
assert response.order_id == payment_id |
|
251 |
assert response.transaction_id == '201912261758460053903194 cc0cb210-1cd4-11ea-8cca-0213ad91a103' |
tox.ini | ||
---|---|---|
18 | 18 |
pytest-freezegun |
19 | 19 |
py2: pytest-cov |
20 | 20 |
mock |
21 |
httmock |
|
22 |
lxml |
|
23 | ||
24 |
[pytest] |
|
25 |
filterwarnings = |
|
26 |
ignore:defusedxml.lxml is no longer supported.* |
|
27 |
markers = |
|
28 |
update_data |
|
21 |
- |