Projet

Général

Profil

0001-paybox-implement-transaction-validation-and-cancelli.patch

Serghei Mihai (congés, retour 15/05), 12 octobre 2018 11:29

Télécharger (8,67 ko)

Voir les différences:

Subject: [PATCH] paybox: implement transaction validation and cancelling
 (#26960)

 eopayment/paybox.py  | 46 +++++++++++++++++++++++++++
 tests/test_paybox.py | 75 ++++++++++++++++++++++++++++++++++++++++++++
 tox.ini              |  1 +
 3 files changed, 122 insertions(+)
eopayment/paybox.py
6 6
import logging
7 7
import hashlib
8 8
import hmac
9
import requests
9 10
from decimal import Decimal, ROUND_DOWN
10 11
from Crypto.Signature import PKCS1_v1_5
11 12
from Crypto.PublicKey import RSA
......
99 100
        'https://tpeweb1.paybox.com/cgi/MYchoix_pagepaiement.cgi',
100 101
}
101 102

  
103
PAYBOX_DIRECT_URLS = {
104
    'test': 'https://preprod-ppps.paybox.com/PPPS.php',
105
    'prod': 'https://ppps.paybox.com/PPPS.php',
106
    'backup': 'https://ppps1.paybox.com/PPPS.php'
107
}
108

  
109
PAYBOX_DIRECT_CANCEL_OPERATION = '00005'
110
PAYBOX_DIRECT_VALIDATE_OPERATION = '00002'
111

  
102 112

  
103 113
def sign(data, key):
104 114
    '''Take a list of tuple key, value and sign it by building a string to
......
174 184
                'validation': lambda x: isinstance(x, basestring) and
175 185
                x.isdigit() and len(x) == 7,
176 186
            },
187
            {
188
                'name': 'cle',
189
                'caption': _('Site key'),
190
                'required': False,
191
                'validation': lambda x: isinstance(x, basestring),
192
            },
177 193
            {
178 194
                'name': 'rang',
179 195
                'caption': _('Numéro de rang'),
......
311 327
            bank_data=d,
312 328
            result=result,
313 329
            bank_status=bank_status)
330

  
331
    def perform(self, amount, bank_data, operation):
332
        logger = logging.getLogger(__name__)
333
        url = PAYBOX_DIRECT_URLS[self.platform]
334
        params = {'VERSION': '00104', # protocol version
335
                  'TYPE': operation,
336
                  'SITE': force_text(self.site),
337
                  'RANG': self.rang.strip(),
338
                  'CLE': force_text(self.cle),
339
                  'NUMQUESTION': bank_data['numero_transaction'][0].zfill(10),
340
                  'MONTANT': (amount * Decimal(100)).to_integral_value(ROUND_DOWN),
341
                  'DEVISE': force_text(self.devise),
342
                  'NUMTRANS': bank_data['numero_transaction'][0], # paybox transaction number
343
                  'NUMAPPEL': bank_data['numero_appel'][0],
344
                  'REFERENCE': bank_data['reference'][0],
345
                  'DATEQ': datetime.datetime.now().strftime('%d%m%Y%H%M%S'),
346
        }
347
        response = requests.post(url, params)
348
        response.raise_for_status()
349
        logger.debug('received %r', response.content)
350
        data = dict(urlparse.parse_qsl(response.content, True, True))
351
        if data.get('CODEREPONSE') != '00000':
352
            raise ResponseError(data.get('COMMENTAIRE'))
353
        return data
354

  
355
    def validate(self, amount, bank_data, **kwargs):
356
        return self.perform(amount, bank_data, PAYBOX_DIRECT_VALIDATE_OPERATION)
357

  
358
    def cancel(self, amount, bank_data, **kwargs):
359
        return self.perform(amount, bank_data, PAYBOX_DIRECT_CANCEL_OPERATION)
tests/test_paybox.py
4 4
from unittest import TestCase
5 5
from decimal import Decimal
6 6
import base64
7
import mock
7 8
from six.moves.urllib import parse as urllib
8 9
from xml.etree import ElementTree as ET
9 10

  
......
116 117
        with self.assertRaisesRegexp(eopayment.ResponseError, 'missing erreur or reference'):
117 118
            backend.response('foo=bar')
118 119

  
120
    def test_perform_operations(self):
121
        operations = {'validate': '00002', 'cancel': '00005'}
122
        for operation_name, operation_code in operations.items():
123
            params = BACKEND_PARAMS.copy()
124
            params['cle'] = 'cancelling_key'
125
            backend = eopayment.Payment('paybox', params)
126
            bank_data = {'numero_transaction': ['13957441'],
127
                         'numero_appel': ['30310733'],
128
                         'reference': ['830657461681']
129
            }
130
            backend_raw_response = """NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR="""
131
            backend_expected_response = {"CODEREPONSE": "00000",
132
                                         "RANG": "32",
133
                                         "AUTORISATION": "XXXXXX",
134
                                         "NUMTRANS": "0013989865",
135
                                         "PORTEUR": "",
136
                                         "COMMENTAIRE": "Demande traitée avec succès",
137
                                         "SITE": "1999888",
138
                                         "NUMAPPEL": "0030378572",
139
                                         "REFABONNE": "",
140
                                         "NUMQUESTION": "0013989862"}
141

  
142
            with mock.patch('eopayment.paybox.requests.post') as requests_post:
143
                response = mock.Mock(status_code=200, content=backend_raw_response)
144
                requests_post.return_value = response
145
                backend_response = getattr(backend, operation_name)(Decimal('10'), bank_data)
146
                self.assertEqual(requests_post.call_args[0][0], 'https://preprod-ppps.paybox.com/PPPS.php')
147
                params_sent = requests_post.call_args[0][1]
148
                # make sure the date parameter is present
149
                assert 'DATEQ' in params_sent
150
                # don't care about its value
151
                params_sent.pop('DATEQ')
152
                expected_params = {'CLE': 'cancelling_key',
153
                                   'VERSION': '00104',
154
                                   'TYPE': operation_code,
155
                                   'MONTANT': Decimal('1000'),
156
                                   'NUMAPPEL': '30310733',
157
                                   'NUMTRANS': '13957441',
158
                                   'NUMQUESTION': '0013957441',
159
                                   'REFERENCE': '830657461681',
160
                                   'RANG': backend.backend.rang,
161
                                   'SITE': backend.backend.site,
162
                                   'DEVISE': backend.backend.devise
163
                }
164
                self.assertEqual(params_sent, expected_params)
165
                self.assertEqual(backend_response, backend_expected_response)
166

  
167
                params['platform'] = 'prod'
168
                backend = eopayment.Payment('paybox', params)
169
                with mock.patch('eopayment.paybox.requests.post') as requests_post:
170
                    response = mock.Mock(status_code=200, content=backend_raw_response)
171
                    requests_post.return_value = response
172
                    getattr(backend, operation_name)(Decimal('10'), bank_data)
173
                    self.assertEqual(requests_post.call_args[0][0], 'https://ppps.paybox.com/PPPS.php')
174

  
175
                with mock.patch('eopayment.paybox.requests.post') as requests_post:
176
                    error_response = """CODEREPONSE=00015&COMMENTAIRE=PAYBOX : Transaction non trouvée"""
177
                    response = mock.Mock(status_code=200, content=error_response)
178
                    requests_post.return_value = response
179
                    self.assertRaisesRegexp(eopayment.ResponseError, 'Transaction non trouvée', getattr(backend, operation_name),
180
                                            Decimal('10'), bank_data)
181

  
182

  
183
    def test_validate_payment(self):
184
        params = BACKEND_PARAMS.copy()
185
        params['cle'] = 'cancelling_key'
186
        backend = eopayment.Payment('paybox', params)
187
        bank_data = {'numero_transaction': ['13957441'],
188
                     'numero_appel': ['30310733'],
189
                     'reference': ['830657461681']
190
        }
191
        backend_raw_response = """NUMTRANS=0013989865&NUMAPPEL=0030378572&NUMQUESTION=0013989862&SITE=1999888&RANG=32&AUTORISATION=XXXXXX&CODEREPONSE=00000&COMMENTAIRE=Demande traitée avec succès&REFABONNE=&PORTEUR="""
192

  
193

  
119 194
    def test_rsa_signature_validation(self):
120 195
        pkey = '''-----BEGIN PUBLIC KEY-----
121 196
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUgYufHuheMztK1LhQSG6xsOzb
tox.ini
15 15
deps = coverage
16 16
  pytest
17 17
  pytest-cov
18
  mock
18
-