python-redsys 2.0.0__py3-none-any.whl → 2.2.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.
- python_redsys-2.2.0.dist-info/METADATA +187 -0
- python_redsys-2.2.0.dist-info/RECORD +34 -0
- redsys/__init__.py +38 -2
- redsys/_code_texts.py +4673 -0
- redsys/api/__init__.py +1 -1
- redsys/api/bizum.py +71 -0
- redsys/api/cards.py +50 -0
- redsys/api/currencies.py +18 -18
- redsys/api/emv3ds.py +99 -0
- redsys/api/environments.py +41 -0
- redsys/api/languages.py +9 -1
- redsys/api/options.py +31 -0
- redsys/api/pay_methods.py +25 -0
- redsys/api/paygold.py +33 -0
- redsys/api/processed_pay_methods.py +207 -0
- redsys/api/responses.py +290 -0
- redsys/api/sca.py +11 -3
- redsys/api/secure_payment.py +12 -0
- redsys/api/transactions.py +53 -8
- redsys/api/xpay.py +25 -0
- redsys/clients.py +270 -85
- redsys/codes.py +76 -0
- redsys/crypto.py +8 -8
- redsys/exceptions.py +5 -2
- redsys/models.py +309 -57
- redsys/orders.py +20 -0
- redsys/request.py +19 -13
- redsys/threeds.py +21 -16
- python_redsys-2.0.0.dist-info/METADATA +0 -214
- python_redsys-2.0.0.dist-info/RECORD +0 -20
- {python_redsys-2.0.0.dist-info → python_redsys-2.2.0.dist-info}/WHEEL +0 -0
- {python_redsys-2.0.0.dist-info → python_redsys-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {python_redsys-2.0.0.dist-info → python_redsys-2.2.0.dist-info}/licenses/NOTICE +0 -0
- {python_redsys-2.0.0.dist-info → python_redsys-2.2.0.dist-info}/top_level.txt +0 -0
redsys/threeds.py
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
"""Builders for the Ds_Merchant_EMV3DS object
|
|
1
|
+
"""Builders for the ``Ds_Merchant_EMV3DS`` object of 3-D Secure v2 on the direct connection.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
finalize (`challenge_response`). See docs/direct-connection.md. The
|
|
5
|
-
EMV3DS sub-field names are case-sensitive and passed through verbatim.
|
|
3
|
+
The step numbers follow docs/direct-connection.md. The sub-field names are case-sensitive.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
client.payment(emv3ds=threeds.authentication_data(..., three_ds_comp_ind=emv3ds.METHOD_COMPLETED), ...)
|
|
6
|
+
"""
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
from base64 import urlsafe_b64encode
|
|
9
|
+
from json import dumps
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
"""
|
|
11
|
+
from redsys.api import emv3ds
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
def card_data() -> dict:
|
|
16
|
-
"""
|
|
17
|
-
return {'threeDSInfo':
|
|
15
|
+
"""``emv3ds`` of the 3-D Secure initialization, iniciaPeticionREST (step 1)."""
|
|
16
|
+
return {'threeDSInfo': emv3ds.CARD_DATA}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def method_data(*, three_ds_server_trans_id, notification_url) -> str:
|
|
20
|
+
"""Value of the ``threeDSMethodData`` form field the browser posts to ``threeDSMethodURL`` (step 2)."""
|
|
21
|
+
payload = {'threeDSServerTransID': three_ds_server_trans_id, 'threeDSMethodNotificationURL': notification_url}
|
|
22
|
+
return urlsafe_b64encode(dumps(payload, separators=(',', ':')).encode('utf-8')).decode('ascii').rstrip('=')
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
def browser_info(
|
|
@@ -30,7 +35,7 @@ def browser_info(
|
|
|
30
35
|
javascript_enabled=True,
|
|
31
36
|
ip=None,
|
|
32
37
|
) -> dict:
|
|
33
|
-
"""Cardholder browser data the merchant page
|
|
38
|
+
"""Cardholder browser data, collected by the merchant page, for ``authentication_data()``."""
|
|
34
39
|
data = {
|
|
35
40
|
'browserAcceptHeader': accept_header,
|
|
36
41
|
'browserUserAgent': user_agent,
|
|
@@ -48,11 +53,11 @@ def browser_info(
|
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
def authentication_data(
|
|
51
|
-
*, three_ds_server_trans_id, notification_url, protocol_version, browser, three_ds_comp_ind=
|
|
56
|
+
*, three_ds_server_trans_id, notification_url, protocol_version, browser, three_ds_comp_ind=emv3ds.METHOD_COMPLETED
|
|
52
57
|
) -> dict:
|
|
53
|
-
"""
|
|
58
|
+
"""``emv3ds`` of the authentication (step 3); ``browser`` is ``browser_info(...)``."""
|
|
54
59
|
data = {
|
|
55
|
-
'threeDSInfo':
|
|
60
|
+
'threeDSInfo': emv3ds.AUTHENTICATION_DATA,
|
|
56
61
|
'protocolVersion': protocol_version,
|
|
57
62
|
'threeDSServerTransID': three_ds_server_trans_id,
|
|
58
63
|
'notificationURL': notification_url,
|
|
@@ -63,5 +68,5 @@ def authentication_data(
|
|
|
63
68
|
|
|
64
69
|
|
|
65
70
|
def challenge_response(*, cres, protocol_version) -> dict:
|
|
66
|
-
"""
|
|
67
|
-
return {'threeDSInfo':
|
|
71
|
+
"""``emv3ds`` of the operation after the challenge (step 5)."""
|
|
72
|
+
return {'threeDSInfo': emv3ds.CHALLENGE_RESPONSE, 'protocolVersion': protocol_version, 'cres': cres}
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python-redsys
|
|
3
|
-
Version: 2.0.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>=49
|
|
25
|
-
Requires-Dist: pydantic>=2.13
|
|
26
|
-
Provides-Extra: http
|
|
27
|
-
Requires-Dist: httpx>=0.26; extra == "http"
|
|
28
|
-
Dynamic: license-file
|
|
29
|
-
|
|
30
|
-
# Welcome to python-redsys!
|
|
31
|
-
|
|
32
|
-
[](https://github.com/ddiazpinto/python-redsys/actions/workflows/ci.yml) [](https://codecov.io/gh/ddiazpinto/python-redsys) [](https://pypi.org/project/python-redsys/)  [](https://www.apache.org/licenses/LICENSE-2.0)
|
|
33
|
-
|
|
34
|
-
A simple, lightweight client to handle payments through the RedSys platform, covering both connection types it defines: *redirect connection* (browser form POST) and *direct connection* (server-to-server REST).
|
|
35
|
-
|
|
36
|
-
The library builds the signed request and verifies and parses the response. The redirect form is submitted by the cardholder's browser; the direct REST call is made by the integrator's HTTP client or by the optional `http` extra (`httpx`).
|
|
37
|
-
|
|
38
|
-
For Django, see [django-redsys](https://github.com/ddiazpinto/django-redsys).
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
pip install python-redsys
|
|
44
|
-
|
|
45
|
-
# optional: the direct-connection sender (httpx)
|
|
46
|
-
pip install "python-redsys[http]"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Requires Python 3.10+. Runtime dependencies: `cryptography` and `pydantic` (v2).
|
|
50
|
-
|
|
51
|
-
## Upgrading from 1.x
|
|
52
|
-
|
|
53
|
-
2.0 has a new API. The [migration guide](docs/migration-1x-to-2x.md) lists every change; 1.x receives fixes until 2026-12-31.
|
|
54
|
-
|
|
55
|
-
## Usage
|
|
56
|
-
|
|
57
|
-
The client is the single entry point: an operation method (`payment`, `preauthorization`, `confirmation`, `refund`, `cancellation`) returns a `Request`, with `.to_dict()` for the redirect POST fields and `.send()` for the direct REST call. Fields are validated on construction; `amount` is a `Decimal`. A parameter the library does not model yet is passed by its wire name (`Ds_Merchant_...`), sent as given, with an `UnknownParameterWarning`.
|
|
58
|
-
|
|
59
|
-
## Using *redirect connection*
|
|
60
|
-
|
|
61
|
-
### 1. Build a signed request
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
from decimal import Decimal, ROUND_HALF_UP
|
|
65
|
-
from redsys import currencies
|
|
66
|
-
from redsys.clients import RedirectClient
|
|
67
|
-
|
|
68
|
-
client = RedirectClient(
|
|
69
|
-
secret_key="123456789abcdef",
|
|
70
|
-
merchant_code="100000001",
|
|
71
|
-
terminal=1,
|
|
72
|
-
sandbox=False,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
post_parameters = client.payment(
|
|
76
|
-
order="000000001",
|
|
77
|
-
currency=currencies.EUR,
|
|
78
|
-
# The amount is a Decimal; pre-quantize to two decimals.
|
|
79
|
-
amount=Decimal("10.56489").quantize(Decimal(".01"), ROUND_HALF_UP),
|
|
80
|
-
merchant_url="https://example.com/redsys/response",
|
|
81
|
-
url_ok="https://example.com/ok",
|
|
82
|
-
url_ko="https://example.com/ko",
|
|
83
|
-
merchant_name="Example Commerce",
|
|
84
|
-
product_description="Products of Example Commerce",
|
|
85
|
-
).to_dict() # Ds_SignatureVersion / Ds_MerchantParameters / Ds_Signature
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Bizum: add `pay_methods="z"` (and optionally `bizum_mobile_number`) to the payment.
|
|
89
|
-
|
|
90
|
-
### 2. Communication step
|
|
91
|
-
|
|
92
|
-
Auto-submit an HTML form (`method="POST"`) to `client.endpoint` with the three `post_parameters` fields. RedSys notifies the result both as a server-to-server POST to `merchant_url` and as a browser redirect to `url_ok` / `url_ko`.
|
|
93
|
-
|
|
94
|
-
### 3. Verify and read the response
|
|
95
|
-
|
|
96
|
-
`parse_response()` accepts the received parameters (callback form fields or REST JSON body) and raises `ValueError` on signature mismatch.
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
response = client.parse_response(received) # received: the Ds_* dict RedSys posted back
|
|
100
|
-
|
|
101
|
-
if response.is_paid():
|
|
102
|
-
... # actions after a successful payment
|
|
103
|
-
else:
|
|
104
|
-
raise Exception(f"declined with code {response.response_code}")
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Response predicates (per the RedSys documentation):
|
|
108
|
-
|
|
109
|
-
- `response.is_paid()`: response code between 0 and 99.
|
|
110
|
-
- `response.is_canceled()`: response code 400.
|
|
111
|
-
- `response.is_refunded()`: response code 900.
|
|
112
|
-
- `response.is_authorized()`: paid, refunded or canceled.
|
|
113
|
-
|
|
114
|
-
`response.response_code` is the raw RedSys code; meanings are listed in [`docs/response-codes.md`](docs/response-codes.md). Decoded fields (`response.order`, `response.amount` as a `Decimal`, ...) read off the model; parameters the library does not list are kept under their wire name in `response.model_extra`.
|
|
115
|
-
|
|
116
|
-
## Using *direct connection* (REST)
|
|
117
|
-
|
|
118
|
-
The direct connection exchanges the same signed envelope server-to-server as JSON. Build the operation with the same methods: `.send()` performs the call, `.to_dict()` returns the JSON body for your own HTTP client.
|
|
119
|
-
|
|
120
|
-
With the optional sender (`pip install "python-redsys[http]"`):
|
|
121
|
-
|
|
122
|
-
```python
|
|
123
|
-
from decimal import Decimal
|
|
124
|
-
from redsys import currencies
|
|
125
|
-
from redsys.clients import DirectClient
|
|
126
|
-
|
|
127
|
-
client = DirectClient(secret_key="123456789abcdef", merchant_code="100000001",
|
|
128
|
-
terminal=1, sandbox=True)
|
|
129
|
-
|
|
130
|
-
response = client.refund(
|
|
131
|
-
order="000000001", amount=Decimal("10.56"), currency=currencies.EUR,
|
|
132
|
-
).send() # signs, POSTs, verifies, parses
|
|
133
|
-
response.is_refunded()
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Asynchronous (`httpx.AsyncClient`), with `send()` awaitable:
|
|
137
|
-
|
|
138
|
-
```python
|
|
139
|
-
from redsys.clients import AsyncDirectClient
|
|
140
|
-
|
|
141
|
-
client = AsyncDirectClient(secret_key="123456789abcdef", merchant_code="100000001",
|
|
142
|
-
terminal=1, sandbox=True)
|
|
143
|
-
response = await client.payment(
|
|
144
|
-
order="000000001", amount=Decimal("10.56"), currency=currencies.EUR,
|
|
145
|
-
pan="...", expiry_date="2512", cvv2="...",
|
|
146
|
-
).send()
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Transport-agnostic (do your own HTTP):
|
|
150
|
-
|
|
151
|
-
```python
|
|
152
|
-
body = client.refund(order="000000001", amount=Decimal("10.56"),
|
|
153
|
-
currency=currencies.EUR).to_dict() # JSON body to POST
|
|
154
|
-
# ... POST body to client.endpoint with your HTTP client ...
|
|
155
|
-
response = client.parse_response(received_json) # verifies and parses
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
`parse_response()` raises `redsys.exceptions.ResponseError` when RedSys answers with an error code instead of a signed result.
|
|
159
|
-
|
|
160
|
-
## inSite (hosted fields)
|
|
161
|
-
|
|
162
|
-
inSite captures the card in the browser via RedSys's JavaScript and yields an `idoper` token, keeping the PAN off your server. The card-capture JS is the front-end's responsibility; the backend completes the operation as a direct payment carrying the token:
|
|
163
|
-
|
|
164
|
-
```python
|
|
165
|
-
client.payment(order="000000001", amount=Decimal("10.56"), currency=currencies.EUR,
|
|
166
|
-
idoper="<token from the inSite JS>").send()
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Tokenization and recurring (COF / MIT)
|
|
170
|
-
|
|
171
|
-
Store a card on the first payment and charge it later without card data. The first payment requests a token and flags the credential-on-file intent; RedSys returns a token and a COF transaction id that subsequent merchant-initiated charges reuse.
|
|
172
|
-
|
|
173
|
-
```python
|
|
174
|
-
from decimal import Decimal
|
|
175
|
-
from redsys import cof, currencies, sca
|
|
176
|
-
from redsys.clients import DirectClient
|
|
177
|
-
|
|
178
|
-
client = DirectClient(secret_key="123456789abcdef", merchant_code="100000001",
|
|
179
|
-
terminal=1, sandbox=True)
|
|
180
|
-
|
|
181
|
-
# 1. First payment, request a token (cardholder present)
|
|
182
|
-
response = client.payment(
|
|
183
|
-
order="000000001", amount=Decimal("10.56"), currency=currencies.EUR,
|
|
184
|
-
pan="...", expiry_date="YYMM", cvv2="...",
|
|
185
|
-
identifier=cof.REQUEST_TOKEN, cof_ini=cof.INITIAL, cof_type=cof.RECURRING,
|
|
186
|
-
).send()
|
|
187
|
-
token, cof_txnid = response.identifier, response.cof_txnid
|
|
188
|
-
|
|
189
|
-
# 2. Later merchant-initiated charge, no card data
|
|
190
|
-
client.payment(
|
|
191
|
-
order="000000002", amount=Decimal("10.56"), currency=currencies.EUR,
|
|
192
|
-
identifier=token, cof_ini=cof.SUBSEQUENT, cof_type=cof.RECURRING, cof_txnid=cof_txnid,
|
|
193
|
-
excep_sca=sca.MIT, direct_payment="true",
|
|
194
|
-
).send()
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
`redsys.cof` provides the indicator constants (`INITIAL`/`SUBSEQUENT`, `RECURRING`/`INSTALLMENT`/...) and the `REQUEST_TOKEN` sentinel. The periodic fields (`sum_total`, `date_frequency`, `charge_expiry_date`) are also accepted by `DirectClient.payment()`. `redsys.sca` holds the PSD2 exemption codes for `excep_sca` (`LWV`, `TRA`, `MIT`, `COR`, `ATD`); `sca.QUERY` on `inicia()` asks which exemptions the card allows, returned in `response.excep_sca`.
|
|
198
|
-
|
|
199
|
-
## Signature versions
|
|
200
|
-
|
|
201
|
-
`signature_version` on the client constructor selects the scheme; the constants are in `redsys.clients`.
|
|
202
|
-
|
|
203
|
-
- `HMAC_SHA512_V2` (AES-128 + HMAC-SHA512): the default, as in RedSys's redirection guide and libraries.
|
|
204
|
-
- `HMAC_SHA512_V1`: the same algorithm with padded encoding, as in the REST guide.
|
|
205
|
-
- `HMAC_SHA256_V1` (3DES): legacy, still accepted by RedSys.
|
|
206
|
-
|
|
207
|
-
## Reference
|
|
208
|
-
|
|
209
|
-
- [`docs/response-codes.md`](docs/response-codes.md): response-code meanings.
|
|
210
|
-
- [`docs/redirect-connection.md`](docs/redirect-connection.md) / [`docs/direct-connection.md`](docs/direct-connection.md): connection-type protocol reference.
|
|
211
|
-
|
|
212
|
-
## Contributing
|
|
213
|
-
|
|
214
|
-
Dev setup, checks and live tests are in [`CONTRIBUTING.md`](CONTRIBUTING.md); contributors are listed in [`CONTRIBUTORS.md`](CONTRIBUTORS.md).
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
python_redsys-2.0.0.dist-info/licenses/LICENSE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360
|
|
2
|
-
python_redsys-2.0.0.dist-info/licenses/NOTICE,sha256=6tlv5QuzsbfmGxsQNEcN7Uopzyp7vgD54xb-_L4DGyE,201
|
|
3
|
-
redsys/__init__.py,sha256=bhpyzXB0Y-3FORBJlV4dZ3vnrz1jSc-sbZ9hyb7itFo,138
|
|
4
|
-
redsys/clients.py,sha256=4_lM6c6ofb13z_GNMa4d7pTCn2R6zbSRYrKJLfNdejA,10710
|
|
5
|
-
redsys/crypto.py,sha256=GOm8RkDYnD83fMNwey-bxLg3vWf6xbNzWbza6m23M9M,5480
|
|
6
|
-
redsys/exceptions.py,sha256=MqknsHm4sI5iAelrYcAuqSC70u1hhqdLyvA27vKcLHY,601
|
|
7
|
-
redsys/models.py,sha256=UzvCBrYgwixTS-b4PjMaeLCg7txNlWkcs0vmWeX94SM,15765
|
|
8
|
-
redsys/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
-
redsys/request.py,sha256=dVAQ0zSVmdyUPjnERo_62xilNQk3-LGJTbR6im_y0fQ,2408
|
|
10
|
-
redsys/threeds.py,sha256=IheD2mWVD9Pw7efRP6_a170LVppSSlKpyxhUJVI3LnU,2244
|
|
11
|
-
redsys/api/__init__.py,sha256=tjuisAfTqUpk7cdQq8ZayZk1cwaJ1ntqt1zgVbLamyw,111
|
|
12
|
-
redsys/api/cof.py,sha256=fEf9ugVvOMs-jAzXkXDQl4NshyZuSYvbYfqnRH8otrk,660
|
|
13
|
-
redsys/api/currencies.py,sha256=GufqxjAb941KMZ2S2mJJ980Xz4naJ2GgkcOVwuPx0ms,1043
|
|
14
|
-
redsys/api/languages.py,sha256=GPw7JB-CWDyGo6vhcPZDzpjGuwkojFCz1gyfx13wlek,872
|
|
15
|
-
redsys/api/sca.py,sha256=09b9LV9dvBADDTfHnReMR3OuM38ViVRqQs_hLQFBEoE,786
|
|
16
|
-
redsys/api/transactions.py,sha256=HYmaEXsfAb9-Kc9WlTGTetEA23vM1aGsQQua2Ukk3PM,732
|
|
17
|
-
python_redsys-2.0.0.dist-info/METADATA,sha256=lWRxu0v6tYC3RHfS3Rp29hXH8zL6c6scMm7anIUVY3k,9793
|
|
18
|
-
python_redsys-2.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
19
|
-
python_redsys-2.0.0.dist-info/top_level.txt,sha256=79TrHj--FP5SDyZO42-NUsiYJmHiQX42txZigHOqghY,7
|
|
20
|
-
python_redsys-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|