python-redsys 1.2.1__py3-none-any.whl → 1.2.2__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.
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-redsys
3
+ Version: 1.2.2
4
+ Summary: A simple, lightweight client to handle payments through the RedSys platform.
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.1,<4.0.0)
23
+ Project-URL: Homepage, https://github.com/ddiazpinto/python-redsys
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Welcome to python-redsys!
27
+
28
+ [![CI](https://github.com/ddiazpinto/python-redsys/actions/workflows/python-app.yml/badge.svg?branch=release/1.x)](https://github.com/ddiazpinto/python-redsys/actions/workflows/python-app.yml?query=branch%3Arelease%2F1.x) [![PyPI](https://img.shields.io/pypi/v/python-redsys.svg)](https://pypi.org/project/python-redsys/) ![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue.svg) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ddiazpinto/python-redsys/blob/release/1.x/LICENSE)
29
+
30
+ A simple, lightweight client to handle payments through the RedSys platform, using its *redirect connection* (browser form POST).
31
+
32
+ The library only builds the signed request and verifies/parses the response. It makes no network or browser calls of its own: the payment form is submitted from the cardholder's browser.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install "python-redsys>=1.2,<2"
38
+
39
+ # or with Poetry
40
+ poetry add "python-redsys@^1.2"
41
+ ```
42
+
43
+ Requires Python 3.8+. Runtime dependency: `pycryptodome`.
44
+
45
+ ## Usage
46
+
47
+ The client prepares a request from a dict of parameters and verifies the response. Parameter values are validated on construction (`ValueError` on an unknown parameter or an invalid value, `TypeError` when `amount` is not a `Decimal`); `order` has 12 characters (4 digits followed by 8 alphanumerics), and `amount` is a `Decimal`, serialized to integer minor units on the wire. The allowed values for `currency`, `transaction_type` and `consumer_language` are in `redsys.constants`.
48
+
49
+ ## Using *redirect connection*
50
+
51
+ ### 1. Build a signed request
52
+
53
+ ```python
54
+ from decimal import Decimal, ROUND_HALF_UP
55
+ from redsys.client import RedirectClient
56
+ from redsys.constants import EUR, STANDARD_PAYMENT
57
+
58
+ client = RedirectClient(secret_key="sq7HjrUOBfKmC576ILgskD5srU870gJ7")
59
+
60
+ post_parameters = client.prepare_request({
61
+ "merchant_code": "999008881",
62
+ "terminal": "1",
63
+ "transaction_type": STANDARD_PAYMENT,
64
+ "currency": EUR,
65
+ "order": "000000000001",
66
+ # The amount is a Decimal; pre-quantize to two decimals.
67
+ "amount": Decimal("10.56489").quantize(Decimal(".01"), ROUND_HALF_UP),
68
+ "merchant_url": "https://example.com/redsys/response",
69
+ "url_ok": "https://example.com/ok",
70
+ "url_ko": "https://example.com/ko",
71
+ "merchant_name": "Example Commerce",
72
+ "product_description": "Products of Example Commerce",
73
+ }) # Ds_SignatureVersion / Ds_MerchantParameters / Ds_Signature
74
+ ```
75
+
76
+ The secret key, merchant code `999008881` and terminal `1` are the RedSys public test merchant's. `Ds_MerchantParameters` and `Ds_Signature` are returned as `bytes`; decode them before rendering them in an HTML form.
77
+
78
+ Bizum: add `"payment_method": "z"` (and optionally `"mobile_number"`) to the parameters.
79
+
80
+ ### 2. Communication step
81
+
82
+ Auto-submit an HTML form (`method="POST"`) with the three `post_parameters` fields to the RedSys endpoint:
83
+
84
+ - Test: `https://sis-t.redsys.es:25443/sis/realizarPago`
85
+ - Production: `https://sis.redsys.es/sis/realizarPago`
86
+
87
+ RedSys notifies the result both as a server-to-server POST to `merchant_url` and as a browser redirect to `url_ok` / `url_ko`.
88
+
89
+ ### 3. Verify and read the response
90
+
91
+ `create_response()` takes the received `Ds_Signature` and `Ds_MerchantParameters` and raises `ValueError` on signature mismatch.
92
+
93
+ ```python
94
+ response = client.create_response(signature, merchant_parameters) # as RedSys posted them back
95
+
96
+ if response.is_paid:
97
+ ... # actions after a successful payment
98
+ else:
99
+ raise Exception(f"declined with code {response.code}")
100
+ ```
101
+
102
+ Response predicates (per the RedSys documentation):
103
+
104
+ - `response.is_paid`: response code between 0 and 99.
105
+ - `response.is_canceled`: response code 400.
106
+ - `response.is_refunded`: response code 900.
107
+ - `response.is_authorized`: paid, refunded or canceled.
108
+
109
+ `response.code` is the RedSys code as an `int`, and `response.message` its Spanish description (`None` for a code without one). Decoded fields (`response.order`, `response.amount` as a `Decimal`, ...) read as attributes, `None` when absent; `response.parameters` holds every decoded parameter: listed ones under their attribute name (`amount`), unlisted ones under their original `Ds_*` key.
110
+
111
+ ## Signature version
112
+
113
+ The 1.x line signs with `HMAC_SHA256_V1` (3DES + HMAC-SHA256), the legacy scheme still accepted by RedSys. `HMAC_SHA512_V1` requires 2.x.
114
+
115
+ ## Support
116
+
117
+ The 1.x line receives fixes until 2026-12-31.
118
+
119
+ ## Contributing
120
+
121
+ Contributions that keep the *lightweight* philosophy are welcome.
122
+
@@ -1,13 +1,13 @@
1
- redsys/__init__.py,sha256=kRuS8epm885TJGvdrjNc9xwT70glq_h9x71_bUgT68s,138
1
+ redsys/__init__.py,sha256=k234cuEigYkeydQvG5NyAl4pPAyCQSjL65UvOkeTak8,138
2
2
  redsys/client.py,sha256=ervLX4FbzDfWjh_SO2SFyY9IT0PZ5aIcVZdYy4mUO7Q,3937
3
3
  redsys/constants.py,sha256=lJYgbmikxh0b9iVM4M5XFWBWVvdFY27OkcXSLluHSsQ,2497
4
4
  redsys/request.py,sha256=IEzlRi34JgDBRRyaBHtIrAZ38fADL977RdBFaeWDdbY,5304
5
- redsys/response.py,sha256=leDhlDlnVLGeruxlN2_3DQ-V56xAbIiFkvoqYDmz9S0,5430
5
+ redsys/response.py,sha256=YDihLSIE388Ib53qr9PUtGsWqLqj1zFdjbgn9x_i-8A,5585
6
6
  redsys/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  redsys/tests/test_client.py,sha256=oND8W9y6O6MkDLNxnBpd8UDShwKWoAW3SgKmWDOXHrw,3858
8
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,,
9
+ redsys/tests/test_response.py,sha256=K1QP7eRyBum88LXG6IOQtno8FZNxoVHUv-GJU83k5OI,1777
10
+ python_redsys-1.2.2.dist-info/METADATA,sha256=PXqNlioj4tNFrfUXhiPZ1P77pgIJne39Q_oD4sXto4U,5517
11
+ python_redsys-1.2.2.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
12
+ python_redsys-1.2.2.dist-info/licenses/LICENSE,sha256=Ku6mJcUOcHd2aZ4o2Jh4Tjkx1oZT-iT0VCyUNvm2IXU,1257
13
+ python_redsys-1.2.2.dist-info/RECORD,,
redsys/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "1.2.1"
1
+ __version__ = "1.2.2"
2
2
  __version_info__ = tuple(int(num) if num.isdigit() else num for num in __version__.replace("-", ".", 1).split("."))
redsys/response.py CHANGED
@@ -135,7 +135,10 @@ class Response:
135
135
 
136
136
  @property
137
137
  def message(self):
138
- return RESPONSE_MAP["0000"] if self.is_paid else RESPONSE_MAP[self.response]
138
+ if self.is_paid:
139
+ return RESPONSE_MAP["0000"]
140
+ # RedSys sends 4-digit codes ("0190"); the map keys drop the leading zeros ("190").
141
+ return RESPONSE_MAP.get(self.response) or RESPONSE_MAP.get(str(self.code))
139
142
 
140
143
  @property
141
144
  def parameters(self):
@@ -30,3 +30,10 @@ class TestResponse:
30
30
  assert "amount" not in second.parameters
31
31
  assert second.amount is None
32
32
  assert first.amount == Decimal("1.00")
33
+
34
+ def test_message_for_four_digit_codes(self):
35
+ assert Response({"Ds_Response": "0190"}).message == "Denegación del emisor sin especificar motivo"
36
+ assert Response({"Ds_Response": "0900"}).message == "Transacción autorizada para devoluciones y confirmaciones"
37
+ assert Response({"Ds_Response": "0400"}).message == "Transacción autorizada para anulaciones"
38
+ assert Response({"Ds_Response": "9915"}).message == "A petición del usuario se ha cancelado el pago"
39
+ assert Response({"Ds_Response": "0105"}).message is None
@@ -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
-