Projet

Général

Profil

0001-start-support-for-PayFiP-Regie-web-service-38405.patch

Benjamin Dauvergne, 26 décembre 2019 19:17

Télécharger (60,7 ko)

Voir les différences:

Subject: [PATCH 1/2] start support for PayFiP Regie web-service (#38405)

 MANIFEST.in                                   |   4 +
 README.txt                                    |  31 ++
 eopayment/payfip_ws.py                        | 346 ++++++++++++++++++
 .../resource/PaiementSecuriseService.wsdl     | 140 +++++++
 .../resource/PaiementSecuriseService1.xsd     | 116 ++++++
 .../resource/PaiementSecuriseService2.xsd     |  16 +
 .../resource/PaiementSecuriseService3.xsd     |  47 +++
 setup.py                                      |   2 +
 tests/data/payfip-test_get_client_info.json   |   1 +
 ...p-test_get_idop_adresse_mel_incorrect.json |   1 +
 tests/data/payfip-test_get_idop_ok.json       |   1 +
 .../payfip-test_get_idop_refdet_error.json    |   1 +
 .../payfip-test_get_info_paiement_P1.json     |   1 +
 .../payfip-test_get_info_paiement_P5.json     |   1 +
 .../payfip-test_get_info_paiement_ok.json     |   1 +
 tests/data/payfip-test_payment_cancelled.json |   4 +
 tests/data/payfip-test_payment_denied.json    |   4 +
 tests/data/payfip-test_payment_ok.json        |   4 +
 tests/test_payfip_ws.py                       | 245 +++++++++++++
 tox.ini                                       |   8 +
 20 files changed, 974 insertions(+)
 create mode 100644 eopayment/payfip_ws.py
 create mode 100644 eopayment/resource/PaiementSecuriseService.wsdl
 create mode 100644 eopayment/resource/PaiementSecuriseService1.xsd
 create mode 100644 eopayment/resource/PaiementSecuriseService2.xsd
 create mode 100644 eopayment/resource/PaiementSecuriseService3.xsd
 create mode 100644 tests/data/payfip-test_get_client_info.json
 create mode 100644 tests/data/payfip-test_get_idop_adresse_mel_incorrect.json
 create mode 100644 tests/data/payfip-test_get_idop_ok.json
 create mode 100644 tests/data/payfip-test_get_idop_refdet_error.json
 create mode 100644 tests/data/payfip-test_get_info_paiement_P1.json
 create mode 100644 tests/data/payfip-test_get_info_paiement_P5.json
 create mode 100644 tests/data/payfip-test_get_info_paiement_ok.json
 create mode 100644 tests/data/payfip-test_payment_cancelled.json
 create mode 100644 tests/data/payfip-test_payment_denied.json
 create mode 100644 tests/data/payfip-test_payment_ok.json
 create mode 100644 tests/test_payfip_ws.py
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)
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(Exception):
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 Decimal('0') > montant > Decimal('999999'):
200
                raise ValueError('MONTANT > 9999.99 euros')
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&#233;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&#232;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&#233;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
class PayFiPHTTMock(object):
42
    def __init__(self, request):
43
        history_path = 'tests/data/payfip-%s.json' % request.function.__name__
44
        with open(history_path) as fd:
45
            self.history = json.load(fd)
46
        self.counter = 0
47

  
48
    @httmock.urlmatch()
49
    def mock(self, url, request):
50
        request_content, response_content = self.history[self.counter]
51
        self.counter += 1
52
        assert xmlindent(request.body) == request_content
53
        return response_content
54

  
55

  
56
@pytest.fixture
57
def payfip(request):
58
    history = HistoryPlugin()
59

  
60
    @httmock.urlmatch()
61
    def raise_on_request(url, request):
62
        # ensure we do not access network
63
        from requests.exceptions import RequestException
64
        raise RequestException('huhu')
65

  
66
    with httmock.HTTMock(raise_on_request):
67
        payfip = PayFiP(wsdl_url='file://eopayment/resource/PaiementSecuriseService.wsdl',
68
                        zeep_client_kwargs={'plugins': [history]})
69
    try:
70
        if 'update_data' not in request.keywords:
71
            with httmock.HTTMock(PayFiPHTTMock(request).mock):
72
                yield payfip
73
        else:
74
            yield payfip
75
    finally:
76
        # add @pytest.mark.update_data to test to update fixtures data
77
        if 'update_data' in request.keywords:
78
            history_path = 'tests/data/payfip-%s.json' % request.function.__name__
79
            d = [
80
                (xmlindent(exchange['sent']['envelope']),
81
                 xmlindent(exchange['received']['envelope']))
82
                for exchange in history._buffer
83
            ]
84
            content = json.dumps(d)
85
            with open(history_path, 'wb') as fd:
86
                fd.write(content)
87

  
88
# pytestmark = pytest.mark.update_data
89

  
90

  
91
def test_get_client_info(payfip):
92
    result = payfip.get_info_client(NUMCLI)
93
    assert result.numcli == NUMCLI
94
    assert result.libelleN2 == 'POUETPOUET'
95

  
96
NOTIF_URL = 'https://notif.payfip.example.com/'
97
REDIRECT_URL = 'https://redirect.payfip.example.com/'
98

  
99

  
100
def test_get_idop_ok(payfip):
101
    result = payfip.get_idop(
102
        numcli=NUMCLI,
103
        exer='2019',
104
        refdet='ABCDEFGH',
105
        montant='1000',
106
        mel='john.doe@example.com',
107
        objet='coucou',
108
        url_notification=NOTIF_URL,
109
        url_redirect=REDIRECT_URL,
110
        saisie='T')
111
    assert result == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103'
112

  
113

  
114
def test_get_idop_refdet_error(payfip):
115
    with pytest.raises(PayFiPError, match='.*R3.*Le format.*REFDET.*conforme'):
116
        payfip.get_idop(
117
            numcli=NUMCLI,
118
            exer='2019',
119
            refdet='ABCD',
120
            montant='1000',
121
            mel='john.doe@example.com',
122
            objet='coucou',
123
            url_notification='https://notif.payfip.example.com/',
124
            url_redirect='https://redirect.payfip.example.com/',
125
            saisie='T')
126

  
127

  
128
def test_get_idop_adresse_mel_incorrect(payfip):
129
    with pytest.raises(PayFiPError, match='.*A2.*Adresse.*incorrecte'):
130
        payfip.get_idop(
131
            numcli=NUMCLI,
132
            exer='2019',
133
            refdet='ABCDEF',
134
            montant='9990000001',
135
            mel='john.doeexample.com',
136
            objet='coucou',
137
            url_notification='https://notif.payfip.example.com/',
138
            url_redirect='https://redirect.payfip.example.com/',
139
            saisie='T')
140

  
141

  
142
def test_get_info_paiement_ok(payfip):
143
    result = payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103')
144
    assert {k: result[k] for k in result} == {
145
        'dattrans': '12122019',
146
        'exer': '20',
147
        'heurtrans': '1311',
148
        'idOp': 'cc0cb210-1cd4-11ea-8cca-0213ad91a103',
149
        'mel': 'john.doe@example.com',
150
        'montant': '1000',
151
        'numauto': '112233445566-tip',
152
        'numcli': NUMCLI,
153
        'objet': 'coucou',
154
        'refdet': 'EFEFAEFG',
155
        'resultrans': 'V',
156
        'saisie': 'T'
157
    }
158

  
159

  
160
def test_get_info_paiement_P1(payfip):
161
    # idop par pas encore reçu par la plate-forme ou déjà nettoyé (la nuit)
162
    with pytest.raises(PayFiPError, match='.*P1.*IdOp incorrect.*'):
163
        payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103')
164

  
165

  
166
def test_get_info_paiement_P5(payfip):
167
    # idop reçu par la plate-forme mais transaction en cours
168
    with pytest.raises(PayFiPError, match='.*P5.*sultat de la transaction non connu.*'):
169
        payfip.get_info_paiement('cc0cb210-1cd4-11ea-8cca-0213ad91a103')
170

  
171

  
172
def test_payment_ok(request):
173
    payment = eopayment.Payment('payfip_ws', {
174
        'numcli': '090909',
175
        'automatic_return_url': NOTIF_URL,
176
        'normal_return_url': REDIRECT_URL,
177
    })
178

  
179
    with httmock.HTTMock(PayFiPHTTMock(request).mock):
180
        payment_id, kind, url = payment.request(
181
            amount='10.00',
182
            email='john.doe@example.com',
183
            # make test deterministic
184
            refdet='201912261758460053903194')
185

  
186
        assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103'
187
        assert kind == eopayment.URL
188
        assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103'
189

  
190
        response = payment.response('idop=%s' % payment_id)
191
        assert response.result == eopayment.PAID
192
        assert response.bank_status == ''
193
        assert response.order_id == payment_id
194
        assert response.transaction_id == (
195
            '201912261758460053903194 cc0cb210-1cd4-11ea-8cca-0213ad91a103 112233445566-tip')
196

  
197

  
198
def test_payment_denied(request):
199
    payment = eopayment.Payment('payfip_ws', {
200
        'numcli': '090909',
201
        'automatic_return_url': NOTIF_URL,
202
        'normal_return_url': REDIRECT_URL,
203
    })
204

  
205
    with httmock.HTTMock(PayFiPHTTMock(request).mock):
206
        payment_id, kind, url = payment.request(
207
            amount='10.00',
208
            email='john.doe@example.com',
209
            # make test deterministic
210
            refdet='201912261758460053903194')
211

  
212
        assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103'
213
        assert kind == eopayment.URL
214
        assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103'
215

  
216
        response = payment.response('idop=%s' % payment_id)
217
        assert response.result == eopayment.DENIED
218
        assert response.bank_status == 'refused'
219
        assert response.order_id == payment_id
220
        assert response.transaction_id == '201912261758460053903194 cc0cb210-1cd4-11ea-8cca-0213ad91a103'
221

  
222

  
223
def test_payment_cancelled(request):
224
    payment = eopayment.Payment('payfip_ws', {
225
        'numcli': '090909',
226
        'automatic_return_url': NOTIF_URL,
227
        'normal_return_url': REDIRECT_URL,
228
    })
229

  
230
    with httmock.HTTMock(PayFiPHTTMock(request).mock):
231
        payment_id, kind, url = payment.request(
232
            amount='10.00',
233
            email='john.doe@example.com',
234
            # make test deterministic
235
            refdet='201912261758460053903194')
236

  
237
        assert payment_id == 'cc0cb210-1cd4-11ea-8cca-0213ad91a103'
238
        assert kind == eopayment.URL
239
        assert url == 'https://www.tipi.budget.gouv.fr/tpa/paiementws.web?idop=cc0cb210-1cd4-11ea-8cca-0213ad91a103'
240

  
241
        response = payment.response('idop=%s' % payment_id)
242
        assert response.result == eopayment.CANCELLED
243
        assert response.bank_status == 'cancelled'
244
        assert response.order_id == payment_id
245
        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
-