Projet

Général

Profil

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

Benjamin Dauvergne, 03 janvier 2020 15:00

Télécharger (60,8 ko)

Voir les différences:

Subject: [PATCH 1/3] 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                       | 251 +++++++++++++
 tox.ini                                       |   8 +
 20 files changed, 980 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
# 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
-