prava-sdk 0.1.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.
- prava_sdk-0.1.0/.gitignore +12 -0
- prava_sdk-0.1.0/LICENSE +21 -0
- prava_sdk-0.1.0/PKG-INFO +236 -0
- prava_sdk-0.1.0/README.md +180 -0
- prava_sdk-0.1.0/docs/architecture.md +44 -0
- prava_sdk-0.1.0/docs/assets/prava-payments-logo.svg +4 -0
- prava_sdk-0.1.0/docs/examples.md +31 -0
- prava_sdk-0.1.0/docs/getting-started.md +67 -0
- prava_sdk-0.1.0/docs/guides/async.md +50 -0
- prava_sdk-0.1.0/docs/guides/cards.md +49 -0
- prava_sdk-0.1.0/docs/guides/payment-flow.md +128 -0
- prava_sdk-0.1.0/docs/index.md +3 -0
- prava_sdk-0.1.0/docs/javascripts/site.js +84 -0
- prava_sdk-0.1.0/docs/reference/client.md +48 -0
- prava_sdk-0.1.0/docs/reference/errors.md +45 -0
- prava_sdk-0.1.0/docs/reference/models.md +57 -0
- prava_sdk-0.1.0/docs/security.md +32 -0
- prava_sdk-0.1.0/docs/stylesheets/extra.css +1697 -0
- prava_sdk-0.1.0/examples/__init__.py +1 -0
- prava_sdk-0.1.0/examples/async_payment_flow.py +71 -0
- prava_sdk-0.1.0/examples/complete_payment.py +91 -0
- prava_sdk-0.1.0/examples/create_embedded_session.py +73 -0
- prava_sdk-0.1.0/examples/create_hosted_session.py +73 -0
- prava_sdk-0.1.0/examples/manage_cards.py +48 -0
- prava_sdk-0.1.0/mkdocs.yml +52 -0
- prava_sdk-0.1.0/pyproject.toml +88 -0
- prava_sdk-0.1.0/src/prava_sdk/__init__.py +94 -0
- prava_sdk-0.1.0/src/prava_sdk/_config.py +91 -0
- prava_sdk-0.1.0/src/prava_sdk/_transport.py +176 -0
- prava_sdk-0.1.0/src/prava_sdk/_version.py +1 -0
- prava_sdk-0.1.0/src/prava_sdk/client.py +116 -0
- prava_sdk-0.1.0/src/prava_sdk/errors.py +134 -0
- prava_sdk-0.1.0/src/prava_sdk/models/__init__.py +71 -0
- prava_sdk-0.1.0/src/prava_sdk/models/_base.py +66 -0
- prava_sdk-0.1.0/src/prava_sdk/models/cards.py +72 -0
- prava_sdk-0.1.0/src/prava_sdk/models/enums.py +58 -0
- prava_sdk-0.1.0/src/prava_sdk/models/sessions.py +162 -0
- prava_sdk-0.1.0/src/prava_sdk/py.typed +1 -0
- prava_sdk-0.1.0/src/prava_sdk/resources/__init__.py +11 -0
- prava_sdk-0.1.0/src/prava_sdk/resources/_base.py +101 -0
- prava_sdk-0.1.0/src/prava_sdk/resources/cards.py +124 -0
- prava_sdk-0.1.0/src/prava_sdk/resources/sessions.py +314 -0
- prava_sdk-0.1.0/tests/__init__.py +1 -0
- prava_sdk-0.1.0/tests/conftest.py +104 -0
- prava_sdk-0.1.0/tests/test_async_client.py +141 -0
- prava_sdk-0.1.0/tests/test_client.py +329 -0
- prava_sdk-0.1.0/tests/test_examples.py +16 -0
- prava_sdk-0.1.0/tests/test_models.py +128 -0
- prava_sdk-0.1.0/uv.lock +1038 -0
prava_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Prava Payments
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
prava_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prava-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python server-side SDK for Prava Payments
|
|
5
|
+
Project-URL: Documentation, https://prava-sdk.prajwalsuryawanshi.in
|
|
6
|
+
Project-URL: Homepage, https://www.prava.space
|
|
7
|
+
Project-URL: Issues, https://github.com/prajwalsuryawanshi/prava-payments/issues
|
|
8
|
+
Project-URL: Source, https://github.com/prajwalsuryawanshi/prava-payments
|
|
9
|
+
Author-email: Prava Payments <support@prava.space>
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Prava Payments
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: agentic-commerce,payments,prava,sdk
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Operating System :: OS Independent
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Typing :: Typed
|
|
43
|
+
Requires-Python: >=3.10
|
|
44
|
+
Requires-Dist: httpx<1,>=0.27
|
|
45
|
+
Requires-Dist: pydantic[email]<3,>=2.7
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: build!=1.5.1,<2,>=1.2; extra == 'dev'
|
|
48
|
+
Requires-Dist: hatchling<2,>=1.25; extra == 'dev'
|
|
49
|
+
Requires-Dist: mkdocs<2,>=1.6; extra == 'dev'
|
|
50
|
+
Requires-Dist: mypy<2,>=1.11; extra == 'dev'
|
|
51
|
+
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
|
|
52
|
+
Requires-Dist: pytest<10,>=8; extra == 'dev'
|
|
53
|
+
Requires-Dist: respx<1,>=0.21; extra == 'dev'
|
|
54
|
+
Requires-Dist: ruff<1,>=0.6; extra == 'dev'
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
# Prava Python SDK
|
|
58
|
+
|
|
59
|
+
Typed server-side Python SDK for [Prava Payments](https://www.prava.space).
|
|
60
|
+
|
|
61
|
+
[Documentation](https://prava-sdk.prajwalsuryawanshi.in) ·
|
|
62
|
+
[Source](https://github.com/prajwalsuryawanshi/prava-payments)
|
|
63
|
+
|
|
64
|
+
The SDK creates payment sessions, manages enrolled cards, retrieves one-time payment
|
|
65
|
+
credentials, and reports checkout outcomes. Card collection still happens on Prava's hosted
|
|
66
|
+
surface or through the browser [`@prava-sdk/core`](https://www.npmjs.com/package/@prava-sdk/core)
|
|
67
|
+
package—your Python server must never collect a cardholder's raw card number.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install prava-sdk
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Python 3.10 or newer is required.
|
|
76
|
+
|
|
77
|
+
The complete SDK guide is available in [docs/](docs/index.md), with runnable programs in
|
|
78
|
+
[examples/](examples/).
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from decimal import Decimal
|
|
84
|
+
|
|
85
|
+
from prava_sdk import PravaClient
|
|
86
|
+
|
|
87
|
+
client = PravaClient("sk_test_xxx")
|
|
88
|
+
|
|
89
|
+
session = client.sessions.create(
|
|
90
|
+
user_id="user_123",
|
|
91
|
+
user_email="buyer@example.com",
|
|
92
|
+
total_amount=Decimal("49.99"),
|
|
93
|
+
currency="USD",
|
|
94
|
+
purchase_context=[
|
|
95
|
+
{
|
|
96
|
+
"merchant_details": {
|
|
97
|
+
"name": "Example Store",
|
|
98
|
+
"url": "https://merchant.example",
|
|
99
|
+
"country_code_iso2": "US",
|
|
100
|
+
},
|
|
101
|
+
"product_details": [
|
|
102
|
+
{
|
|
103
|
+
"description": "Example product",
|
|
104
|
+
"unit_price": "49.99",
|
|
105
|
+
"product_id": "sku_123",
|
|
106
|
+
"quantity": 1,
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
integration_type="full_checkout",
|
|
112
|
+
callback_url="https://app.example/payments/complete",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Send the cardholder to session.iframe_url. After card entry and passkey approval:
|
|
116
|
+
result = client.sessions.wait_for_payment_result(session.session_id)
|
|
117
|
+
line_item = result.transactions[0].line_items[0]
|
|
118
|
+
|
|
119
|
+
card_number = line_item.token.get_secret_value() if line_item.token else None
|
|
120
|
+
dynamic_cvv = line_item.dynamic_cvv.get_secret_value() if line_item.dynamic_cvv else None
|
|
121
|
+
|
|
122
|
+
# Use the one-time credentials at the merchant checkout, then always report the outcome.
|
|
123
|
+
client.sessions.report_status(
|
|
124
|
+
session.session_id,
|
|
125
|
+
txn_ref_id=line_item.txn_ref_id,
|
|
126
|
+
txn_status="APPROVED",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
client.close()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The secret key can instead be supplied through `PRAVA_SECRET_KEY`. `sk_test_*` keys automatically
|
|
133
|
+
use `https://sandbox.api.prava.space`; `sk_live_*` keys use `https://api.prava.space`.
|
|
134
|
+
|
|
135
|
+
Use the client as a context manager to guarantee cleanup:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from prava_sdk import PravaClient
|
|
139
|
+
|
|
140
|
+
with PravaClient("sk_test_xxx") as client:
|
|
141
|
+
cards = client.cards.list(customer_id="user_123")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Async client
|
|
145
|
+
|
|
146
|
+
`AsyncPravaClient` exposes the same grouped resources and method arguments:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from prava_sdk import AsyncPravaClient
|
|
150
|
+
|
|
151
|
+
async with AsyncPravaClient("sk_test_xxx") as client:
|
|
152
|
+
result = await client.sessions.wait_for_payment_result(
|
|
153
|
+
"ses_123",
|
|
154
|
+
timeout=300,
|
|
155
|
+
poll_interval=2,
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## API
|
|
160
|
+
|
|
161
|
+
### Sessions
|
|
162
|
+
|
|
163
|
+
- `client.sessions.create(...)`
|
|
164
|
+
- `client.sessions.revoke(session_id)`
|
|
165
|
+
- `client.sessions.get_payment_result(session_id)`
|
|
166
|
+
- `client.sessions.wait_for_payment_result(session_id, timeout=300, poll_interval=2)`
|
|
167
|
+
- `client.sessions.report_status(session_id, ...)`
|
|
168
|
+
|
|
169
|
+
The polling helper returns as soon as the result status becomes `awaiting_result`, `completed`, or
|
|
170
|
+
`failed`. It raises `PravaPollingTimeoutError` if the session remains `pending` past the timeout.
|
|
171
|
+
|
|
172
|
+
### Cards
|
|
173
|
+
|
|
174
|
+
- `client.cards.list(customer_id=..., status="active", include_card_art=False)`
|
|
175
|
+
- `client.cards.delete(customer_id=..., card_id=..., reason="OTHER")`
|
|
176
|
+
|
|
177
|
+
Only non-sensitive card metadata is returned by card-management endpoints.
|
|
178
|
+
|
|
179
|
+
## Errors
|
|
180
|
+
|
|
181
|
+
All failures derive from `PravaError`. API exceptions expose `code`, `message`, `status_code`,
|
|
182
|
+
`details`, and `response_id` (from the `X-Response-ID` response header):
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from prava_sdk import PravaError
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
client.sessions.revoke("ses_missing")
|
|
189
|
+
except PravaError as exc:
|
|
190
|
+
print(exc.code, exc.response_id)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The SDK provides dedicated authentication, validation, not-found, invalid-state, rate-limit,
|
|
194
|
+
transport, request-timeout, and polling-timeout exception types. Requests are not retried
|
|
195
|
+
automatically because the API does not currently document idempotency keys.
|
|
196
|
+
|
|
197
|
+
## Security
|
|
198
|
+
|
|
199
|
+
- Never use `sk_test_*` or `sk_live_*` keys in browsers, mobile apps, logs, or version control.
|
|
200
|
+
- Never collect or send a cardholder's underlying card number through this SDK.
|
|
201
|
+
- Payment-result `token` and `dynamic_cvv` values are Pydantic `SecretStr` fields and are redacted
|
|
202
|
+
from normal model representations. Retrieve them only immediately before checkout.
|
|
203
|
+
- Always call `report_status` with `APPROVED` or `DECLINED` after attempting checkout.
|
|
204
|
+
|
|
205
|
+
See the [API reference](https://docs.prava.space/api-reference/overview),
|
|
206
|
+
[error catalog](https://docs.prava.space/api-reference/errors), and
|
|
207
|
+
[sandbox guide](https://docs.prava.space/api-reference/testing) for the complete server contract.
|
|
208
|
+
|
|
209
|
+
## Package architecture
|
|
210
|
+
|
|
211
|
+
The SDK is organized as a standalone Python library:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
prava_sdk/
|
|
215
|
+
├── client.py # Small public sync and async client facades
|
|
216
|
+
├── resources/ # Session and card endpoint groups
|
|
217
|
+
│ └── _base.py # Shared resource contracts and validation boundary
|
|
218
|
+
├── models/ # Pydantic requests, responses, and enums
|
|
219
|
+
├── _config.py # Secret-key and environment resolution
|
|
220
|
+
├── _transport.py # Shared HTTP, JSON, and response handling
|
|
221
|
+
└── errors.py # Public exception hierarchy
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The JavaScript SDK is not a runtime or build dependency. It only handles browser-side card
|
|
225
|
+
collection and is excluded from Python source and wheel distributions.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
uv sync --extra dev
|
|
231
|
+
uv run ruff check .
|
|
232
|
+
uv run mypy
|
|
233
|
+
uv run pytest
|
|
234
|
+
uv run mkdocs build --strict
|
|
235
|
+
uv run python -m build
|
|
236
|
+
```
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Prava Python SDK
|
|
2
|
+
|
|
3
|
+
Typed server-side Python SDK for [Prava Payments](https://www.prava.space).
|
|
4
|
+
|
|
5
|
+
[Documentation](https://prava-sdk.prajwalsuryawanshi.in) ·
|
|
6
|
+
[Source](https://github.com/prajwalsuryawanshi/prava-payments)
|
|
7
|
+
|
|
8
|
+
The SDK creates payment sessions, manages enrolled cards, retrieves one-time payment
|
|
9
|
+
credentials, and reports checkout outcomes. Card collection still happens on Prava's hosted
|
|
10
|
+
surface or through the browser [`@prava-sdk/core`](https://www.npmjs.com/package/@prava-sdk/core)
|
|
11
|
+
package—your Python server must never collect a cardholder's raw card number.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install prava-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Python 3.10 or newer is required.
|
|
20
|
+
|
|
21
|
+
The complete SDK guide is available in [docs/](docs/index.md), with runnable programs in
|
|
22
|
+
[examples/](examples/).
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from decimal import Decimal
|
|
28
|
+
|
|
29
|
+
from prava_sdk import PravaClient
|
|
30
|
+
|
|
31
|
+
client = PravaClient("sk_test_xxx")
|
|
32
|
+
|
|
33
|
+
session = client.sessions.create(
|
|
34
|
+
user_id="user_123",
|
|
35
|
+
user_email="buyer@example.com",
|
|
36
|
+
total_amount=Decimal("49.99"),
|
|
37
|
+
currency="USD",
|
|
38
|
+
purchase_context=[
|
|
39
|
+
{
|
|
40
|
+
"merchant_details": {
|
|
41
|
+
"name": "Example Store",
|
|
42
|
+
"url": "https://merchant.example",
|
|
43
|
+
"country_code_iso2": "US",
|
|
44
|
+
},
|
|
45
|
+
"product_details": [
|
|
46
|
+
{
|
|
47
|
+
"description": "Example product",
|
|
48
|
+
"unit_price": "49.99",
|
|
49
|
+
"product_id": "sku_123",
|
|
50
|
+
"quantity": 1,
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
integration_type="full_checkout",
|
|
56
|
+
callback_url="https://app.example/payments/complete",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Send the cardholder to session.iframe_url. After card entry and passkey approval:
|
|
60
|
+
result = client.sessions.wait_for_payment_result(session.session_id)
|
|
61
|
+
line_item = result.transactions[0].line_items[0]
|
|
62
|
+
|
|
63
|
+
card_number = line_item.token.get_secret_value() if line_item.token else None
|
|
64
|
+
dynamic_cvv = line_item.dynamic_cvv.get_secret_value() if line_item.dynamic_cvv else None
|
|
65
|
+
|
|
66
|
+
# Use the one-time credentials at the merchant checkout, then always report the outcome.
|
|
67
|
+
client.sessions.report_status(
|
|
68
|
+
session.session_id,
|
|
69
|
+
txn_ref_id=line_item.txn_ref_id,
|
|
70
|
+
txn_status="APPROVED",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
client.close()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The secret key can instead be supplied through `PRAVA_SECRET_KEY`. `sk_test_*` keys automatically
|
|
77
|
+
use `https://sandbox.api.prava.space`; `sk_live_*` keys use `https://api.prava.space`.
|
|
78
|
+
|
|
79
|
+
Use the client as a context manager to guarantee cleanup:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from prava_sdk import PravaClient
|
|
83
|
+
|
|
84
|
+
with PravaClient("sk_test_xxx") as client:
|
|
85
|
+
cards = client.cards.list(customer_id="user_123")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Async client
|
|
89
|
+
|
|
90
|
+
`AsyncPravaClient` exposes the same grouped resources and method arguments:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from prava_sdk import AsyncPravaClient
|
|
94
|
+
|
|
95
|
+
async with AsyncPravaClient("sk_test_xxx") as client:
|
|
96
|
+
result = await client.sessions.wait_for_payment_result(
|
|
97
|
+
"ses_123",
|
|
98
|
+
timeout=300,
|
|
99
|
+
poll_interval=2,
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API
|
|
104
|
+
|
|
105
|
+
### Sessions
|
|
106
|
+
|
|
107
|
+
- `client.sessions.create(...)`
|
|
108
|
+
- `client.sessions.revoke(session_id)`
|
|
109
|
+
- `client.sessions.get_payment_result(session_id)`
|
|
110
|
+
- `client.sessions.wait_for_payment_result(session_id, timeout=300, poll_interval=2)`
|
|
111
|
+
- `client.sessions.report_status(session_id, ...)`
|
|
112
|
+
|
|
113
|
+
The polling helper returns as soon as the result status becomes `awaiting_result`, `completed`, or
|
|
114
|
+
`failed`. It raises `PravaPollingTimeoutError` if the session remains `pending` past the timeout.
|
|
115
|
+
|
|
116
|
+
### Cards
|
|
117
|
+
|
|
118
|
+
- `client.cards.list(customer_id=..., status="active", include_card_art=False)`
|
|
119
|
+
- `client.cards.delete(customer_id=..., card_id=..., reason="OTHER")`
|
|
120
|
+
|
|
121
|
+
Only non-sensitive card metadata is returned by card-management endpoints.
|
|
122
|
+
|
|
123
|
+
## Errors
|
|
124
|
+
|
|
125
|
+
All failures derive from `PravaError`. API exceptions expose `code`, `message`, `status_code`,
|
|
126
|
+
`details`, and `response_id` (from the `X-Response-ID` response header):
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from prava_sdk import PravaError
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
client.sessions.revoke("ses_missing")
|
|
133
|
+
except PravaError as exc:
|
|
134
|
+
print(exc.code, exc.response_id)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The SDK provides dedicated authentication, validation, not-found, invalid-state, rate-limit,
|
|
138
|
+
transport, request-timeout, and polling-timeout exception types. Requests are not retried
|
|
139
|
+
automatically because the API does not currently document idempotency keys.
|
|
140
|
+
|
|
141
|
+
## Security
|
|
142
|
+
|
|
143
|
+
- Never use `sk_test_*` or `sk_live_*` keys in browsers, mobile apps, logs, or version control.
|
|
144
|
+
- Never collect or send a cardholder's underlying card number through this SDK.
|
|
145
|
+
- Payment-result `token` and `dynamic_cvv` values are Pydantic `SecretStr` fields and are redacted
|
|
146
|
+
from normal model representations. Retrieve them only immediately before checkout.
|
|
147
|
+
- Always call `report_status` with `APPROVED` or `DECLINED` after attempting checkout.
|
|
148
|
+
|
|
149
|
+
See the [API reference](https://docs.prava.space/api-reference/overview),
|
|
150
|
+
[error catalog](https://docs.prava.space/api-reference/errors), and
|
|
151
|
+
[sandbox guide](https://docs.prava.space/api-reference/testing) for the complete server contract.
|
|
152
|
+
|
|
153
|
+
## Package architecture
|
|
154
|
+
|
|
155
|
+
The SDK is organized as a standalone Python library:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
prava_sdk/
|
|
159
|
+
├── client.py # Small public sync and async client facades
|
|
160
|
+
├── resources/ # Session and card endpoint groups
|
|
161
|
+
│ └── _base.py # Shared resource contracts and validation boundary
|
|
162
|
+
├── models/ # Pydantic requests, responses, and enums
|
|
163
|
+
├── _config.py # Secret-key and environment resolution
|
|
164
|
+
├── _transport.py # Shared HTTP, JSON, and response handling
|
|
165
|
+
└── errors.py # Public exception hierarchy
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The JavaScript SDK is not a runtime or build dependency. It only handles browser-side card
|
|
169
|
+
collection and is excluded from Python source and wheel distributions.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
uv sync --extra dev
|
|
175
|
+
uv run ruff check .
|
|
176
|
+
uv run mypy
|
|
177
|
+
uv run pytest
|
|
178
|
+
uv run mkdocs build --strict
|
|
179
|
+
uv run python -m build
|
|
180
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
The project is a standalone Python library. Browser SDK files are reference material only and are
|
|
4
|
+
not runtime dependencies or package inputs.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
src/prava_sdk/
|
|
8
|
+
├── client.py
|
|
9
|
+
├── resources/
|
|
10
|
+
│ ├── _base.py
|
|
11
|
+
│ ├── sessions.py
|
|
12
|
+
│ └── cards.py
|
|
13
|
+
├── models/
|
|
14
|
+
│ ├── sessions.py
|
|
15
|
+
│ ├── cards.py
|
|
16
|
+
│ ├── enums.py
|
|
17
|
+
│ └── _base.py
|
|
18
|
+
├── _config.py
|
|
19
|
+
├── _transport.py
|
|
20
|
+
└── errors.py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Responsibilities
|
|
24
|
+
|
|
25
|
+
- `client.py` owns lifecycle and exposes grouped resources.
|
|
26
|
+
- `resources/` maps Python methods to REST paths and builds request models. Its internal base layer
|
|
27
|
+
defines structural transport protocols, so endpoint groups are independent of `httpx` and the
|
|
28
|
+
concrete transport implementation.
|
|
29
|
+
- `models/` validates requests and parses typed responses.
|
|
30
|
+
- `_config.py` resolves secret keys, environments, and base URLs.
|
|
31
|
+
- `_transport.py` owns `httpx` pools, authentication headers, JSON parsing, and transport failures.
|
|
32
|
+
- `errors.py` maps the common Prava error envelope into public exceptions.
|
|
33
|
+
|
|
34
|
+
Sync and async resources share model-building helpers but use distinct transport types. This keeps
|
|
35
|
+
the public APIs symmetrical without hiding blocking I/O inside async methods.
|
|
36
|
+
|
|
37
|
+
Resource methods translate local model errors into `PravaValidationError`; direct use of a public
|
|
38
|
+
Pydantic model still raises Pydantic's `ValidationError`. This keeps the client boundary consistent
|
|
39
|
+
while preserving normal Pydantic behavior for applications that use the schemas independently.
|
|
40
|
+
|
|
41
|
+
## Compatibility boundary
|
|
42
|
+
|
|
43
|
+
Supported public imports live in `prava_sdk`, `prava_sdk.models`, and the documented client resource
|
|
44
|
+
methods. Underscore-prefixed modules are internal and may change between releases.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="262" height="98" viewBox="0 0 262 98" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M68.452 42.6934L63.6249 39.4767C62.8242 36.9521 61.2403 34.7481 59.1028 33.1841C56.9654 31.6202 54.3854 30.7775 51.7369 30.7783H51.6766C44.8359 30.8116 39.269 36.4865 39.269 43.4311V47.533L21.7372 68.5724L21.7164 68.5974C21.3252 69.086 21.0798 69.6753 21.0086 70.2972C20.9374 70.9192 21.0433 71.5486 21.314 72.1131C21.5847 72.6776 22.0093 73.1541 22.5389 73.488C23.0685 73.8218 23.6816 73.9993 24.3077 74.0001H42.5938C48.3235 73.994 53.8168 71.7152 57.8683 67.6637C61.9198 63.6122 64.1986 58.1189 64.2047 52.3892V48.2914L68.452 45.4612C68.6801 45.3095 68.8671 45.1038 68.9964 44.8623C69.1257 44.6209 69.1934 44.3512 69.1934 44.0773C69.1934 43.8034 69.1257 43.5338 68.9964 43.2923C68.8671 43.0509 68.6801 42.8451 68.452 42.6934ZM45.5341 55.1155L33.0663 70.0769C32.9282 70.2494 32.757 70.3927 32.5628 70.4983C32.3686 70.6039 32.1553 70.6698 31.9354 70.692C31.7155 70.7141 31.4933 70.6922 31.2819 70.6275C31.0706 70.5628 30.8742 70.4566 30.7044 70.3151C30.5346 70.1736 30.3947 69.9997 30.2929 69.8035C30.1911 69.6073 30.1295 69.3927 30.1116 69.1724C30.0937 68.9521 30.12 68.7304 30.1888 68.5203C30.2576 68.3103 30.3677 68.116 30.5125 67.949L42.9803 52.9877C43.1184 52.8151 43.2896 52.6718 43.4838 52.5662C43.678 52.4606 43.8913 52.3948 44.1112 52.3726C44.3311 52.3504 44.5533 52.3723 44.7646 52.437C44.976 52.5017 45.1724 52.6079 45.3422 52.7494C45.512 52.8909 45.6519 53.0648 45.7537 53.2611C45.8555 53.4573 45.9171 53.6718 45.935 53.8921C45.9529 54.1125 45.9266 54.3341 45.8578 54.5442C45.7889 54.7543 45.6789 54.9485 45.5341 55.1155ZM53.3992 44.0773C52.906 44.0773 52.4239 43.9311 52.0139 43.6571C51.6038 43.3831 51.2842 42.9936 51.0955 42.538C50.9067 42.0824 50.8574 41.581 50.9536 41.0973C51.0498 40.6136 51.2873 40.1693 51.636 39.8205C51.9847 39.4718 52.4291 39.2343 52.9128 39.1381C53.3965 39.0419 53.8978 39.0913 54.3535 39.28C54.8091 39.4687 55.1986 39.7883 55.4725 40.1984C55.7465 40.6085 55.8928 41.0906 55.8928 41.5838C55.8928 42.2451 55.6301 42.8793 55.1624 43.347C54.6948 43.8146 54.0606 44.0773 53.3992 44.0773Z" fill="black"/>
|
|
3
|
+
<path d="M110.312 74V26.32H127.144C130.301 26.32 133.075 26.9173 135.464 28.112C137.853 29.3067 139.709 31.0133 141.032 33.232C142.397 35.4507 143.08 38.0533 143.08 41.04C143.08 44.0267 142.397 46.6293 141.032 48.848C139.709 51.024 137.853 52.7307 135.464 53.968C133.117 55.1627 130.344 55.76 127.144 55.76H116.584V74H110.312ZM116.584 50H127.336C129.256 50 130.92 49.6373 132.328 48.912C133.736 48.1867 134.824 47.1413 135.592 45.776C136.36 44.4107 136.744 42.832 136.744 41.04C136.744 39.2053 136.36 37.6267 135.592 36.304C134.824 34.9387 133.736 33.8933 132.328 33.168C130.92 32.4427 129.256 32.08 127.336 32.08H116.584V50ZM147.658 74V39.504H153.482V45.84L152.842 44.944C153.653 42.9813 154.89 41.5307 156.554 40.592C158.218 39.6107 160.245 39.12 162.634 39.12H164.746V44.752H161.738C159.306 44.752 157.343 45.52 155.85 47.056C154.357 48.5493 153.61 50.6827 153.61 53.456V74H147.658ZM176.945 74.768C174.684 74.768 172.678 74.3627 170.929 73.552C169.222 72.6987 167.878 71.5467 166.897 70.096C165.916 68.6027 165.425 66.896 165.425 64.976C165.425 63.1413 165.809 61.4987 166.577 60.048C167.388 58.5547 168.625 57.296 170.289 56.272C171.996 55.248 174.129 54.5227 176.689 54.096L189.489 51.984V56.976L178.033 58.896C175.814 59.28 174.193 59.984 173.169 61.008C172.188 62.032 171.697 63.2907 171.697 64.784C171.697 66.192 172.252 67.3653 173.361 68.304C174.513 69.2427 175.942 69.712 177.649 69.712C179.825 69.712 181.702 69.264 183.281 68.368C184.902 67.4293 186.161 66.1707 187.057 64.592C187.996 63.0133 188.465 61.264 188.465 59.344V50.576C188.465 48.6987 187.761 47.184 186.353 46.032C184.988 44.8373 183.174 44.24 180.913 44.24C178.95 44.24 177.201 44.752 175.665 45.776C174.172 46.7573 173.062 48.08 172.337 49.744L167.153 47.056C167.793 45.4773 168.817 44.0693 170.225 42.832C171.633 41.552 173.276 40.5493 175.153 39.824C177.03 39.0987 178.993 38.736 181.041 38.736C183.686 38.736 186.012 39.248 188.017 40.272C190.022 41.2533 191.58 42.64 192.689 44.432C193.841 46.1813 194.417 48.2293 194.417 50.576V74H188.593V67.472L189.681 67.856C188.956 69.2213 187.974 70.416 186.737 71.44C185.5 72.464 184.049 73.2747 182.385 73.872C180.721 74.4693 178.908 74.768 176.945 74.768ZM209.421 74L195.917 39.504H202.573L213.389 68.688H211.085L221.965 39.504H228.621L215.053 74H209.421ZM239.607 74.768C237.346 74.768 235.341 74.3627 233.591 73.552C231.885 72.6987 230.541 71.5467 229.559 70.096C228.578 68.6027 228.087 66.896 228.087 64.976C228.087 63.1413 228.471 61.4987 229.239 60.048C230.05 58.5547 231.287 57.296 232.951 56.272C234.658 55.248 236.791 54.5227 239.351 54.096L252.151 51.984V56.976L240.695 58.896C238.477 59.28 236.855 59.984 235.831 61.008C234.85 62.032 234.359 63.2907 234.359 64.784C234.359 66.192 234.914 67.3653 236.023 68.304C237.175 69.2427 238.605 69.712 240.311 69.712C242.487 69.712 244.365 69.264 245.943 68.368C247.565 67.4293 248.823 66.1707 249.719 64.592C250.658 63.0133 251.127 61.264 251.127 59.344V50.576C251.127 48.6987 250.423 47.184 249.015 46.032C247.65 44.8373 245.837 44.24 243.575 44.24C241.613 44.24 239.863 44.752 238.327 45.776C236.834 46.7573 235.725 48.08 234.999 49.744L229.815 47.056C230.455 45.4773 231.479 44.0693 232.887 42.832C234.295 41.552 235.938 40.5493 237.815 39.824C239.693 39.0987 241.655 38.736 243.703 38.736C246.349 38.736 248.674 39.248 250.679 40.272C252.685 41.2533 254.242 42.64 255.351 44.432C256.503 46.1813 257.079 48.2293 257.079 50.576V74H251.255V67.472L252.343 67.856C251.618 69.2213 250.637 70.416 249.399 71.44C248.162 72.464 246.711 73.2747 245.047 73.872C243.383 74.4693 241.57 74.768 239.607 74.768Z" fill="black"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
The repository includes task-focused programs under `examples/`:
|
|
4
|
+
|
|
5
|
+
| File | Demonstrates |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `create_hosted_session.py` | Create a hosted cardholder redirect. |
|
|
8
|
+
| `create_embedded_session.py` | Build the payload returned by a backend to its frontend. |
|
|
9
|
+
| `complete_payment.py` | Poll, invoke application checkout logic, and always report status. |
|
|
10
|
+
| `async_payment_flow.py` | Perform the payment orchestration with `AsyncPravaClient`. |
|
|
11
|
+
| `manage_cards.py` | List or delete enrolled cards from a CLI. |
|
|
12
|
+
|
|
13
|
+
Set a sandbox key before running an example:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export PRAVA_SECRET_KEY=sk_test_xxx
|
|
17
|
+
python examples/create_hosted_session.py \
|
|
18
|
+
--user-id user_123 \
|
|
19
|
+
--email buyer@example.com \
|
|
20
|
+
--callback-url https://app.example/payments/complete
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Display each program's arguments with `--help`:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python examples/manage_cards.py --help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The complete-payment examples intentionally require an application-provided merchant checkout
|
|
30
|
+
callback. Prava provides the one-time credential; submitting it to a specific merchant remains the
|
|
31
|
+
application's responsibility.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install prava-sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The package requires Python 3.10 or newer.
|
|
10
|
+
|
|
11
|
+
## Configure a secret key
|
|
12
|
+
|
|
13
|
+
Pass the key directly when constructing a client:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from prava_sdk import PravaClient
|
|
17
|
+
|
|
18
|
+
client = PravaClient("sk_test_xxx")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or set `PRAVA_SECRET_KEY` and omit the argument:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export PRAVA_SECRET_KEY=sk_test_xxx
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from prava_sdk import PravaClient
|
|
29
|
+
|
|
30
|
+
client = PravaClient()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Key prefixes select the API environment automatically:
|
|
34
|
+
|
|
35
|
+
| Prefix | Environment | Base URL |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `sk_test_*` | Sandbox | `https://sandbox.api.prava.space` |
|
|
38
|
+
| `sk_live_*` | Production | `https://api.prava.space` |
|
|
39
|
+
|
|
40
|
+
Passing a conflicting `environment` raises `PravaValidationError` before any network request.
|
|
41
|
+
|
|
42
|
+
## Manage the connection lifecycle
|
|
43
|
+
|
|
44
|
+
Prefer a context manager so the HTTP connection pool is always closed:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from prava_sdk import PravaClient
|
|
48
|
+
|
|
49
|
+
with PravaClient() as client:
|
|
50
|
+
response = client.cards.list(customer_id="user_123")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Long-running applications can create one client at startup, reuse it for requests, and call
|
|
54
|
+
`close()` during shutdown. Do not create a new client for every operation.
|
|
55
|
+
|
|
56
|
+
For async services, use `AsyncPravaClient` with `async with` or call `aclose()` during shutdown.
|
|
57
|
+
|
|
58
|
+
## Timeouts and custom transports
|
|
59
|
+
|
|
60
|
+
Set the HTTP timeout at client construction:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
client = PravaClient(timeout=20.0)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `base_url` and `transport` options exist for local testing and custom `httpx` transports. Normal
|
|
67
|
+
integrations should rely on key-based environment selection.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Async applications
|
|
2
|
+
|
|
3
|
+
`AsyncPravaClient` mirrors the synchronous API. It is appropriate for FastAPI, Starlette, aiohttp,
|
|
4
|
+
and other asyncio-based services.
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
from prava_sdk import AsyncPravaClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def retrieve_payment(session_id: str) -> None:
|
|
11
|
+
async with AsyncPravaClient() as client:
|
|
12
|
+
result = await client.sessions.wait_for_payment_result(
|
|
13
|
+
session_id,
|
|
14
|
+
timeout=300,
|
|
15
|
+
poll_interval=2,
|
|
16
|
+
)
|
|
17
|
+
print(result.status)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Application lifecycle
|
|
21
|
+
|
|
22
|
+
Create one client during application startup and close it during shutdown. For example, the relevant
|
|
23
|
+
FastAPI lifecycle pattern is:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from contextlib import asynccontextmanager
|
|
27
|
+
|
|
28
|
+
from fastapi import FastAPI
|
|
29
|
+
from prava_sdk import AsyncPravaClient
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@asynccontextmanager
|
|
33
|
+
async def lifespan(app: FastAPI):
|
|
34
|
+
app.state.prava = AsyncPravaClient()
|
|
35
|
+
try:
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
await app.state.prava.aclose()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app = FastAPI(lifespan=lifespan)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
FastAPI is not an SDK dependency; this snippet only demonstrates lifecycle management.
|
|
45
|
+
|
|
46
|
+
## Cancellation and polling
|
|
47
|
+
|
|
48
|
+
Async polling uses `asyncio.sleep`, so task cancellation is propagated normally. A
|
|
49
|
+
`PravaPollingTimeoutError` is raised when the configured overall timeout expires. The per-request
|
|
50
|
+
HTTP timeout is configured separately on `AsyncPravaClient(timeout=...)`.
|