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.
redsys/threeds.py CHANGED
@@ -1,20 +1,25 @@
1
- """Builders for the Ds_Merchant_EMV3DS object used in 3-D Secure v2 (REST direct connection).
1
+ """Builders for the ``Ds_Merchant_EMV3DS`` object of 3-D Secure v2 on the direct connection.
2
2
 
3
- Flow: inicia (`card_data`) -> authorize (`authentication_data`) -> [challenge] ->
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
- Each function returns the dict to assign to a request's `emv3ds` field, e.g.
5
+ client.payment(emv3ds=threeds.authentication_data(..., three_ds_comp_ind=emv3ds.METHOD_COMPLETED), ...)
6
+ """
8
7
 
9
- from redsys import threeds
8
+ from base64 import urlsafe_b64encode
9
+ from json import dumps
10
10
 
11
- response = client.inicia(..., emv3ds=threeds.card_data()).send()
12
- """
11
+ from redsys.api import emv3ds
13
12
 
14
13
 
15
14
  def card_data() -> dict:
16
- """EMV3DS payload for the iniciaPeticionREST call (step 1)."""
17
- return {'threeDSInfo': 'CardData'}
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 collects for the authorization."""
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='Y'
56
+ *, three_ds_server_trans_id, notification_url, protocol_version, browser, three_ds_comp_ind=emv3ds.METHOD_COMPLETED
52
57
  ) -> dict:
53
- """EMV3DS payload for the authorization call (step 3). `browser` is `browser_info(...)`."""
58
+ """``emv3ds`` of the authentication (step 3); ``browser`` is ``browser_info(...)``."""
54
59
  data = {
55
- 'threeDSInfo': 'AuthenticationData',
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
- """EMV3DS payload to finalize the authorization after the challenge (step 5)."""
67
- return {'threeDSInfo': 'ChallengeResponse', 'protocolVersion': protocol_version, 'cres': cres}
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
- [![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 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,,