python-redsys 1.2.0__tar.gz → 1.2.2__tar.gz

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
+
@@ -0,0 +1,96 @@
1
+ # Welcome to python-redsys!
2
+
3
+ [![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)
4
+
5
+ A simple, lightweight client to handle payments through the RedSys platform, using its *redirect connection* (browser form POST).
6
+
7
+ 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.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install "python-redsys>=1.2,<2"
13
+
14
+ # or with Poetry
15
+ poetry add "python-redsys@^1.2"
16
+ ```
17
+
18
+ Requires Python 3.8+. Runtime dependency: `pycryptodome`.
19
+
20
+ ## Usage
21
+
22
+ 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`.
23
+
24
+ ## Using *redirect connection*
25
+
26
+ ### 1. Build a signed request
27
+
28
+ ```python
29
+ from decimal import Decimal, ROUND_HALF_UP
30
+ from redsys.client import RedirectClient
31
+ from redsys.constants import EUR, STANDARD_PAYMENT
32
+
33
+ client = RedirectClient(secret_key="sq7HjrUOBfKmC576ILgskD5srU870gJ7")
34
+
35
+ post_parameters = client.prepare_request({
36
+ "merchant_code": "999008881",
37
+ "terminal": "1",
38
+ "transaction_type": STANDARD_PAYMENT,
39
+ "currency": EUR,
40
+ "order": "000000000001",
41
+ # The amount is a Decimal; pre-quantize to two decimals.
42
+ "amount": Decimal("10.56489").quantize(Decimal(".01"), ROUND_HALF_UP),
43
+ "merchant_url": "https://example.com/redsys/response",
44
+ "url_ok": "https://example.com/ok",
45
+ "url_ko": "https://example.com/ko",
46
+ "merchant_name": "Example Commerce",
47
+ "product_description": "Products of Example Commerce",
48
+ }) # Ds_SignatureVersion / Ds_MerchantParameters / Ds_Signature
49
+ ```
50
+
51
+ 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.
52
+
53
+ Bizum: add `"payment_method": "z"` (and optionally `"mobile_number"`) to the parameters.
54
+
55
+ ### 2. Communication step
56
+
57
+ Auto-submit an HTML form (`method="POST"`) with the three `post_parameters` fields to the RedSys endpoint:
58
+
59
+ - Test: `https://sis-t.redsys.es:25443/sis/realizarPago`
60
+ - Production: `https://sis.redsys.es/sis/realizarPago`
61
+
62
+ RedSys notifies the result both as a server-to-server POST to `merchant_url` and as a browser redirect to `url_ok` / `url_ko`.
63
+
64
+ ### 3. Verify and read the response
65
+
66
+ `create_response()` takes the received `Ds_Signature` and `Ds_MerchantParameters` and raises `ValueError` on signature mismatch.
67
+
68
+ ```python
69
+ response = client.create_response(signature, merchant_parameters) # as RedSys posted them back
70
+
71
+ if response.is_paid:
72
+ ... # actions after a successful payment
73
+ else:
74
+ raise Exception(f"declined with code {response.code}")
75
+ ```
76
+
77
+ Response predicates (per the RedSys documentation):
78
+
79
+ - `response.is_paid`: response code between 0 and 99.
80
+ - `response.is_canceled`: response code 400.
81
+ - `response.is_refunded`: response code 900.
82
+ - `response.is_authorized`: paid, refunded or canceled.
83
+
84
+ `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.
85
+
86
+ ## Signature version
87
+
88
+ 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.
89
+
90
+ ## Support
91
+
92
+ The 1.x line receives fixes until 2026-12-31.
93
+
94
+ ## Contributing
95
+
96
+ Contributions that keep the *lightweight* philosophy are welcome.
@@ -1,11 +1,11 @@
1
1
  [tool.poetry]
2
2
  name = "python-redsys"
3
- version = "1.2.0"
4
- description = "A simple, clean and less dependant client to handle payments through Redsys."
5
- authors = ["Andrés Reverón Molina <andres@reveronmolina.me>", "David Díaz <d.diazp@gmail.com>"]
3
+ version = "1.2.2"
4
+ description = "A simple, lightweight client to handle payments through the RedSys platform."
5
+ authors = ["David Díaz <d.diazp@gmail.com>", "Andrés Reverón Molina <andres@reveronmolina.me>"]
6
6
  license = "MIT"
7
7
  readme = "README.md"
8
- homepage = "https://github.com/systemallica/python-redsys"
8
+ homepage = "https://github.com/ddiazpinto/python-redsys"
9
9
  classifiers = [
10
10
  'Intended Audience :: Developers',
11
11
  'License :: OSI Approved :: MIT License',
@@ -17,14 +17,13 @@ packages = [
17
17
 
18
18
  [tool.poetry.dependencies]
19
19
  python = "^3.8"
20
- pycryptodome = "^3.19.0"
20
+ pycryptodome = "^3.19.1"
21
21
 
22
22
  [tool.poetry.dev-dependencies]
23
23
  pytest = "^7.4.2"
24
24
  pre-commit = "^3.5.0"
25
25
  pytest-cov = "^4.1.0"
26
26
  mypy = "^1.6.1"
27
- python-semantic-release = "^7.34.6"
28
27
  ruff = "^0.1.3"
29
28
 
30
29
  [tool.ruff]
@@ -40,15 +39,6 @@ indent-style = "space"
40
39
  [tool.ruff.isort]
41
40
  force-single-line = true
42
41
 
43
- [tool.semantic_release]
44
- version_variable = [
45
- "redsys/__init__.py:__version__",
46
- ]
47
- version_toml = ["pyproject.toml:tool.poetry.version"]
48
- upload_to_pypi = true
49
- upload_to_release = true
50
- build_command = "poetry build"
51
-
52
42
  [build-system]
53
43
  requires = ["poetry-core>=1.0.0"]
54
44
  build-backend = "poetry.core.masonry.api"
@@ -1,2 +1,2 @@
1
- __version__ = "1.2.0"
1
+ __version__ = "1.2.2"
2
2
  __version_info__ = tuple(int(num) if num.isdigit() else num for num in __version__.replace("-", ".", 1).split("."))
@@ -77,9 +77,11 @@ class Request:
77
77
  their values according to the platform specifications
78
78
  """
79
79
 
80
- _parameters: Dict[str, Any] = {}
80
+ _parameters: Dict[str, Any]
81
81
 
82
82
  def __init__(self, parameters: Dict[str, Any]) -> None:
83
+ # __setattr__ only handles merchant parameters, so the per-instance store is set directly.
84
+ object.__setattr__(self, "_parameters", {})
83
85
  for key, value in parameters.items():
84
86
  if key in MERCHANT_PARAMETERS_MAP:
85
87
  if check := getattr(self, f"check_{str(key)}", None):
@@ -90,7 +92,7 @@ class Request:
90
92
 
91
93
  def __getattr__(self, item: str) -> Any:
92
94
  if item in MERCHANT_PARAMETERS_MAP:
93
- return self._parameters[item]
95
+ return self._parameters.get(item)
94
96
 
95
97
  def __setattr__(self, key, value):
96
98
  if key in MERCHANT_PARAMETERS_MAP:
@@ -93,9 +93,11 @@ class Response:
93
93
  Defines a response
94
94
  """
95
95
 
96
- _parameters: Dict[str, Any] = {}
96
+ _parameters: Dict[str, Any]
97
97
 
98
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", {})
99
101
  MERCHANT_PARAMETERS_MAP_REVERSE = {value: key for key, value in MERCHANT_PARAMETERS_MAP.items()}
100
102
  for key, value in parameters.items():
101
103
  reversed_parameter = MERCHANT_PARAMETERS_MAP_REVERSE.get(key, key)
@@ -104,7 +106,7 @@ class Response:
104
106
 
105
107
  def __getattr__(self, item: str) -> Any:
106
108
  if item in MERCHANT_PARAMETERS_MAP:
107
- return self._parameters[item]
109
+ return self._parameters.get(item)
108
110
 
109
111
  def __setattr__(self, key: str, value: Any):
110
112
  if key in MERCHANT_PARAMETERS_MAP:
@@ -133,7 +135,10 @@ class Response:
133
135
 
134
136
  @property
135
137
  def message(self):
136
- 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))
137
142
 
138
143
  @property
139
144
  def parameters(self):
@@ -63,3 +63,12 @@ class TestRequest:
63
63
  merchant_url = "".join(choice("abcdefghijklmnopqrtsuvwxyz-0123456789") for _ in range(251))
64
64
  with pytest.raises(ValueError):
65
65
  assert Request.check_merchant_url(merchant_url)
66
+
67
+ def test_requests_do_not_share_parameters(self):
68
+ first = Request(
69
+ {"order": "000000000001", "amount": D("1.00"), "currency": EUR, "mobile_number": "+34700000000"}
70
+ )
71
+ second = Request({"order": "000000000002", "amount": D("2.00"), "currency": EUR})
72
+ assert "Ds_Merchant_Bizum_MobileNumber" not in second.prepare_parameters()
73
+ assert second.mobile_number is None
74
+ assert first.prepare_parameters()["Ds_Merchant_Bizum_MobileNumber"] == "+34700000000"
@@ -0,0 +1,39 @@
1
+ from decimal import Decimal
2
+
3
+ from redsys.response import Response
4
+
5
+
6
+ class TestResponse:
7
+ def test_create_response(self):
8
+ parameters = {
9
+ "Ds_Response": "90",
10
+ "Ds_MerchantCode": "1056",
11
+ "Ds_Terminal": "1",
12
+ "Ds_TransactionType": "1",
13
+ "Ds_Order": "000000000001",
14
+ "Ds_Amount": "10054",
15
+ "Ds_MerchantData": "test merchant data",
16
+ "Ds_Currency": 978,
17
+ }
18
+ response = Response(parameters)
19
+ assert response.code == 90
20
+ assert response.message == "Transacción autorizada para pagos y preautorizaciones"
21
+ assert len(response._parameters.keys()) == 8
22
+ assert response.is_authorized is True
23
+ assert response.is_paid is True
24
+ assert response.is_canceled is False
25
+ assert response.is_refunded is False
26
+
27
+ def test_responses_do_not_share_parameters(self):
28
+ first = Response({"Ds_Response": "0000", "Ds_Order": "000000000001", "Ds_Amount": "100"})
29
+ second = Response({"Ds_Response": "0190", "Ds_Order": "000000000002"})
30
+ assert "amount" not in second.parameters
31
+ assert second.amount is None
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,135 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: python-redsys
3
- Version: 1.2.0
4
- Summary: A simple, clean and less dependant client to handle payments through Redsys.
5
- Home-page: https://github.com/systemallica/python-redsys
6
- License: MIT
7
- Author: Andrés Reverón Molina
8
- Author-email: andres@reveronmolina.me
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
- Requires-Dist: pycryptodome (>=3.19.0,<4.0.0)
19
- Description-Content-Type: text/markdown
20
-
21
-
22
- [![PyPI version](https://badge.fury.io/py/python-redsys.svg)](https://badge.fury.io/py/python-redsys)
23
-
24
- # Welcome to python-redsys!
25
-
26
- A simple, clean and less dependant client to handle payments through the
27
- Redsys platform using one of the available methods: _redirect connection_ or (secure method).
28
-
29
- The purpose of this library is to provide a normalized interface between
30
- Redsys and other Python applications.
31
-
32
- **About RedirectClient**
33
-
34
- Although _redirect connection_ depends on a webserver to resolve the
35
- communication step, the RedirectClient provided in this library does not
36
- assume any kind of procedure to resolve that step; it merely prepares
37
- the necessary parameters to make a request and handles the corresponding
38
- response parameters. That's what less dependant means.
39
-
40
- ## Example using _redirect connection_
41
-
42
- ### 0. Install python-redsys
43
-
44
- You can add python-redsys to your project with pip:
45
- > pip install python-redsys
46
-
47
- Or with poetry:
48
- > poetry add python-redsys
49
-
50
- ### 1. Instantiate the redirect client
51
-
52
- ```python
53
- from decimal import Decimal as D, ROUND_HALF_UP
54
- from redsys.constants import EUR, STANDARD_PAYMENT
55
- from redsys.client import RedirectClient
56
-
57
- secret_key = "123456789abcdef"
58
- client = RedirectClient(secret_key)
59
- ```
60
-
61
- ### 2. Set up the request parameters
62
-
63
- ```python
64
- parameters = {
65
- "merchant_code": "100000001",
66
- "terminal": "1",
67
- "transaction_type": STANDARD_PAYMENT,
68
- "currency": EUR,
69
- "order": "000000001",
70
- "amount": D("10.56489").quantize(D(".01"), ROUND_HALF_UP),
71
- "merchant_data": "test merchant data",
72
- "merchant_name": "Example Commerce",
73
- "titular": "Example Ltd.",
74
- "product_description": "Products of Example Commerce",
75
- "merchant_url": "https://example.com/redsys/response",
76
- }
77
- ```
78
-
79
- ### 3. Prepare the request
80
-
81
- This method returns a dict with the necessary post parameters that are
82
- needed during the communication step.
83
-
84
- ```python
85
- args = client.prepare_request(parameters)
86
- ```
87
-
88
- ### 4. Communication step
89
-
90
- Redirect the _user-agent_ to the corresponding Redsys' endpoint using
91
- the post parameters given in the previous step.
92
-
93
- After the payment process is finished, Redsys will respond making a
94
- request to the `merchant_url` defined in step 2.
95
-
96
- ### 5. Create and check the response
97
-
98
- Create the response object using the received parameters from Redsys.
99
- The method `create_response()` throws a `ValueError` in case the
100
- received signature is not equal to the calculated one using the
101
- given `merchant_parameters`. This normally means that the response **is
102
- not coming from Redsys** or that it **has been compromised**.
103
-
104
- ```python
105
- signature = "YqFenHc2HpB273l8c995...."
106
- merchant_parameters = "AndvIh66VZdkC5TG3nYL5j4XfCnFFbo3VkOu9TAeTs58fxddgc..."
107
- response = client.create_response(signature, merchant_parameters)
108
- if response.is_paid:
109
- # Do the corresponding actions after a successful payment
110
- else:
111
- # Do the corresponding actions after a failed payment
112
- raise Exception(response.code, response.message)
113
- ```
114
-
115
- **Methods for checking the response:**
116
-
117
- According to the Redsys documentation:
118
-
119
- - `response.is_paid`: Returns `True` if the response code is
120
- between 0 and 99 (both included).
121
- - `response.is_canceled`: Returns `True` if the response code
122
- is 400.
123
- - `response.is_refunded`: Returns `True` if the response code
124
- is 900.
125
- - `response.is_authorized`: Returns `True` if the response is
126
- **paid**, **refunded** or **canceled**.
127
-
128
- Also, you can directly access the code or the message defined in Redsys
129
- documentation using `response.code` or `response.message`.
130
-
131
- ## Contributions
132
-
133
- Please, feel free to send any contribution that maintains the _less
134
- dependant_ philosophy.
135
-
@@ -1,114 +0,0 @@
1
-
2
- [![PyPI version](https://badge.fury.io/py/python-redsys.svg)](https://badge.fury.io/py/python-redsys)
3
-
4
- # Welcome to python-redsys!
5
-
6
- A simple, clean and less dependant client to handle payments through the
7
- Redsys platform using one of the available methods: _redirect connection_ or (secure method).
8
-
9
- The purpose of this library is to provide a normalized interface between
10
- Redsys and other Python applications.
11
-
12
- **About RedirectClient**
13
-
14
- Although _redirect connection_ depends on a webserver to resolve the
15
- communication step, the RedirectClient provided in this library does not
16
- assume any kind of procedure to resolve that step; it merely prepares
17
- the necessary parameters to make a request and handles the corresponding
18
- response parameters. That's what less dependant means.
19
-
20
- ## Example using _redirect connection_
21
-
22
- ### 0. Install python-redsys
23
-
24
- You can add python-redsys to your project with pip:
25
- > pip install python-redsys
26
-
27
- Or with poetry:
28
- > poetry add python-redsys
29
-
30
- ### 1. Instantiate the redirect client
31
-
32
- ```python
33
- from decimal import Decimal as D, ROUND_HALF_UP
34
- from redsys.constants import EUR, STANDARD_PAYMENT
35
- from redsys.client import RedirectClient
36
-
37
- secret_key = "123456789abcdef"
38
- client = RedirectClient(secret_key)
39
- ```
40
-
41
- ### 2. Set up the request parameters
42
-
43
- ```python
44
- parameters = {
45
- "merchant_code": "100000001",
46
- "terminal": "1",
47
- "transaction_type": STANDARD_PAYMENT,
48
- "currency": EUR,
49
- "order": "000000001",
50
- "amount": D("10.56489").quantize(D(".01"), ROUND_HALF_UP),
51
- "merchant_data": "test merchant data",
52
- "merchant_name": "Example Commerce",
53
- "titular": "Example Ltd.",
54
- "product_description": "Products of Example Commerce",
55
- "merchant_url": "https://example.com/redsys/response",
56
- }
57
- ```
58
-
59
- ### 3. Prepare the request
60
-
61
- This method returns a dict with the necessary post parameters that are
62
- needed during the communication step.
63
-
64
- ```python
65
- args = client.prepare_request(parameters)
66
- ```
67
-
68
- ### 4. Communication step
69
-
70
- Redirect the _user-agent_ to the corresponding Redsys' endpoint using
71
- the post parameters given in the previous step.
72
-
73
- After the payment process is finished, Redsys will respond making a
74
- request to the `merchant_url` defined in step 2.
75
-
76
- ### 5. Create and check the response
77
-
78
- Create the response object using the received parameters from Redsys.
79
- The method `create_response()` throws a `ValueError` in case the
80
- received signature is not equal to the calculated one using the
81
- given `merchant_parameters`. This normally means that the response **is
82
- not coming from Redsys** or that it **has been compromised**.
83
-
84
- ```python
85
- signature = "YqFenHc2HpB273l8c995...."
86
- merchant_parameters = "AndvIh66VZdkC5TG3nYL5j4XfCnFFbo3VkOu9TAeTs58fxddgc..."
87
- response = client.create_response(signature, merchant_parameters)
88
- if response.is_paid:
89
- # Do the corresponding actions after a successful payment
90
- else:
91
- # Do the corresponding actions after a failed payment
92
- raise Exception(response.code, response.message)
93
- ```
94
-
95
- **Methods for checking the response:**
96
-
97
- According to the Redsys documentation:
98
-
99
- - `response.is_paid`: Returns `True` if the response code is
100
- between 0 and 99 (both included).
101
- - `response.is_canceled`: Returns `True` if the response code
102
- is 400.
103
- - `response.is_refunded`: Returns `True` if the response code
104
- is 900.
105
- - `response.is_authorized`: Returns `True` if the response is
106
- **paid**, **refunded** or **canceled**.
107
-
108
- Also, you can directly access the code or the message defined in Redsys
109
- documentation using `response.code` or `response.message`.
110
-
111
- ## Contributions
112
-
113
- Please, feel free to send any contribution that maintains the _less
114
- dependant_ philosophy.
@@ -1,23 +0,0 @@
1
- from redsys.response import Response
2
-
3
-
4
- class TestResponse:
5
- def test_create_response(self):
6
- parameters = {
7
- "Ds_Response": "90",
8
- "Ds_MerchantCode": "1056",
9
- "Ds_Terminal": "1",
10
- "Ds_TransactionType": "1",
11
- "Ds_Order": "000000000001",
12
- "Ds_Amount": "10054",
13
- "Ds_MerchantData": "test merchant data",
14
- "Ds_Currency": 978,
15
- }
16
- response = Response(parameters)
17
- assert response.code == 90
18
- assert response.message == "Transacción autorizada para pagos y preautorizaciones"
19
- assert len(response._parameters.keys()) == 8
20
- assert response.is_authorized is True
21
- assert response.is_paid is True
22
- assert response.is_canceled is False
23
- assert response.is_refunded is False
File without changes