python-redsys 1.2.1__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,140 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: python-redsys
3
- Version: 1.2.1
4
- Summary: A simple, clean and less dependant client to handle payments through Redsys.
5
- License: MIT
6
- License-File: LICENSE
7
- Author: David Díaz
8
- Author-email: d.diazp@gmail.com
9
- Requires-Python: >=3.8,<4.0
10
- Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Programming Language :: Python :: 3.14
21
- Classifier: Programming Language :: Python :: 3.15
22
- Requires-Dist: pycryptodome (>=3.19.0,<4.0.0)
23
- Project-URL: Homepage, https://github.com/ddiazpinto/python-redsys
24
- Description-Content-Type: text/markdown
25
-
26
-
27
- [![PyPI version](https://badge.fury.io/py/python-redsys.svg)](https://badge.fury.io/py/python-redsys)
28
-
29
- # Welcome to python-redsys!
30
-
31
- A simple, clean and less dependant client to handle payments through the
32
- Redsys platform using one of the available methods: _redirect connection_ or (secure method).
33
-
34
- The purpose of this library is to provide a normalized interface between
35
- Redsys and other Python applications.
36
-
37
- **About RedirectClient**
38
-
39
- Although _redirect connection_ depends on a webserver to resolve the
40
- communication step, the RedirectClient provided in this library does not
41
- assume any kind of procedure to resolve that step; it merely prepares
42
- the necessary parameters to make a request and handles the corresponding
43
- response parameters. That's what less dependant means.
44
-
45
- ## Example using _redirect connection_
46
-
47
- ### 0. Install python-redsys
48
-
49
- You can add python-redsys to your project with pip:
50
- > pip install python-redsys
51
-
52
- Or with poetry:
53
- > poetry add python-redsys
54
-
55
- ### 1. Instantiate the redirect client
56
-
57
- ```python
58
- from decimal import Decimal as D, ROUND_HALF_UP
59
- from redsys.constants import EUR, STANDARD_PAYMENT
60
- from redsys.client import RedirectClient
61
-
62
- secret_key = "123456789abcdef"
63
- client = RedirectClient(secret_key)
64
- ```
65
-
66
- ### 2. Set up the request parameters
67
-
68
- ```python
69
- parameters = {
70
- "merchant_code": "100000001",
71
- "terminal": "1",
72
- "transaction_type": STANDARD_PAYMENT,
73
- "currency": EUR,
74
- "order": "000000001",
75
- "amount": D("10.56489").quantize(D(".01"), ROUND_HALF_UP),
76
- "merchant_data": "test merchant data",
77
- "merchant_name": "Example Commerce",
78
- "titular": "Example Ltd.",
79
- "product_description": "Products of Example Commerce",
80
- "merchant_url": "https://example.com/redsys/response",
81
- }
82
- ```
83
-
84
- ### 3. Prepare the request
85
-
86
- This method returns a dict with the necessary post parameters that are
87
- needed during the communication step.
88
-
89
- ```python
90
- args = client.prepare_request(parameters)
91
- ```
92
-
93
- ### 4. Communication step
94
-
95
- Redirect the _user-agent_ to the corresponding Redsys' endpoint using
96
- the post parameters given in the previous step.
97
-
98
- After the payment process is finished, Redsys will respond making a
99
- request to the `merchant_url` defined in step 2.
100
-
101
- ### 5. Create and check the response
102
-
103
- Create the response object using the received parameters from Redsys.
104
- The method `create_response()` throws a `ValueError` in case the
105
- received signature is not equal to the calculated one using the
106
- given `merchant_parameters`. This normally means that the response **is
107
- not coming from Redsys** or that it **has been compromised**.
108
-
109
- ```python
110
- signature = "YqFenHc2HpB273l8c995...."
111
- merchant_parameters = "AndvIh66VZdkC5TG3nYL5j4XfCnFFbo3VkOu9TAeTs58fxddgc..."
112
- response = client.create_response(signature, merchant_parameters)
113
- if response.is_paid:
114
- # Do the corresponding actions after a successful payment
115
- else:
116
- # Do the corresponding actions after a failed payment
117
- raise Exception(response.code, response.message)
118
- ```
119
-
120
- **Methods for checking the response:**
121
-
122
- According to the Redsys documentation:
123
-
124
- - `response.is_paid`: Returns `True` if the response code is
125
- between 0 and 99 (both included).
126
- - `response.is_canceled`: Returns `True` if the response code
127
- is 400.
128
- - `response.is_refunded`: Returns `True` if the response code
129
- is 900.
130
- - `response.is_authorized`: Returns `True` if the response is
131
- **paid**, **refunded** or **canceled**.
132
-
133
- Also, you can directly access the code or the message defined in Redsys
134
- documentation using `response.code` or `response.message`.
135
-
136
- ## Contributions
137
-
138
- Please, feel free to send any contribution that maintains the _less
139
- dependant_ philosophy.
140
-
@@ -1,13 +0,0 @@
1
- redsys/__init__.py,sha256=kRuS8epm885TJGvdrjNc9xwT70glq_h9x71_bUgT68s,138
2
- redsys/client.py,sha256=ervLX4FbzDfWjh_SO2SFyY9IT0PZ5aIcVZdYy4mUO7Q,3937
3
- redsys/constants.py,sha256=lJYgbmikxh0b9iVM4M5XFWBWVvdFY27OkcXSLluHSsQ,2497
4
- redsys/request.py,sha256=IEzlRi34JgDBRRyaBHtIrAZ38fADL977RdBFaeWDdbY,5304
5
- redsys/response.py,sha256=leDhlDlnVLGeruxlN2_3DQ-V56xAbIiFkvoqYDmz9S0,5430
6
- redsys/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- redsys/tests/test_client.py,sha256=oND8W9y6O6MkDLNxnBpd8UDShwKWoAW3SgKmWDOXHrw,3858
8
- redsys/tests/test_request.py,sha256=FONigjMNdaug2DwillYKCLPK-gzIYZSVUZmcm9QkCms,3110
9
- redsys/tests/test_response.py,sha256=V8WdZ1oR4Ed5ZKmi2R-Hpgnr3AyYZy-jFH1yn2S0lA4,1220
10
- python_redsys-1.2.1.dist-info/METADATA,sha256=Wn6Z7SG3HtRu0PIaxBwEzsDomMFzxO_BH9M4NpFu-uA,4631
11
- python_redsys-1.2.1.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
12
- python_redsys-1.2.1.dist-info/licenses/LICENSE,sha256=Ku6mJcUOcHd2aZ4o2Jh4Tjkx1oZT-iT0VCyUNvm2IXU,1257
13
- python_redsys-1.2.1.dist-info/RECORD,,
@@ -1,25 +0,0 @@
1
- MIT License
2
-
3
- The original development of python-redsys is copyrighted to David Díaz.
4
- Copyright (c) 2016 David Díaz
5
-
6
- Extensions to python-redsys are copyrighted to Andrés Reverón Molina.
7
- Copyright (c) 2021 Andrés Reverón Molina
8
-
9
- Permission is hereby granted, free of charge, to any person obtaining a copy
10
- of this software and associated documentation files (the "Software"), to deal
11
- in the Software without restriction, including without limitation the rights
12
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- copies of the Software, and to permit persons to whom the Software is
14
- furnished to do so, subject to the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be included in all
17
- copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
- SOFTWARE.
redsys/client.py DELETED
@@ -1,105 +0,0 @@
1
- import base64
2
- import hashlib
3
- import hmac
4
- import json
5
- import re
6
- from abc import ABC
7
- from abc import abstractmethod
8
- from typing import Any
9
- from typing import Dict
10
-
11
- from Crypto.Cipher import DES3
12
-
13
- from redsys.request import Request
14
- from redsys.response import Response
15
-
16
- # Calculated parameters
17
- SIGNATURE_VERSION = "Ds_SignatureVersion"
18
- MERCHANT_PARAMETERS = "Ds_MerchantParameters"
19
- SIGNATURE = "Ds_Signature"
20
- DEFAULT_SIGNATURE_VERSION = "HMAC_SHA256_V1"
21
-
22
-
23
- class Client(ABC):
24
- """
25
- Abstract class from which RedirectClient inherits.
26
- It implements methods that may be used in future clients(i.e. rest).
27
- """
28
-
29
- def __init__(self, secret_key: str):
30
- self.secret_key: bytes = secret_key.encode()
31
-
32
- @abstractmethod
33
- def create_response(self, signature, parameters):
34
- raise NotImplementedError
35
-
36
- @abstractmethod
37
- def prepare_request(self, request):
38
- raise NotImplementedError
39
-
40
- @staticmethod
41
- def encode_parameters(parameters: Dict[str, Any]) -> bytes:
42
- """Encodes the merchant parameters in base64"""
43
- return base64.b64encode(json.dumps(parameters).encode())
44
-
45
- @staticmethod
46
- def decode_parameters(parameters: bytes) -> Dict[str, Any]:
47
- """Decodes the merchant parameters from base64"""
48
- return json.loads(base64.b64decode(parameters).decode())
49
-
50
- @staticmethod
51
- def sign_hmac256(encrypted_order: bytes, merchant_parameters: bytes) -> bytes:
52
- """
53
- Generates the encrypted signature using the 3DES-encrypted order
54
- and base64-encoded merchant parameters
55
- """
56
- signature = hmac.new(encrypted_order, merchant_parameters, hashlib.sha256).digest()
57
- return base64.b64encode(signature)
58
-
59
- def encrypt_3DES(self, order: str) -> bytes:
60
- """Encrypts(3DES algorithm) the payment order using the secret key"""
61
- cipher = DES3.new(base64.b64decode(self.secret_key), DES3.MODE_CBC, IV=b"\0\0\0\0\0\0\0\0")
62
- # the cipher needs to be passed 16 bytes,
63
- # so "order" must be 16 bytes long,
64
- # therefore we left-justify adding ceros
65
- return cipher.encrypt(order.encode().ljust(16, b"\0"))
66
-
67
- def generate_signature(self, order: str, merchant_parameters: bytes) -> bytes:
68
- return self.sign_hmac256(self.encrypt_3DES(order), merchant_parameters)
69
-
70
-
71
- class RedirectClient(Client):
72
- def prepare_request(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
73
- """Takes the merchant parameters and returns the necessary parameters
74
- to make the POST request to Redsys"""
75
- request = Request(parameters)
76
- merchant_parameters = self.encode_parameters(request.prepare_parameters())
77
- signature = self.generate_signature(request.order, merchant_parameters)
78
- return {
79
- SIGNATURE_VERSION: DEFAULT_SIGNATURE_VERSION,
80
- MERCHANT_PARAMETERS: merchant_parameters,
81
- SIGNATURE: signature,
82
- }
83
-
84
- def create_response(
85
- self,
86
- signature: str,
87
- merchant_parameters: str,
88
- ) -> Response:
89
- """
90
- Decodes the Redsys response to check for validity.
91
- Checks if the received signature corresponds to the sent signature.
92
-
93
- Both the `signature` and `merchant parameters` are plain strings, not bytes.
94
- """
95
- decoded_parameters = self.decode_parameters(merchant_parameters.encode())
96
- response = Response(decoded_parameters)
97
- calculated_signature = self.generate_signature(response.order, merchant_parameters.encode())
98
- # Remove any non-alphanumeric characters from the signature
99
- not_alphanumeric = re.compile("[^a-zA-Z0-9]")
100
- safe_signature = re.sub(not_alphanumeric, "", signature)
101
-
102
- safe_calculated_signature = re.sub(not_alphanumeric, "", calculated_signature.decode())
103
- if safe_signature != safe_calculated_signature:
104
- raise ValueError("The provided signature is not valid.")
105
- return response
redsys/constants.py DELETED
@@ -1,113 +0,0 @@
1
- # Currencies
2
- """ Euro """
3
- EUR = 978
4
- """ United States dollar """
5
- USD = 840
6
- """ Pound sterling """
7
- GBP = 826
8
- """ Japanese yen """
9
- JPY = 392
10
- """ Argentine peso """
11
- ARS = 32
12
- """ Canadian dollar """
13
- CAD = 124
14
- """ Chilean Peso """
15
- CLP = 152
16
- """ Colombian peso """
17
- COP = 170
18
- """ Indian rupee """
19
- INR = 356
20
- """ Mexican peso """
21
- MXN = 484
22
- """ Peruvian Sol """
23
- PEN = 604
24
- """ Swiss franc """
25
- CHF = 756
26
- """ Brazilian real """
27
- BRL = 986
28
- """ Venezuelan bolivar """
29
- VEF = 937
30
- """ Turkish lira """
31
- TRY = 949
32
-
33
- CURRENCIES = [EUR, USD, GBP, JPY, ARS, CAD, CLP, COP, INR, MXN, PEN, CHF, BRL, VEF, TRY]
34
-
35
- # Languages
36
- CUSTOMER = "000"
37
- SPANISH = "001"
38
- ENGLISH = "002"
39
- CATALAN = "003"
40
- FRENCH = "004"
41
- GERMAN = "005"
42
- DUTCH = "006"
43
- ITALIAN = "007"
44
- SWEDISH = "008"
45
- PORTUGUESE = "009"
46
- VALENCIAN = "010"
47
- POLISH = "011"
48
- GALICIAN = "012"
49
- EUSKERA = "013"
50
-
51
- LANGUAGES = [
52
- CUSTOMER,
53
- SPANISH,
54
- ENGLISH,
55
- CATALAN,
56
- FRENCH,
57
- GERMAN,
58
- DUTCH,
59
- ITALIAN,
60
- SWEDISH,
61
- PORTUGUESE,
62
- VALENCIAN,
63
- POLISH,
64
- GALICIAN,
65
- EUSKERA,
66
- ]
67
-
68
- # Transaction types
69
- """ Standard payment """
70
- STANDARD_PAYMENT = "0"
71
- """ Pre-authorization """
72
- PREAUTHORIZATION = "1"
73
- """ Confirmation of pre-authorization """
74
- PREAUTHORIZATION_CONFIRMATION = "2"
75
- """ Partial or total refund """
76
- REFUND = "3"
77
- """ Recurring transaction """
78
- RECURRING_TRANSACTION = "5"
79
- """ Successive transaction """
80
- SUCCESSIVE_TRANSACTION = "6"
81
- """ Authentication """
82
- AUTHENTICATION = "7"
83
- """ Confirmation of authentication """
84
- AUTHENTICATION_CONFIRMATION = "8"
85
- """ Cancellation of pre-authorization """
86
- PREAUTHORIZATION_CANCELATION = "9"
87
- """ Deferred pre-authorization """
88
- DEFERRED_PREAUTHORIZATION = "O"
89
- """ Confirmation of deferred pre-authorization """
90
- DEFERRED_PREAUTHORIZATION_CONFIRMATION = "P"
91
- """ Cancelation of deferred pre-authorization """
92
- DEFERRED_PREAUTHORIZATION_CANCELATION = "Q"
93
- """ Recurring deferred pre-authorization """
94
- RECURRING_DEFERRED_PREAUTHORIZATION = "R"
95
- """ Confirmation of recurring deferred pre-authorization and successive transaction """
96
- SUCCESSIVE_RECURRING_TRANSACTION = "S"
97
-
98
- TRANSACTIONS = [
99
- STANDARD_PAYMENT,
100
- PREAUTHORIZATION,
101
- PREAUTHORIZATION_CONFIRMATION,
102
- REFUND,
103
- RECURRING_TRANSACTION,
104
- SUCCESSIVE_TRANSACTION,
105
- AUTHENTICATION,
106
- AUTHENTICATION_CONFIRMATION,
107
- PREAUTHORIZATION_CANCELATION,
108
- DEFERRED_PREAUTHORIZATION,
109
- DEFERRED_PREAUTHORIZATION_CONFIRMATION,
110
- DEFERRED_PREAUTHORIZATION_CANCELATION,
111
- RECURRING_DEFERRED_PREAUTHORIZATION,
112
- SUCCESSIVE_RECURRING_TRANSACTION,
113
- ]
redsys/response.py DELETED
@@ -1,146 +0,0 @@
1
- from decimal import Decimal
2
- from typing import Any
3
- from typing import Dict
4
-
5
- RESPONSE = "Ds_Response"
6
- DATE = "Ds_Date"
7
- HOUR = "Ds_Hour"
8
- MERCHANT_CODE = "Ds_MerchantCode"
9
- TERMINAL = "Ds_Terminal"
10
- TRANSACTION_TYPE = "Ds_TransactionType"
11
- ORDER = "Ds_Order"
12
- CURRENCY = "Ds_Currency"
13
- AMOUNT = "Ds_Amount"
14
- MERCHANT_DATA = "Ds_MerchantData"
15
- CONSUMER_LANGUAGE = "Ds_ConsumerLanguage"
16
- CARD_COUNTRY = "Ds_Card_Country"
17
- CARD_TYPE = "Ds_Card_Type"
18
- CARD_BRAND = "Ds_Card_Brand"
19
- MERCHANT_COF_TXNID = "Ds_Merchant_Cof_Txnid"
20
- SECURE_PAYMENT = "Ds_SecurePayment"
21
- AUTHORIZATION_CODE = "Ds_AuthorisationCode"
22
- ERROR_CODE = "Ds_ErrorCode"
23
- AMOUNT_EURO = "Ds_Amount_Euro"
24
- PROCESSED_PAY_METHOD = "Ds_ProcessedPayMethod"
25
-
26
- RESPONSE_MAP = {
27
- "0000": "Transacción autorizada para pagos y preautorizaciones",
28
- "900": "Transacción autorizada para devoluciones y confirmaciones",
29
- "400": "Transacción autorizada para anulaciones",
30
- "101": "Tarjeta caducada",
31
- "102": "Tarjeta en excepción transitoria o bajo sospecha de fraude",
32
- "106": "Intentos de PIN excedidos",
33
- "125": "Tarjeta no efectiva",
34
- "129": "Código de seguridad (CVV2/CVC2) incorrecto",
35
- "180": "Tarjeta ajena al servicio",
36
- "184": "Error en la autenticación del titular",
37
- "190": "Denegación del emisor sin especificar motivo",
38
- "191": "Fecha de caducidad errónea",
39
- "202": "Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de tarjeta",
40
- "904": "Comercio no registrado en FUC",
41
- "909": "Error de sistema",
42
- "912": "Emisor no disponible",
43
- "913": "Pedido repetido",
44
- "944": "Sesión Incorrecta",
45
- "950": "Operación de devolución no permitida",
46
- "9912": "Emisor no disponible",
47
- "9064": "Número de posiciones de la tarjeta incorrecto",
48
- "9078": "Tipo de operación no permitida para esa tarjeta",
49
- "9093": "Tarjeta no existente",
50
- "9094": "Rechazo servidores internacionales",
51
- "9104": "Comercio con “titular seguro” y titular sin clave de compra segura",
52
- "9218": "El comercio no permite op. seguras por entrada /operaciones",
53
- "9253": "Tarjeta no cumple el check-digit",
54
- "9256": "El comercio no puede realizar preautorizaciones",
55
- "9257": "Esta tarjeta no permite operativa de preautorizaciones",
56
- "9261": "Operación detenida por superar el control de restricciones en la entrada al SIS",
57
- "9913": "Error en la confirmación que el comercio envía al TPV Virtual",
58
- "9914": "Confirmación “KO” del comercio",
59
- "9915": "A petición del usuario se ha cancelado el pago",
60
- "9928": "Anulación de autorización en diferido realizada por el SIS (proceso batch)",
61
- "9929": "Anulación de autorización en diferido realizada por el comercio",
62
- "9997": "Se está procesando otra transacción en SIS con la misma tarjeta",
63
- "9998": "Operación en proceso de solicitud de datos de tarjeta",
64
- "9999": "Operación que ha sido redirigida al emisor a autenticar",
65
- }
66
-
67
- MERCHANT_PARAMETERS_MAP = {
68
- "response": RESPONSE,
69
- "date": DATE,
70
- "hour": HOUR,
71
- "merchant_code": MERCHANT_CODE,
72
- "terminal": TERMINAL,
73
- "transaction_type": TRANSACTION_TYPE,
74
- "order": ORDER,
75
- "currency": CURRENCY,
76
- "amount": AMOUNT,
77
- "merchant_data": MERCHANT_DATA,
78
- "consumer_language": CONSUMER_LANGUAGE,
79
- "card_country": CARD_COUNTRY,
80
- "card_type": CARD_TYPE,
81
- "card_brand": CARD_BRAND,
82
- "merchant_cof_txnid": MERCHANT_COF_TXNID,
83
- "secure_payment": SECURE_PAYMENT,
84
- "authorization_code": AUTHORIZATION_CODE,
85
- "error_code": ERROR_CODE,
86
- "amount_euro": AMOUNT_EURO,
87
- "processed_pay_method": PROCESSED_PAY_METHOD,
88
- }
89
-
90
-
91
- class Response:
92
- """
93
- Defines a response
94
- """
95
-
96
- _parameters: Dict[str, Any]
97
-
98
- def __init__(self, parameters: Dict[str, Any]):
99
- # __setattr__ only handles merchant parameters, so the per-instance store is set directly.
100
- object.__setattr__(self, "_parameters", {})
101
- MERCHANT_PARAMETERS_MAP_REVERSE = {value: key for key, value in MERCHANT_PARAMETERS_MAP.items()}
102
- for key, value in parameters.items():
103
- reversed_parameter = MERCHANT_PARAMETERS_MAP_REVERSE.get(key, key)
104
- clean = getattr(self, "clean_%s" % reversed_parameter, None)
105
- self._parameters[reversed_parameter] = clean(value) if clean else value
106
-
107
- def __getattr__(self, item: str) -> Any:
108
- if item in MERCHANT_PARAMETERS_MAP:
109
- return self._parameters.get(item)
110
-
111
- def __setattr__(self, key: str, value: Any):
112
- if key in MERCHANT_PARAMETERS_MAP:
113
- clean = getattr(self, "clean_%s" % MERCHANT_PARAMETERS_MAP[key], None)
114
- self._parameters[MERCHANT_PARAMETERS_MAP[key]] = clean(value) if clean else value
115
-
116
- @property
117
- def is_authorized(self):
118
- return (0 <= self.code <= 99) or self.code == 900 or self.code == 400
119
-
120
- @property
121
- def is_paid(self):
122
- return 0 <= self.code <= 99
123
-
124
- @property
125
- def is_refunded(self):
126
- return self.code == 900
127
-
128
- @property
129
- def is_canceled(self):
130
- return self.code == 400
131
-
132
- @property
133
- def code(self):
134
- return int(self.response)
135
-
136
- @property
137
- def message(self):
138
- return RESPONSE_MAP["0000"] if self.is_paid else RESPONSE_MAP[self.response]
139
-
140
- @property
141
- def parameters(self):
142
- return self._parameters
143
-
144
- @staticmethod
145
- def clean_amount(value):
146
- return Decimal(f"{str(value)[:-2]}.{str(value)[-2:]}")
redsys/tests/__init__.py DELETED
File without changes
@@ -1,89 +0,0 @@
1
- from decimal import ROUND_HALF_UP
2
- from decimal import Decimal as D
3
-
4
- import pytest
5
-
6
- from redsys.client import RedirectClient
7
- from redsys.constants import EUR
8
- from redsys.constants import STANDARD_PAYMENT
9
- from redsys.request import Request
10
-
11
-
12
- class TestRedirectClient:
13
- @pytest.fixture(autouse=True)
14
- def set_up(self):
15
- secret_key = "sq7HjrUOBfKmC576ILgskD5srU870gJ7"
16
- self.client = RedirectClient(secret_key)
17
- self.parameters = {
18
- "merchant_code": "100000001",
19
- "terminal": "1",
20
- "transaction_type": STANDARD_PAYMENT,
21
- "currency": EUR,
22
- "order": "000000000001",
23
- "amount": D("10.56489").quantize(D(".01"), ROUND_HALF_UP),
24
- "merchant_data": "test merchant data",
25
- "merchant_name": "Example Commerce",
26
- "titular": "Example Ltd.",
27
- "product_description": "Products of Example Commerce",
28
- "merchant_url": "https://example.com/redsys/response",
29
- }
30
- self.request = Request(self.parameters)
31
- self.merchant_params = self.request.prepare_parameters()
32
-
33
- def test_encode_parameters(self):
34
- encoded_params = self.client.encode_parameters(self.merchant_params)
35
- encoded_params_str = encoded_params.decode("UTF-8")
36
- assert encoded_params_str.endswith("ZWRzeXMvcmVzcG9uc2UifQ==")
37
- assert len(encoded_params_str) == 628
38
-
39
- def test_decode_parameters(self):
40
- encoded_params = self.client.encode_parameters(self.merchant_params)
41
- decoded_params = self.client.decode_parameters(encoded_params)
42
- assert decoded_params == self.merchant_params
43
-
44
- def test_encrypt_3DES(self):
45
- encrypted_order = self.client.encrypt_3DES(self.request.order)
46
- assert len(encrypted_order) == 16
47
- assert str(encrypted_order) == "b'/\\x17\\\\6\\n\\x1aY\\xf3\\xd7\\xa5\\x97\\xb9\\xb6\\xe0\\x8dH'"
48
-
49
- def test_sign_hmac256(self):
50
- encrypted_order = self.client.encrypt_3DES(self.request.order)
51
- encoded_params = self.client.encode_parameters(self.merchant_params)
52
- signature = self.client.sign_hmac256(encrypted_order, encoded_params)
53
- assert signature == b"a2F05DwuH5aXu5vD1IdjQ+NxaR0lPsRwksW1nr2Nzuw="
54
-
55
- def test_prepare_request(self):
56
- prepared_request = self.client.prepare_request(self.parameters)
57
- assert len(prepared_request.keys()) == 3
58
- assert prepared_request.get("Ds_Signature") == b"a2F05DwuH5aXu5vD1IdjQ+NxaR0lPsRwksW1nr2Nzuw="
59
- pass
60
-
61
- def test_create_response_valid(self):
62
- merchant_parameters = {
63
- "Ds_MerchantCode": "100000001",
64
- "Ds_Terminal": "1",
65
- "Ds_TransactionType": "0",
66
- "Ds_Currency": 978,
67
- "Ds_Order": "000000000001",
68
- "Ds_Amount": 1056,
69
- "Ds_MerchantData": "test merchant data",
70
- }
71
- encoded_params = self.client.encode_parameters(merchant_parameters)
72
- signature = self.client.generate_signature(self.request.order, encoded_params)
73
- response = self.client.create_response(signature.decode(), encoded_params.decode())
74
- assert response._parameters.get("order") == merchant_parameters.get("Ds_Order")
75
-
76
- def test_create_response_invalid(self):
77
- merchant_parameters = {
78
- "Ds_MerchantCode": "100000001",
79
- "Ds_Terminal": "1",
80
- "Ds_TransactionType": "0",
81
- "Ds_Currency": 978,
82
- "Ds_Order": "000000000001",
83
- "Ds_Amount": 1056,
84
- "Ds_MerchantData": "test merchant data",
85
- }
86
- encoded_params = self.client.encode_parameters(merchant_parameters)
87
- bad_signature = b"sadfe2r3q2fdssaf3"
88
- with pytest.raises(ValueError, match="The provided signature is not valid"):
89
- self.client.create_response(bad_signature.decode(), encoded_params.decode())