python-redsys 2.1.0__tar.gz → 2.2.0__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.
Files changed (62) hide show
  1. python_redsys-2.2.0/PKG-INFO +187 -0
  2. python_redsys-2.2.0/README.md +158 -0
  3. {python_redsys-2.1.0 → python_redsys-2.2.0}/pyproject.toml +2 -2
  4. python_redsys-2.2.0/python_redsys.egg-info/PKG-INFO +187 -0
  5. {python_redsys-2.1.0 → python_redsys-2.2.0}/python_redsys.egg-info/SOURCES.txt +24 -1
  6. {python_redsys-2.1.0 → python_redsys-2.2.0}/python_redsys.egg-info/requires.txt +1 -1
  7. python_redsys-2.2.0/redsys/__init__.py +39 -0
  8. python_redsys-2.2.0/redsys/_code_texts.py +4673 -0
  9. python_redsys-2.2.0/redsys/api/__init__.py +1 -0
  10. python_redsys-2.2.0/redsys/api/bizum.py +71 -0
  11. python_redsys-2.2.0/redsys/api/cards.py +50 -0
  12. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/api/currencies.py +18 -18
  13. python_redsys-2.2.0/redsys/api/emv3ds.py +99 -0
  14. python_redsys-2.2.0/redsys/api/environments.py +41 -0
  15. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/api/languages.py +9 -1
  16. python_redsys-2.2.0/redsys/api/options.py +31 -0
  17. python_redsys-2.2.0/redsys/api/pay_methods.py +25 -0
  18. python_redsys-2.2.0/redsys/api/paygold.py +33 -0
  19. python_redsys-2.2.0/redsys/api/processed_pay_methods.py +207 -0
  20. python_redsys-2.2.0/redsys/api/responses.py +290 -0
  21. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/api/sca.py +11 -3
  22. python_redsys-2.2.0/redsys/api/secure_payment.py +12 -0
  23. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/api/transactions.py +26 -9
  24. python_redsys-2.2.0/redsys/api/xpay.py +25 -0
  25. python_redsys-2.2.0/redsys/clients.py +457 -0
  26. python_redsys-2.2.0/redsys/codes.py +76 -0
  27. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/crypto.py +8 -8
  28. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/exceptions.py +5 -2
  29. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/models.py +283 -49
  30. python_redsys-2.2.0/redsys/orders.py +20 -0
  31. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/request.py +19 -13
  32. python_redsys-2.2.0/redsys/threeds.py +72 -0
  33. python_redsys-2.2.0/tests/test_bizum.py +103 -0
  34. {python_redsys-2.1.0 → python_redsys-2.2.0}/tests/test_clients.py +79 -2
  35. python_redsys-2.2.0/tests/test_codes.py +138 -0
  36. python_redsys-2.2.0/tests/test_constants.py +98 -0
  37. {python_redsys-2.1.0 → python_redsys-2.2.0}/tests/test_crypto.py +0 -2
  38. {python_redsys-2.1.0 → python_redsys-2.2.0}/tests/test_direct.py +55 -1
  39. python_redsys-2.2.0/tests/test_environments.py +302 -0
  40. python_redsys-2.2.0/tests/test_live.py +202 -0
  41. {python_redsys-2.1.0 → python_redsys-2.2.0}/tests/test_models.py +4 -4
  42. python_redsys-2.2.0/tests/test_operations.py +242 -0
  43. python_redsys-2.2.0/tests/test_orders.py +50 -0
  44. python_redsys-2.2.0/tests/test_parameters.py +163 -0
  45. python_redsys-2.2.0/tests/test_paygold.py +94 -0
  46. {python_redsys-2.1.0 → python_redsys-2.2.0}/tests/test_threeds.py +15 -1
  47. python_redsys-2.2.0/tests/test_xpay.py +83 -0
  48. python_redsys-2.1.0/PKG-INFO +0 -225
  49. python_redsys-2.1.0/README.md +0 -196
  50. python_redsys-2.1.0/python_redsys.egg-info/PKG-INFO +0 -225
  51. python_redsys-2.1.0/redsys/__init__.py +0 -3
  52. python_redsys-2.1.0/redsys/api/__init__.py +0 -1
  53. python_redsys-2.1.0/redsys/clients.py +0 -277
  54. python_redsys-2.1.0/redsys/threeds.py +0 -67
  55. python_redsys-2.1.0/tests/test_live.py +0 -116
  56. {python_redsys-2.1.0 → python_redsys-2.2.0}/LICENSE +0 -0
  57. {python_redsys-2.1.0 → python_redsys-2.2.0}/NOTICE +0 -0
  58. {python_redsys-2.1.0 → python_redsys-2.2.0}/python_redsys.egg-info/dependency_links.txt +0 -0
  59. {python_redsys-2.1.0 → python_redsys-2.2.0}/python_redsys.egg-info/top_level.txt +0 -0
  60. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/api/cof.py +0 -0
  61. {python_redsys-2.1.0 → python_redsys-2.2.0}/redsys/py.typed +0 -0
  62. {python_redsys-2.1.0 → python_redsys-2.2.0}/setup.cfg +0 -0
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-redsys
3
+ Version: 2.2.0
4
+ Summary: A simple, lightweight client to handle payments through the RedSys platform.
5
+ Author-email: David Díaz <d.diazp@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/ddiazpinto/python-redsys
8
+ Project-URL: Source, https://github.com/ddiazpinto/python-redsys
9
+ Keywords: redsys,sermepa,tpv,payments,psd2
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3.15
19
+ Classifier: Topic :: Office/Business :: Financial
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ License-File: NOTICE
24
+ Requires-Dist: cryptography>=50
25
+ Requires-Dist: pydantic>=2.13
26
+ Provides-Extra: http
27
+ Requires-Dist: httpx>=0.26; extra == "http"
28
+ Dynamic: license-file
29
+
30
+ # python-redsys
31
+
32
+ [![CI](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml/badge.svg)](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml) [![Coverage](https://codecov.io/gh/ddiazpinto/python-redsys/branch/master/graph/badge.svg)](https://codecov.io/gh/ddiazpinto/python-redsys) [![PyPI](https://img.shields.io/pypi/v/python-redsys.svg)](https://pypi.org/project/python-redsys/) ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg) [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
33
+
34
+ A lightweight client for the RedSys TPV Virtual: it builds signed requests and verifies and parses responses, for the *redirect connection* (browser form) and the *direct connection* (server-to-server REST). For Django, see [django-redsys](https://github.com/ddiazpinto/django-redsys).
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install python-redsys
40
+ pip install "python-redsys[http]" # optional: sends direct-connection requests with httpx
41
+ ```
42
+
43
+ Python 3.10 or later. Runtime dependencies: `cryptography` and `pydantic` 2.
44
+
45
+ ## Redirect connection
46
+
47
+ ```python
48
+ from decimal import Decimal
49
+ from redsys import generate_order
50
+ from redsys.clients import RedirectClient
51
+
52
+ client = RedirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
53
+
54
+ # 1. Build the signed request.
55
+ request = client.payment(
56
+ order=generate_order(),
57
+ amount=Decimal("10.56"), # euros unless currency= says otherwise
58
+ merchant_url="https://example.com/redsys/notification",
59
+ url_ok="https://example.com/ok",
60
+ url_ko="https://example.com/ko",
61
+ )
62
+
63
+ # 2. The browser POSTs request.to_dict() (three fields) to request.endpoint.
64
+
65
+ # 3. Verify and read the notification RedSys posts to merchant_url.
66
+ response = client.parse_response(received_fields)
67
+ if response.is_paid():
68
+ ...
69
+ else:
70
+ print(response.response_code, response.describe("en"))
71
+ ```
72
+
73
+ The redirect connection takes `payment()`, `preauthorization()`, `authentication()` and `refund_without_original()`.
74
+
75
+ ## Direct connection
76
+
77
+ ```python
78
+ from redsys.clients import DirectClient
79
+
80
+ client = DirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
81
+
82
+ response = client.refund(order="000000001", amount=Decimal("10.56")).send() # signs, posts, verifies
83
+ response.is_refunded()
84
+ ```
85
+
86
+ - `AsyncDirectClient` has the same methods; `await request.send()`.
87
+ - Without the `http` extra, POST `request.to_dict()` to `request.endpoint` with any HTTP client and pass the JSON answer to `client.parse_response()`.
88
+ - Operations: `payment`, `preauthorization`, `confirmation`, `partial_confirmation`, `refund`, `refund_without_original`, `void`, `payment_cancellation`, `authentication`, `authentication_confirmation`, `pay_gold`, and `request(transaction_type, ...)` for any other type in `redsys.transactions`.
89
+
90
+ ## 3-D Secure
91
+
92
+ Card data sent by the merchant goes through EMV3DS. The library builds each call; the browser steps (3DSMethod iframe, challenge form) are the integrator's.
93
+
94
+ ```python
95
+ from redsys import emv3ds, threeds
96
+
97
+ card = dict(pan="4548810000000003", expiry_date="4912", cvv2="123")
98
+ operation = dict(order=generate_order(), amount=Decimal("10.56"))
99
+
100
+ config = client.inicia_payment(**operation, **card).send()
101
+ # Optional 3DSMethod: the browser posts threeds.method_data(...) to config.three_ds_method_url.
102
+
103
+ authentication = threeds.authentication_data(
104
+ three_ds_server_trans_id=config.three_ds_server_trans_id,
105
+ notification_url="https://example.com/redsys/3ds",
106
+ protocol_version=config.protocol_version,
107
+ browser=threeds.browser_info(...), # collected by the merchant page
108
+ three_ds_comp_ind=emv3ds.METHOD_COMPLETED,
109
+ )
110
+ response = client.payment(emv3ds=authentication, **operation, **card).send()
111
+
112
+ if response.requires_challenge():
113
+ # The browser posts response.creq to response.acs_url; the ACS posts cres back to notification_url.
114
+ response = client.challenge_response(
115
+ cres=cres, protocol_version=response.protocol_version, **operation, **card
116
+ ).send()
117
+ ```
118
+
119
+ ## Stored cards and merchant-initiated payments
120
+
121
+ ```python
122
+ from redsys import cof, options, sca
123
+
124
+ first = client.payment(..., identifier=cof.REQUEST_TOKEN, cof_ini=cof.INITIAL, cof_type=cof.RECURRING)
125
+ # After 3-D Secure: first.identifier is the token, first.cof_txnid the network transaction id.
126
+
127
+ client.payment(
128
+ order=generate_order(), amount=Decimal("10.56"),
129
+ identifier=token, cof_txnid=cof_txnid, cof_ini=cof.SUBSEQUENT, cof_type=cof.RECURRING,
130
+ excep_sca=sca.MIT, direct_payment=options.DIRECT_PAYMENT,
131
+ ).send()
132
+ ```
133
+
134
+ ## Payment methods
135
+
136
+ - **Bizum, redirect:** `pay_methods=pay_methods.BIZUM` on `payment()`.
137
+ - **Bizum Request-to-Pay, direct:** the same, plus `bizum_mobile_number` and `endpoint=client.url(environments.RTP_TRATA_PATH)`; read `response.rtp_status` and `response.rtp_response` (`redsys.bizum`).
138
+ - **PayGold link:** `pay_gold(customer_mobile=..., customer_sms_text=f"{paygold.SMS_AMOUNT} {paygold.SMS_URL}")`; the link is `response.url_pago_2_fases`.
139
+ - **Google Pay and Apple Pay:** `xpay_data` (or `xpay_decoded_data`) with `xpay_type=xpay.GOOGLE` and `xpay_origen=xpay.WEB`.
140
+ - **inSite:** `idoper=` with the token from RedSys's JavaScript, on a direct `payment()`.
141
+
142
+ ## Configuration
143
+
144
+ | Option | Use |
145
+ |---|---|
146
+ | `environment=` | `environments.TEST`, `PROD`, `INTE` or `DESA`; `sandbox=True` is `TEST` |
147
+ | `Client.from_api_key(api_key)` | Reads the environment, merchant code, terminal and secret key from a RedSys API key |
148
+ | `currency=` | Default currency of the client (`currencies.EUR` unless set); a call can override it |
149
+ | `signature_version=` | `HMAC_SHA512_V2` (default), `HMAC_SHA512_V1` or legacy `HMAC_SHA256_V1`, from `redsys.clients` |
150
+ | `module=` | `Ds_Merchant_Module` of every request: `python-redsys/<version>` by default, another text, or `None` to leave it out |
151
+ | `endpoint=` on an operation | Sends that request to another HTTPS URL; `client.url(path)` builds one |
152
+
153
+ Every documented value has a named constant in `redsys` (`transactions`, `currencies`, `languages`, `pay_methods`, `cof`, `sca`, `options`, `emv3ds`, `cards`, `responses`, ...). Use them instead of literals.
154
+
155
+ ## Errors
156
+
157
+ Fields are validated when the operation method is called, before anything is signed or sent. Any invalid parameter raises a `ValueError` subclass:
158
+
159
+ | Problem | Raised |
160
+ |---|---|
161
+ | A field with the wrong type or format, a missing required field, a misspelled or 1.x field name, an unknown currency or transaction type | `pydantic.ValidationError`; `.errors()` lists each field |
162
+ | A transaction type that contradicts the operation or the redirect connection, a non-HTTPS `endpoint`, an invalid API key or environment | `redsys.exceptions.ValidationError` |
163
+ | A signature that does not match in `parse_response()` | `ValueError` |
164
+ | RedSys answers with an error code instead of a signed result | `redsys.exceptions.ResponseError`; `.error_code` holds the `SISnnnn` code |
165
+ | An HTTP error status from RedSys in `send()` | `httpx.HTTPStatusError` |
166
+
167
+ A parameter the library does not model yet can be passed by its wire name (`Ds_Merchant_...`): it is sent as given, with an `UnknownParameterWarning`.
168
+
169
+ ## Response codes
170
+
171
+ `response.response_code` is the integer `Ds_Response`; `response.is_paid()` (0 to 99), `is_refunded()` (900), `is_canceled()` (400) and `is_authorized()` (any of them) cover the common outcomes. `response.describe(language)` and `redsys.codes.describe(code, language)` give the meaning of any `Ds_Response`, `SISnnnn` or Bizum `BIZnnnnn` code. Table: [`docs/response-codes.md`](docs/response-codes.md).
172
+
173
+ ## Status
174
+
175
+ | | |
176
+ |---|---|
177
+ | Version | 2.2.0; 1.x receives fixes until 2026-12-31 ([migration guide](docs/migration-1x-to-2x.md)) |
178
+ | Python | 3.10 to 3.15 |
179
+ | RedSys documents followed | Redirection guide 4.2, REST guide 4.0.1, Bizum guide 2.6, inSite guide 4.1, PayGold guide 1.8.2, Google Pay guide 1.9, Apple Pay guide 3.2, "Parámetros de entrada y salida" page |
180
+ | Verified against the RedSys test environment | Redirect in a browser with the 3DS challenge; direct end to end (frictionless and challenged 3DS, 3DSMethod, refund, void, payment cancellation, stored-card merchant-initiated charge), in all three signature versions |
181
+
182
+ ## Reference
183
+
184
+ - [`docs/redirect-connection.md`](docs/redirect-connection.md) and [`docs/direct-connection.md`](docs/direct-connection.md): protocol reference for each connection.
185
+ - [`docs/response-codes.md`](docs/response-codes.md): response, SIS and Bizum codes.
186
+ - [`docs/official-libraries.md`](docs/official-libraries.md): equivalences for integrators of RedSys's PHP and Java libraries.
187
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md): development setup, checks and live tests.
@@ -0,0 +1,158 @@
1
+ # python-redsys
2
+
3
+ [![CI](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml/badge.svg)](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml) [![Coverage](https://codecov.io/gh/ddiazpinto/python-redsys/branch/master/graph/badge.svg)](https://codecov.io/gh/ddiazpinto/python-redsys) [![PyPI](https://img.shields.io/pypi/v/python-redsys.svg)](https://pypi.org/project/python-redsys/) ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg) [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
4
+
5
+ A lightweight client for the RedSys TPV Virtual: it builds signed requests and verifies and parses responses, for the *redirect connection* (browser form) and the *direct connection* (server-to-server REST). For Django, see [django-redsys](https://github.com/ddiazpinto/django-redsys).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install python-redsys
11
+ pip install "python-redsys[http]" # optional: sends direct-connection requests with httpx
12
+ ```
13
+
14
+ Python 3.10 or later. Runtime dependencies: `cryptography` and `pydantic` 2.
15
+
16
+ ## Redirect connection
17
+
18
+ ```python
19
+ from decimal import Decimal
20
+ from redsys import generate_order
21
+ from redsys.clients import RedirectClient
22
+
23
+ client = RedirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
24
+
25
+ # 1. Build the signed request.
26
+ request = client.payment(
27
+ order=generate_order(),
28
+ amount=Decimal("10.56"), # euros unless currency= says otherwise
29
+ merchant_url="https://example.com/redsys/notification",
30
+ url_ok="https://example.com/ok",
31
+ url_ko="https://example.com/ko",
32
+ )
33
+
34
+ # 2. The browser POSTs request.to_dict() (three fields) to request.endpoint.
35
+
36
+ # 3. Verify and read the notification RedSys posts to merchant_url.
37
+ response = client.parse_response(received_fields)
38
+ if response.is_paid():
39
+ ...
40
+ else:
41
+ print(response.response_code, response.describe("en"))
42
+ ```
43
+
44
+ The redirect connection takes `payment()`, `preauthorization()`, `authentication()` and `refund_without_original()`.
45
+
46
+ ## Direct connection
47
+
48
+ ```python
49
+ from redsys.clients import DirectClient
50
+
51
+ client = DirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
52
+
53
+ response = client.refund(order="000000001", amount=Decimal("10.56")).send() # signs, posts, verifies
54
+ response.is_refunded()
55
+ ```
56
+
57
+ - `AsyncDirectClient` has the same methods; `await request.send()`.
58
+ - Without the `http` extra, POST `request.to_dict()` to `request.endpoint` with any HTTP client and pass the JSON answer to `client.parse_response()`.
59
+ - Operations: `payment`, `preauthorization`, `confirmation`, `partial_confirmation`, `refund`, `refund_without_original`, `void`, `payment_cancellation`, `authentication`, `authentication_confirmation`, `pay_gold`, and `request(transaction_type, ...)` for any other type in `redsys.transactions`.
60
+
61
+ ## 3-D Secure
62
+
63
+ Card data sent by the merchant goes through EMV3DS. The library builds each call; the browser steps (3DSMethod iframe, challenge form) are the integrator's.
64
+
65
+ ```python
66
+ from redsys import emv3ds, threeds
67
+
68
+ card = dict(pan="4548810000000003", expiry_date="4912", cvv2="123")
69
+ operation = dict(order=generate_order(), amount=Decimal("10.56"))
70
+
71
+ config = client.inicia_payment(**operation, **card).send()
72
+ # Optional 3DSMethod: the browser posts threeds.method_data(...) to config.three_ds_method_url.
73
+
74
+ authentication = threeds.authentication_data(
75
+ three_ds_server_trans_id=config.three_ds_server_trans_id,
76
+ notification_url="https://example.com/redsys/3ds",
77
+ protocol_version=config.protocol_version,
78
+ browser=threeds.browser_info(...), # collected by the merchant page
79
+ three_ds_comp_ind=emv3ds.METHOD_COMPLETED,
80
+ )
81
+ response = client.payment(emv3ds=authentication, **operation, **card).send()
82
+
83
+ if response.requires_challenge():
84
+ # The browser posts response.creq to response.acs_url; the ACS posts cres back to notification_url.
85
+ response = client.challenge_response(
86
+ cres=cres, protocol_version=response.protocol_version, **operation, **card
87
+ ).send()
88
+ ```
89
+
90
+ ## Stored cards and merchant-initiated payments
91
+
92
+ ```python
93
+ from redsys import cof, options, sca
94
+
95
+ first = client.payment(..., identifier=cof.REQUEST_TOKEN, cof_ini=cof.INITIAL, cof_type=cof.RECURRING)
96
+ # After 3-D Secure: first.identifier is the token, first.cof_txnid the network transaction id.
97
+
98
+ client.payment(
99
+ order=generate_order(), amount=Decimal("10.56"),
100
+ identifier=token, cof_txnid=cof_txnid, cof_ini=cof.SUBSEQUENT, cof_type=cof.RECURRING,
101
+ excep_sca=sca.MIT, direct_payment=options.DIRECT_PAYMENT,
102
+ ).send()
103
+ ```
104
+
105
+ ## Payment methods
106
+
107
+ - **Bizum, redirect:** `pay_methods=pay_methods.BIZUM` on `payment()`.
108
+ - **Bizum Request-to-Pay, direct:** the same, plus `bizum_mobile_number` and `endpoint=client.url(environments.RTP_TRATA_PATH)`; read `response.rtp_status` and `response.rtp_response` (`redsys.bizum`).
109
+ - **PayGold link:** `pay_gold(customer_mobile=..., customer_sms_text=f"{paygold.SMS_AMOUNT} {paygold.SMS_URL}")`; the link is `response.url_pago_2_fases`.
110
+ - **Google Pay and Apple Pay:** `xpay_data` (or `xpay_decoded_data`) with `xpay_type=xpay.GOOGLE` and `xpay_origen=xpay.WEB`.
111
+ - **inSite:** `idoper=` with the token from RedSys's JavaScript, on a direct `payment()`.
112
+
113
+ ## Configuration
114
+
115
+ | Option | Use |
116
+ |---|---|
117
+ | `environment=` | `environments.TEST`, `PROD`, `INTE` or `DESA`; `sandbox=True` is `TEST` |
118
+ | `Client.from_api_key(api_key)` | Reads the environment, merchant code, terminal and secret key from a RedSys API key |
119
+ | `currency=` | Default currency of the client (`currencies.EUR` unless set); a call can override it |
120
+ | `signature_version=` | `HMAC_SHA512_V2` (default), `HMAC_SHA512_V1` or legacy `HMAC_SHA256_V1`, from `redsys.clients` |
121
+ | `module=` | `Ds_Merchant_Module` of every request: `python-redsys/<version>` by default, another text, or `None` to leave it out |
122
+ | `endpoint=` on an operation | Sends that request to another HTTPS URL; `client.url(path)` builds one |
123
+
124
+ Every documented value has a named constant in `redsys` (`transactions`, `currencies`, `languages`, `pay_methods`, `cof`, `sca`, `options`, `emv3ds`, `cards`, `responses`, ...). Use them instead of literals.
125
+
126
+ ## Errors
127
+
128
+ Fields are validated when the operation method is called, before anything is signed or sent. Any invalid parameter raises a `ValueError` subclass:
129
+
130
+ | Problem | Raised |
131
+ |---|---|
132
+ | A field with the wrong type or format, a missing required field, a misspelled or 1.x field name, an unknown currency or transaction type | `pydantic.ValidationError`; `.errors()` lists each field |
133
+ | A transaction type that contradicts the operation or the redirect connection, a non-HTTPS `endpoint`, an invalid API key or environment | `redsys.exceptions.ValidationError` |
134
+ | A signature that does not match in `parse_response()` | `ValueError` |
135
+ | RedSys answers with an error code instead of a signed result | `redsys.exceptions.ResponseError`; `.error_code` holds the `SISnnnn` code |
136
+ | An HTTP error status from RedSys in `send()` | `httpx.HTTPStatusError` |
137
+
138
+ A parameter the library does not model yet can be passed by its wire name (`Ds_Merchant_...`): it is sent as given, with an `UnknownParameterWarning`.
139
+
140
+ ## Response codes
141
+
142
+ `response.response_code` is the integer `Ds_Response`; `response.is_paid()` (0 to 99), `is_refunded()` (900), `is_canceled()` (400) and `is_authorized()` (any of them) cover the common outcomes. `response.describe(language)` and `redsys.codes.describe(code, language)` give the meaning of any `Ds_Response`, `SISnnnn` or Bizum `BIZnnnnn` code. Table: [`docs/response-codes.md`](docs/response-codes.md).
143
+
144
+ ## Status
145
+
146
+ | | |
147
+ |---|---|
148
+ | Version | 2.2.0; 1.x receives fixes until 2026-12-31 ([migration guide](docs/migration-1x-to-2x.md)) |
149
+ | Python | 3.10 to 3.15 |
150
+ | RedSys documents followed | Redirection guide 4.2, REST guide 4.0.1, Bizum guide 2.6, inSite guide 4.1, PayGold guide 1.8.2, Google Pay guide 1.9, Apple Pay guide 3.2, "Parámetros de entrada y salida" page |
151
+ | Verified against the RedSys test environment | Redirect in a browser with the 3DS challenge; direct end to end (frictionless and challenged 3DS, 3DSMethod, refund, void, payment cancellation, stored-card merchant-initiated charge), in all three signature versions |
152
+
153
+ ## Reference
154
+
155
+ - [`docs/redirect-connection.md`](docs/redirect-connection.md) and [`docs/direct-connection.md`](docs/direct-connection.md): protocol reference for each connection.
156
+ - [`docs/response-codes.md`](docs/response-codes.md): response, SIS and Bizum codes.
157
+ - [`docs/official-libraries.md`](docs/official-libraries.md): equivalences for integrators of RedSys's PHP and Java libraries.
158
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md): development setup, checks and live tests.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-redsys"
7
- version = "2.1.0"
7
+ version = "2.2.0"
8
8
  description = "A simple, lightweight client to handle payments through the RedSys platform."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -12,7 +12,7 @@ license = "Apache-2.0"
12
12
  license-files = ["LICENSE", "NOTICE"]
13
13
  authors = [{ name = "David Díaz", email = "d.diazp@gmail.com" }]
14
14
  keywords = ["redsys", "sermepa", "tpv", "payments", "psd2"]
15
- dependencies = ["cryptography>=49", "pydantic>=2.13"]
15
+ dependencies = ["cryptography>=50", "pydantic>=2.13"]
16
16
  classifiers = [
17
17
  "Intended Audience :: Developers",
18
18
  "Operating System :: OS Independent",
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-redsys
3
+ Version: 2.2.0
4
+ Summary: A simple, lightweight client to handle payments through the RedSys platform.
5
+ Author-email: David Díaz <d.diazp@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/ddiazpinto/python-redsys
8
+ Project-URL: Source, https://github.com/ddiazpinto/python-redsys
9
+ Keywords: redsys,sermepa,tpv,payments,psd2
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3.15
19
+ Classifier: Topic :: Office/Business :: Financial
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ License-File: NOTICE
24
+ Requires-Dist: cryptography>=50
25
+ Requires-Dist: pydantic>=2.13
26
+ Provides-Extra: http
27
+ Requires-Dist: httpx>=0.26; extra == "http"
28
+ Dynamic: license-file
29
+
30
+ # python-redsys
31
+
32
+ [![CI](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml/badge.svg)](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml) [![Coverage](https://codecov.io/gh/ddiazpinto/python-redsys/branch/master/graph/badge.svg)](https://codecov.io/gh/ddiazpinto/python-redsys) [![PyPI](https://img.shields.io/pypi/v/python-redsys.svg)](https://pypi.org/project/python-redsys/) ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg) [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
33
+
34
+ A lightweight client for the RedSys TPV Virtual: it builds signed requests and verifies and parses responses, for the *redirect connection* (browser form) and the *direct connection* (server-to-server REST). For Django, see [django-redsys](https://github.com/ddiazpinto/django-redsys).
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install python-redsys
40
+ pip install "python-redsys[http]" # optional: sends direct-connection requests with httpx
41
+ ```
42
+
43
+ Python 3.10 or later. Runtime dependencies: `cryptography` and `pydantic` 2.
44
+
45
+ ## Redirect connection
46
+
47
+ ```python
48
+ from decimal import Decimal
49
+ from redsys import generate_order
50
+ from redsys.clients import RedirectClient
51
+
52
+ client = RedirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
53
+
54
+ # 1. Build the signed request.
55
+ request = client.payment(
56
+ order=generate_order(),
57
+ amount=Decimal("10.56"), # euros unless currency= says otherwise
58
+ merchant_url="https://example.com/redsys/notification",
59
+ url_ok="https://example.com/ok",
60
+ url_ko="https://example.com/ko",
61
+ )
62
+
63
+ # 2. The browser POSTs request.to_dict() (three fields) to request.endpoint.
64
+
65
+ # 3. Verify and read the notification RedSys posts to merchant_url.
66
+ response = client.parse_response(received_fields)
67
+ if response.is_paid():
68
+ ...
69
+ else:
70
+ print(response.response_code, response.describe("en"))
71
+ ```
72
+
73
+ The redirect connection takes `payment()`, `preauthorization()`, `authentication()` and `refund_without_original()`.
74
+
75
+ ## Direct connection
76
+
77
+ ```python
78
+ from redsys.clients import DirectClient
79
+
80
+ client = DirectClient(secret_key="...", merchant_code="999008881", terminal=1, sandbox=True)
81
+
82
+ response = client.refund(order="000000001", amount=Decimal("10.56")).send() # signs, posts, verifies
83
+ response.is_refunded()
84
+ ```
85
+
86
+ - `AsyncDirectClient` has the same methods; `await request.send()`.
87
+ - Without the `http` extra, POST `request.to_dict()` to `request.endpoint` with any HTTP client and pass the JSON answer to `client.parse_response()`.
88
+ - Operations: `payment`, `preauthorization`, `confirmation`, `partial_confirmation`, `refund`, `refund_without_original`, `void`, `payment_cancellation`, `authentication`, `authentication_confirmation`, `pay_gold`, and `request(transaction_type, ...)` for any other type in `redsys.transactions`.
89
+
90
+ ## 3-D Secure
91
+
92
+ Card data sent by the merchant goes through EMV3DS. The library builds each call; the browser steps (3DSMethod iframe, challenge form) are the integrator's.
93
+
94
+ ```python
95
+ from redsys import emv3ds, threeds
96
+
97
+ card = dict(pan="4548810000000003", expiry_date="4912", cvv2="123")
98
+ operation = dict(order=generate_order(), amount=Decimal("10.56"))
99
+
100
+ config = client.inicia_payment(**operation, **card).send()
101
+ # Optional 3DSMethod: the browser posts threeds.method_data(...) to config.three_ds_method_url.
102
+
103
+ authentication = threeds.authentication_data(
104
+ three_ds_server_trans_id=config.three_ds_server_trans_id,
105
+ notification_url="https://example.com/redsys/3ds",
106
+ protocol_version=config.protocol_version,
107
+ browser=threeds.browser_info(...), # collected by the merchant page
108
+ three_ds_comp_ind=emv3ds.METHOD_COMPLETED,
109
+ )
110
+ response = client.payment(emv3ds=authentication, **operation, **card).send()
111
+
112
+ if response.requires_challenge():
113
+ # The browser posts response.creq to response.acs_url; the ACS posts cres back to notification_url.
114
+ response = client.challenge_response(
115
+ cres=cres, protocol_version=response.protocol_version, **operation, **card
116
+ ).send()
117
+ ```
118
+
119
+ ## Stored cards and merchant-initiated payments
120
+
121
+ ```python
122
+ from redsys import cof, options, sca
123
+
124
+ first = client.payment(..., identifier=cof.REQUEST_TOKEN, cof_ini=cof.INITIAL, cof_type=cof.RECURRING)
125
+ # After 3-D Secure: first.identifier is the token, first.cof_txnid the network transaction id.
126
+
127
+ client.payment(
128
+ order=generate_order(), amount=Decimal("10.56"),
129
+ identifier=token, cof_txnid=cof_txnid, cof_ini=cof.SUBSEQUENT, cof_type=cof.RECURRING,
130
+ excep_sca=sca.MIT, direct_payment=options.DIRECT_PAYMENT,
131
+ ).send()
132
+ ```
133
+
134
+ ## Payment methods
135
+
136
+ - **Bizum, redirect:** `pay_methods=pay_methods.BIZUM` on `payment()`.
137
+ - **Bizum Request-to-Pay, direct:** the same, plus `bizum_mobile_number` and `endpoint=client.url(environments.RTP_TRATA_PATH)`; read `response.rtp_status` and `response.rtp_response` (`redsys.bizum`).
138
+ - **PayGold link:** `pay_gold(customer_mobile=..., customer_sms_text=f"{paygold.SMS_AMOUNT} {paygold.SMS_URL}")`; the link is `response.url_pago_2_fases`.
139
+ - **Google Pay and Apple Pay:** `xpay_data` (or `xpay_decoded_data`) with `xpay_type=xpay.GOOGLE` and `xpay_origen=xpay.WEB`.
140
+ - **inSite:** `idoper=` with the token from RedSys's JavaScript, on a direct `payment()`.
141
+
142
+ ## Configuration
143
+
144
+ | Option | Use |
145
+ |---|---|
146
+ | `environment=` | `environments.TEST`, `PROD`, `INTE` or `DESA`; `sandbox=True` is `TEST` |
147
+ | `Client.from_api_key(api_key)` | Reads the environment, merchant code, terminal and secret key from a RedSys API key |
148
+ | `currency=` | Default currency of the client (`currencies.EUR` unless set); a call can override it |
149
+ | `signature_version=` | `HMAC_SHA512_V2` (default), `HMAC_SHA512_V1` or legacy `HMAC_SHA256_V1`, from `redsys.clients` |
150
+ | `module=` | `Ds_Merchant_Module` of every request: `python-redsys/<version>` by default, another text, or `None` to leave it out |
151
+ | `endpoint=` on an operation | Sends that request to another HTTPS URL; `client.url(path)` builds one |
152
+
153
+ Every documented value has a named constant in `redsys` (`transactions`, `currencies`, `languages`, `pay_methods`, `cof`, `sca`, `options`, `emv3ds`, `cards`, `responses`, ...). Use them instead of literals.
154
+
155
+ ## Errors
156
+
157
+ Fields are validated when the operation method is called, before anything is signed or sent. Any invalid parameter raises a `ValueError` subclass:
158
+
159
+ | Problem | Raised |
160
+ |---|---|
161
+ | A field with the wrong type or format, a missing required field, a misspelled or 1.x field name, an unknown currency or transaction type | `pydantic.ValidationError`; `.errors()` lists each field |
162
+ | A transaction type that contradicts the operation or the redirect connection, a non-HTTPS `endpoint`, an invalid API key or environment | `redsys.exceptions.ValidationError` |
163
+ | A signature that does not match in `parse_response()` | `ValueError` |
164
+ | RedSys answers with an error code instead of a signed result | `redsys.exceptions.ResponseError`; `.error_code` holds the `SISnnnn` code |
165
+ | An HTTP error status from RedSys in `send()` | `httpx.HTTPStatusError` |
166
+
167
+ A parameter the library does not model yet can be passed by its wire name (`Ds_Merchant_...`): it is sent as given, with an `UnknownParameterWarning`.
168
+
169
+ ## Response codes
170
+
171
+ `response.response_code` is the integer `Ds_Response`; `response.is_paid()` (0 to 99), `is_refunded()` (900), `is_canceled()` (400) and `is_authorized()` (any of them) cover the common outcomes. `response.describe(language)` and `redsys.codes.describe(code, language)` give the meaning of any `Ds_Response`, `SISnnnn` or Bizum `BIZnnnnn` code. Table: [`docs/response-codes.md`](docs/response-codes.md).
172
+
173
+ ## Status
174
+
175
+ | | |
176
+ |---|---|
177
+ | Version | 2.2.0; 1.x receives fixes until 2026-12-31 ([migration guide](docs/migration-1x-to-2x.md)) |
178
+ | Python | 3.10 to 3.15 |
179
+ | RedSys documents followed | Redirection guide 4.2, REST guide 4.0.1, Bizum guide 2.6, inSite guide 4.1, PayGold guide 1.8.2, Google Pay guide 1.9, Apple Pay guide 3.2, "Parámetros de entrada y salida" page |
180
+ | Verified against the RedSys test environment | Redirect in a browser with the 3DS challenge; direct end to end (frictionless and challenged 3DS, 3DSMethod, refund, void, payment cancellation, stored-card merchant-initiated charge), in all three signature versions |
181
+
182
+ ## Reference
183
+
184
+ - [`docs/redirect-connection.md`](docs/redirect-connection.md) and [`docs/direct-connection.md`](docs/direct-connection.md): protocol reference for each connection.
185
+ - [`docs/response-codes.md`](docs/response-codes.md): response, SIS and Bizum codes.
186
+ - [`docs/official-libraries.md`](docs/official-libraries.md): equivalences for integrators of RedSys's PHP and Java libraries.
187
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md): development setup, checks and live tests.
@@ -8,22 +8,45 @@ python_redsys.egg-info/dependency_links.txt
8
8
  python_redsys.egg-info/requires.txt
9
9
  python_redsys.egg-info/top_level.txt
10
10
  redsys/__init__.py
11
+ redsys/_code_texts.py
11
12
  redsys/clients.py
13
+ redsys/codes.py
12
14
  redsys/crypto.py
13
15
  redsys/exceptions.py
14
16
  redsys/models.py
17
+ redsys/orders.py
15
18
  redsys/py.typed
16
19
  redsys/request.py
17
20
  redsys/threeds.py
18
21
  redsys/api/__init__.py
22
+ redsys/api/bizum.py
23
+ redsys/api/cards.py
19
24
  redsys/api/cof.py
20
25
  redsys/api/currencies.py
26
+ redsys/api/emv3ds.py
27
+ redsys/api/environments.py
21
28
  redsys/api/languages.py
29
+ redsys/api/options.py
30
+ redsys/api/pay_methods.py
31
+ redsys/api/paygold.py
32
+ redsys/api/processed_pay_methods.py
33
+ redsys/api/responses.py
22
34
  redsys/api/sca.py
35
+ redsys/api/secure_payment.py
23
36
  redsys/api/transactions.py
37
+ redsys/api/xpay.py
38
+ tests/test_bizum.py
24
39
  tests/test_clients.py
40
+ tests/test_codes.py
41
+ tests/test_constants.py
25
42
  tests/test_crypto.py
26
43
  tests/test_direct.py
44
+ tests/test_environments.py
27
45
  tests/test_live.py
28
46
  tests/test_models.py
29
- tests/test_threeds.py
47
+ tests/test_operations.py
48
+ tests/test_orders.py
49
+ tests/test_parameters.py
50
+ tests/test_paygold.py
51
+ tests/test_threeds.py
52
+ tests/test_xpay.py
@@ -1,4 +1,4 @@
1
- cryptography>=49
1
+ cryptography>=50
2
2
  pydantic>=2.13
3
3
 
4
4
  [http]
@@ -0,0 +1,39 @@
1
+ from redsys.api import (
2
+ bizum,
3
+ cards,
4
+ cof,
5
+ currencies,
6
+ emv3ds,
7
+ environments,
8
+ languages,
9
+ options,
10
+ pay_methods,
11
+ paygold,
12
+ processed_pay_methods,
13
+ responses,
14
+ sca,
15
+ secure_payment,
16
+ transactions,
17
+ xpay,
18
+ )
19
+ from redsys.orders import generate_order
20
+
21
+ __all__ = [
22
+ 'bizum',
23
+ 'cards',
24
+ 'cof',
25
+ 'currencies',
26
+ 'emv3ds',
27
+ 'environments',
28
+ 'generate_order',
29
+ 'languages',
30
+ 'options',
31
+ 'pay_methods',
32
+ 'paygold',
33
+ 'processed_pay_methods',
34
+ 'responses',
35
+ 'sca',
36
+ 'secure_payment',
37
+ 'transactions',
38
+ 'xpay',
39
+ ]