paybox: implement transaction validation and cancelling (#26960)
This commit is contained in:
parent
383109078d
commit
18eef0578f
|
@ -6,6 +6,7 @@ import datetime
|
|||
import logging
|
||||
import hashlib
|
||||
import hmac
|
||||
import requests
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
from Crypto.PublicKey import RSA
|
||||
|
@ -99,6 +100,19 @@ URLS = {
|
|||
'https://tpeweb1.paybox.com/cgi/MYchoix_pagepaiement.cgi',
|
||||
}
|
||||
|
||||
PAYBOX_DIRECT_URLS = {
|
||||
'test': 'https://preprod-ppps.paybox.com/PPPS.php',
|
||||
'prod': 'https://ppps.paybox.com/PPPS.php',
|
||||
'backup': 'https://ppps1.paybox.com/PPPS.php'
|
||||
}
|
||||
|
||||
PAYBOX_DIRECT_CANCEL_OPERATION = '00005'
|
||||
PAYBOX_DIRECT_VALIDATE_OPERATION = '00002'
|
||||
|
||||
PAYBOX_DIRECT_VERSION_NUMBER = '00104'
|
||||
|
||||
PAYBOX_DIRECT_SUCCESS_RESPONSE_CODE = '00000'
|
||||
|
||||
|
||||
def sign(data, key):
|
||||
'''Take a list of tuple key, value and sign it by building a string to
|
||||
|
@ -174,6 +188,12 @@ class Payment(PaymentCommon):
|
|||
'validation': lambda x: isinstance(x, basestring) and
|
||||
x.isdigit() and len(x) == 7,
|
||||
},
|
||||
{
|
||||
'name': 'cle',
|
||||
'caption': _('Site key'),
|
||||
'required': False,
|
||||
'validation': lambda x: isinstance(x, basestring),
|
||||
},
|
||||
{
|
||||
'name': 'rang',
|
||||
'caption': _('Numéro de rang'),
|
||||
|
@ -311,3 +331,33 @@ class Payment(PaymentCommon):
|
|||
bank_data=d,
|
||||
result=result,
|
||||
bank_status=bank_status)
|
||||
|
||||
def perform(self, amount, bank_data, operation):
|
||||
logger = logging.getLogger(__name__)
|
||||
url = PAYBOX_DIRECT_URLS[self.platform]
|
||||
params = {'VERSION': PAYBOX_DIRECT_VERSION_NUMBER,
|
||||
'TYPE': operation,
|
||||
'SITE': force_text(self.site),
|
||||
'RANG': self.rang.strip(),
|
||||
'CLE': force_text(self.cle),
|
||||
'NUMQUESTION': bank_data['numero_transaction'][0].zfill(10),
|
||||
'MONTANT': (amount * Decimal(100)).to_integral_value(ROUND_DOWN),
|
||||
'DEVISE': force_text(self.devise),
|
||||
'NUMTRANS': bank_data['numero_transaction'][0], # paybox transaction number
|
||||
'NUMAPPEL': bank_data['numero_appel'][0],
|
||||
'REFERENCE': bank_data['reference'][0],
|
||||
'DATEQ': datetime.datetime.now().strftime('%d%m%Y%H%M%S'),
|
||||
}
|
||||
response = requests.post(url, params)
|
||||
response.raise_for_status()
|
||||
logger.debug('received %r', response.content)
|
||||
data = dict(urlparse.parse_qsl(response.content, True, True))
|
||||
if data.get('CODEREPONSE') != PAYBOX_DIRECT_SUCCESS_RESPONSE_CODE:
|
||||
raise ResponseError(data.get('COMMENTAIRE'))
|
||||
return data
|
||||
|
||||
def validate(self, amount, bank_data, **kwargs):
|
||||
return self.perform(amount, bank_data, PAYBOX_DIRECT_VALIDATE_OPERATION)
|
||||
|
||||
def cancel(self, amount, bank_data, **kwargs):
|
||||
return self.perform(amount, bank_data, PAYBOX_DIRECT_CANCEL_OPERATION)
|
||||
|
|
|
@ -4,6 +4,7 @@ import codecs
|
|||
from unittest import TestCase
|
||||
from decimal import Decimal
|
||||
import base64
|
||||
import mock
|
||||
from six.moves.urllib import parse as urllib
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
@ -116,6 +117,80 @@ class PayboxTests(TestCase):
|
|||
with self.assertRaisesRegexp(eopayment.ResponseError, 'missing erreur or reference'):
|
||||
backend.response('foo=bar')
|
||||
|
||||
def test_perform_operations(self):
|
||||
operations = {'validate': '00002', 'cancel': '00005'}
|
||||
for operation_name, operation_code in operations.items():
|
||||
params = BACKEND_PARAMS.copy()
|
||||
params['cle'] = 'cancelling_key'
|
||||
backend = eopayment.Payment('paybox', params)
|
||||
bank_data = {'numero_transaction': ['13957441'],
|
||||
'numero_appel': ['30310733'],
|
||||
'reference': ['830657461681']
|
||||
}
|
||||
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="""
|
||||
backend_expected_response = {"CODEREPONSE": "00000",
|
||||
"RANG": "32",
|
||||
"AUTORISATION": "XXXXXX",
|
||||
"NUMTRANS": "0013989865",
|
||||
"PORTEUR": "",
|
||||
"COMMENTAIRE": "Demande traitée avec succès",
|
||||
"SITE": "1999888",
|
||||
"NUMAPPEL": "0030378572",
|
||||
"REFABONNE": "",
|
||||
"NUMQUESTION": "0013989862"}
|
||||
|
||||
with mock.patch('eopayment.paybox.requests.post') as requests_post:
|
||||
response = mock.Mock(status_code=200, content=backend_raw_response)
|
||||
requests_post.return_value = response
|
||||
backend_response = getattr(backend, operation_name)(Decimal('10'), bank_data)
|
||||
self.assertEqual(requests_post.call_args[0][0], 'https://preprod-ppps.paybox.com/PPPS.php')
|
||||
params_sent = requests_post.call_args[0][1]
|
||||
# make sure the date parameter is present
|
||||
assert 'DATEQ' in params_sent
|
||||
# don't care about its value
|
||||
params_sent.pop('DATEQ')
|
||||
expected_params = {'CLE': 'cancelling_key',
|
||||
'VERSION': '00104',
|
||||
'TYPE': operation_code,
|
||||
'MONTANT': Decimal('1000'),
|
||||
'NUMAPPEL': '30310733',
|
||||
'NUMTRANS': '13957441',
|
||||
'NUMQUESTION': '0013957441',
|
||||
'REFERENCE': '830657461681',
|
||||
'RANG': backend.backend.rang,
|
||||
'SITE': backend.backend.site,
|
||||
'DEVISE': backend.backend.devise
|
||||
}
|
||||
self.assertEqual(params_sent, expected_params)
|
||||
self.assertEqual(backend_response, backend_expected_response)
|
||||
|
||||
params['platform'] = 'prod'
|
||||
backend = eopayment.Payment('paybox', params)
|
||||
with mock.patch('eopayment.paybox.requests.post') as requests_post:
|
||||
response = mock.Mock(status_code=200, content=backend_raw_response)
|
||||
requests_post.return_value = response
|
||||
getattr(backend, operation_name)(Decimal('10'), bank_data)
|
||||
self.assertEqual(requests_post.call_args[0][0], 'https://ppps.paybox.com/PPPS.php')
|
||||
|
||||
with mock.patch('eopayment.paybox.requests.post') as requests_post:
|
||||
error_response = """CODEREPONSE=00015&COMMENTAIRE=PAYBOX : Transaction non trouvée"""
|
||||
response = mock.Mock(status_code=200, content=error_response)
|
||||
requests_post.return_value = response
|
||||
self.assertRaisesRegexp(eopayment.ResponseError, 'Transaction non trouvée', getattr(backend, operation_name),
|
||||
Decimal('10'), bank_data)
|
||||
|
||||
|
||||
def test_validate_payment(self):
|
||||
params = BACKEND_PARAMS.copy()
|
||||
params['cle'] = 'cancelling_key'
|
||||
backend = eopayment.Payment('paybox', params)
|
||||
bank_data = {'numero_transaction': ['13957441'],
|
||||
'numero_appel': ['30310733'],
|
||||
'reference': ['830657461681']
|
||||
}
|
||||
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="""
|
||||
|
||||
|
||||
def test_rsa_signature_validation(self):
|
||||
pkey = '''-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUgYufHuheMztK1LhQSG6xsOzb
|
||||
|
|
Loading…
Reference in New Issue