summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerghei Mihai <smihai@entrouvert.com>2018-10-09 14:43:41 (GMT)
committerSerghei Mihai <smihai@entrouvert.com>2018-10-12 09:50:35 (GMT)
commit18eef0578f055ebc3fb862481fcbe5d7b66e3356 (patch)
treee268736c3d5dee6cb050337f5abc503f7dfef9ec
parent383109078d67ed91a6c84dc3e658dd6cd9bada5b (diff)
downloadeopayment-18eef0578f055ebc3fb862481fcbe5d7b66e3356.zip
eopayment-18eef0578f055ebc3fb862481fcbe5d7b66e3356.tar.gz
eopayment-18eef0578f055ebc3fb862481fcbe5d7b66e3356.tar.bz2
paybox: implement transaction validation and cancelling (#26960)
-rw-r--r--eopayment/paybox.py50
-rw-r--r--tests/test_paybox.py75
-rw-r--r--tox.ini1
3 files changed, 126 insertions, 0 deletions
diff --git a/eopayment/paybox.py b/eopayment/paybox.py
index 705f1fe..d5cd6ce 100644
--- a/eopayment/paybox.py
+++ b/eopayment/paybox.py
@@ -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
@@ -175,6 +189,12 @@ class Payment(PaymentCommon):
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'),
'required': True,
@@ -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)
diff --git a/tests/test_paybox.py b/tests/test_paybox.py
index 93d3c32..a0b8d9a 100644
--- a/tests/test_paybox.py
+++ b/tests/test_paybox.py
@@ -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
diff --git a/tox.ini b/tox.ini
index 7a7f70e..676a49b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -17,3 +17,4 @@ usedevelop = True
deps = coverage
pytest
py2: pytest-cov
+ mock