altpay-py 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.
- altpay_py-0.1.0/LICENSE +21 -0
- altpay_py-0.1.0/PKG-INFO +170 -0
- altpay_py-0.1.0/README.md +138 -0
- altpay_py-0.1.0/altpay/__init__.py +119 -0
- altpay_py-0.1.0/altpay/__meta__.py +5 -0
- altpay_py-0.1.0/altpay/client/__init__.py +6 -0
- altpay_py-0.1.0/altpay/client/async_client.py +114 -0
- altpay_py-0.1.0/altpay/client/base.py +149 -0
- altpay_py-0.1.0/altpay/client/sync_client.py +149 -0
- altpay_py-0.1.0/altpay/credentials.py +40 -0
- altpay_py-0.1.0/altpay/enums.py +94 -0
- altpay_py-0.1.0/altpay/errors.py +246 -0
- altpay_py-0.1.0/altpay/methods/__init__.py +8 -0
- altpay_py-0.1.0/altpay/methods/account.py +54 -0
- altpay_py-0.1.0/altpay/methods/base.py +61 -0
- altpay_py-0.1.0/altpay/methods/invoices.py +257 -0
- altpay_py-0.1.0/altpay/methods/withdrawals.py +119 -0
- altpay_py-0.1.0/altpay/models.py +369 -0
- altpay_py-0.1.0/altpay/py.typed +0 -0
- altpay_py-0.1.0/altpay/signing.py +226 -0
- altpay_py-0.1.0/altpay/webhook.py +88 -0
- altpay_py-0.1.0/pyproject.toml +50 -0
altpay_py-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AltPayments
|
|
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.
|
altpay_py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: altpay-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the AltPay crypto-payments API (sync + async).
|
|
5
|
+
Project-URL: Homepage, https://altpay.money
|
|
6
|
+
Project-URL: Documentation, https://docs.altpay.money/
|
|
7
|
+
Project-URL: Source, https://github.com/altpaysdk/py-sdk
|
|
8
|
+
Author: AltPayments
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: altpay,api,crypto,payments,sdk
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx>=0.24
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy>=1.5; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
29
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# altpay-py
|
|
34
|
+
|
|
35
|
+
Official Python SDK for the [AltPay](https://altpay.money) crypto-payments API. Synchronous
|
|
36
|
+
and asynchronous clients, typed models, request signing and webhook verification in one
|
|
37
|
+
package. Built on `httpx` and `pydantic` v2.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install altpay-py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import altpay # the distribution is altpay-py; the import name is altpay
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from decimal import Decimal
|
|
51
|
+
from altpay import AltPay, Credentials
|
|
52
|
+
|
|
53
|
+
client = AltPay(Credentials(
|
|
54
|
+
merchant_id="YOUR_MERCHANT_ID",
|
|
55
|
+
api_key="vc_live_...",
|
|
56
|
+
api_secret="YOUR_API_SECRET",
|
|
57
|
+
))
|
|
58
|
+
|
|
59
|
+
invoice = client.invoices.create(
|
|
60
|
+
uuid="order-42", # your idempotency key / order reference
|
|
61
|
+
amount=Decimal("100.00"),
|
|
62
|
+
fiat_currency="USD",
|
|
63
|
+
url_callback="https://example.com/altpay/webhook",
|
|
64
|
+
)
|
|
65
|
+
print(invoice.url) # hosted checkout URL to redirect the payer to
|
|
66
|
+
|
|
67
|
+
# later, check on it
|
|
68
|
+
invoice = client.invoices.get(order_id=invoice.order_id)
|
|
69
|
+
print(invoice.status) # PaymentStatus.CREATED | PAYED | EXPIRED | ...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Async
|
|
73
|
+
|
|
74
|
+
The async client mirrors the sync one exactly; just `await` the calls.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from altpay import AsyncAltPay, Credentials
|
|
78
|
+
|
|
79
|
+
async with AsyncAltPay(creds) as client:
|
|
80
|
+
invoice = await client.invoices.create(
|
|
81
|
+
uuid="order-42", amount="100.00", fiat_currency="USD",
|
|
82
|
+
)
|
|
83
|
+
print(invoice.url)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What you can do
|
|
87
|
+
|
|
88
|
+
| Resource | Method | Endpoint |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `client.invoices` | `create(...)` | create an invoice |
|
|
91
|
+
| | `get(order_id=... \| uuid=...)` | fetch one invoice |
|
|
92
|
+
| | `list(status=..., cursor=..., limit=...)` | page through invoices |
|
|
93
|
+
| | `services()` | available methods, limits and fees |
|
|
94
|
+
| | `balance(fiat_currency=...)` | balance valued in one fiat, per asset |
|
|
95
|
+
| | `create_wallet(network=..., order_id=...)` | static deposit wallet |
|
|
96
|
+
| | `list_wallets(limit=..., offset=...)` | list static wallets |
|
|
97
|
+
| `client.account` | `get()` | merchant + API-key identity |
|
|
98
|
+
| | `balance()` | paid volume per method (native asset) |
|
|
99
|
+
| | `statistics()` | aggregate counts and USD volume |
|
|
100
|
+
|
|
101
|
+
## Webhooks
|
|
102
|
+
|
|
103
|
+
AltPay POSTs a signed `payment.updated` event to your `url_callback` when an invoice is
|
|
104
|
+
paid. **Always verify the signature before trusting the body**, and pass the *raw* request
|
|
105
|
+
bytes (not the re-serialized JSON).
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from altpay import WebhookVerifier
|
|
109
|
+
|
|
110
|
+
verifier = WebhookVerifier(WEBHOOK_SECRET, target="/altpay/webhook")
|
|
111
|
+
|
|
112
|
+
# inside your handler (framework-agnostic):
|
|
113
|
+
event = verifier.parse(raw_body, request_headers) # raises AuthenticationError on a bad sig
|
|
114
|
+
if event.status == "PAYED":
|
|
115
|
+
fulfil_order(event.merchant_reference)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`verifier.verify(raw_body, headers)` returns a bool if you prefer to branch yourself.
|
|
119
|
+
|
|
120
|
+
## Errors
|
|
121
|
+
|
|
122
|
+
Every failure is an `AltPayError`. Network problems raise `AltPayTransportError`; anything the
|
|
123
|
+
API rejected raises an `APIError` subclass keyed by HTTP status. Each carries the server's
|
|
124
|
+
machine-readable `detail`, a human `hint`, and the `request_id` for support.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from altpay import AuthenticationError, RateLimitError, ValidationError, NotFoundError
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
client.invoices.create(uuid="o1", amount="100", fiat_currency="USD")
|
|
131
|
+
except AuthenticationError as e:
|
|
132
|
+
... # bad credentials / clock skew / revoked key (HTTP 401, detail="invalid_signature")
|
|
133
|
+
except ValidationError as e:
|
|
134
|
+
... # a field failed validation (HTTP 400, detail="invalid_request")
|
|
135
|
+
except RateLimitError as e:
|
|
136
|
+
sleep(e.retry_after or 1) # HTTP 429
|
|
137
|
+
except NotFoundError as e:
|
|
138
|
+
... # HTTP 404
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The full catalogue, with every `detail` value and how to fix it, is at
|
|
142
|
+
<https://docs.altpay.money/docs/http-codes>.
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
AltPay(
|
|
148
|
+
credentials,
|
|
149
|
+
base_url="https://api.altpay.money", # override for staging
|
|
150
|
+
timeout=30.0, # per-request seconds
|
|
151
|
+
max_retries=2, # retry transient errors (5xx, 429, network) with backoff
|
|
152
|
+
http_client=my_httpx_client, # bring your own httpx.Client (proxies, custom TLS)
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Authentication, in brief
|
|
157
|
+
|
|
158
|
+
Each request is signed with HMAC-SHA256 over a canonical string of the merchant id, API key,
|
|
159
|
+
timestamp, nonce, body hash, method and path. The SDK does this for you; the secret never
|
|
160
|
+
leaves your process. If you ever need the primitives (custom transport, testing), they live
|
|
161
|
+
in `altpay.signing`. Full spec: <https://docs.altpay.money/docs/authentication>.
|
|
162
|
+
|
|
163
|
+
## Requirements
|
|
164
|
+
|
|
165
|
+
- Python 3.10+
|
|
166
|
+
- `httpx >= 0.24`, `pydantic >= 2.0`
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# altpay-py
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [AltPay](https://altpay.money) crypto-payments API. Synchronous
|
|
4
|
+
and asynchronous clients, typed models, request signing and webhook verification in one
|
|
5
|
+
package. Built on `httpx` and `pydantic` v2.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install altpay-py
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import altpay # the distribution is altpay-py; the import name is altpay
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from decimal import Decimal
|
|
19
|
+
from altpay import AltPay, Credentials
|
|
20
|
+
|
|
21
|
+
client = AltPay(Credentials(
|
|
22
|
+
merchant_id="YOUR_MERCHANT_ID",
|
|
23
|
+
api_key="vc_live_...",
|
|
24
|
+
api_secret="YOUR_API_SECRET",
|
|
25
|
+
))
|
|
26
|
+
|
|
27
|
+
invoice = client.invoices.create(
|
|
28
|
+
uuid="order-42", # your idempotency key / order reference
|
|
29
|
+
amount=Decimal("100.00"),
|
|
30
|
+
fiat_currency="USD",
|
|
31
|
+
url_callback="https://example.com/altpay/webhook",
|
|
32
|
+
)
|
|
33
|
+
print(invoice.url) # hosted checkout URL to redirect the payer to
|
|
34
|
+
|
|
35
|
+
# later, check on it
|
|
36
|
+
invoice = client.invoices.get(order_id=invoice.order_id)
|
|
37
|
+
print(invoice.status) # PaymentStatus.CREATED | PAYED | EXPIRED | ...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Async
|
|
41
|
+
|
|
42
|
+
The async client mirrors the sync one exactly; just `await` the calls.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from altpay import AsyncAltPay, Credentials
|
|
46
|
+
|
|
47
|
+
async with AsyncAltPay(creds) as client:
|
|
48
|
+
invoice = await client.invoices.create(
|
|
49
|
+
uuid="order-42", amount="100.00", fiat_currency="USD",
|
|
50
|
+
)
|
|
51
|
+
print(invoice.url)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What you can do
|
|
55
|
+
|
|
56
|
+
| Resource | Method | Endpoint |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `client.invoices` | `create(...)` | create an invoice |
|
|
59
|
+
| | `get(order_id=... \| uuid=...)` | fetch one invoice |
|
|
60
|
+
| | `list(status=..., cursor=..., limit=...)` | page through invoices |
|
|
61
|
+
| | `services()` | available methods, limits and fees |
|
|
62
|
+
| | `balance(fiat_currency=...)` | balance valued in one fiat, per asset |
|
|
63
|
+
| | `create_wallet(network=..., order_id=...)` | static deposit wallet |
|
|
64
|
+
| | `list_wallets(limit=..., offset=...)` | list static wallets |
|
|
65
|
+
| `client.account` | `get()` | merchant + API-key identity |
|
|
66
|
+
| | `balance()` | paid volume per method (native asset) |
|
|
67
|
+
| | `statistics()` | aggregate counts and USD volume |
|
|
68
|
+
|
|
69
|
+
## Webhooks
|
|
70
|
+
|
|
71
|
+
AltPay POSTs a signed `payment.updated` event to your `url_callback` when an invoice is
|
|
72
|
+
paid. **Always verify the signature before trusting the body**, and pass the *raw* request
|
|
73
|
+
bytes (not the re-serialized JSON).
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from altpay import WebhookVerifier
|
|
77
|
+
|
|
78
|
+
verifier = WebhookVerifier(WEBHOOK_SECRET, target="/altpay/webhook")
|
|
79
|
+
|
|
80
|
+
# inside your handler (framework-agnostic):
|
|
81
|
+
event = verifier.parse(raw_body, request_headers) # raises AuthenticationError on a bad sig
|
|
82
|
+
if event.status == "PAYED":
|
|
83
|
+
fulfil_order(event.merchant_reference)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`verifier.verify(raw_body, headers)` returns a bool if you prefer to branch yourself.
|
|
87
|
+
|
|
88
|
+
## Errors
|
|
89
|
+
|
|
90
|
+
Every failure is an `AltPayError`. Network problems raise `AltPayTransportError`; anything the
|
|
91
|
+
API rejected raises an `APIError` subclass keyed by HTTP status. Each carries the server's
|
|
92
|
+
machine-readable `detail`, a human `hint`, and the `request_id` for support.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from altpay import AuthenticationError, RateLimitError, ValidationError, NotFoundError
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
client.invoices.create(uuid="o1", amount="100", fiat_currency="USD")
|
|
99
|
+
except AuthenticationError as e:
|
|
100
|
+
... # bad credentials / clock skew / revoked key (HTTP 401, detail="invalid_signature")
|
|
101
|
+
except ValidationError as e:
|
|
102
|
+
... # a field failed validation (HTTP 400, detail="invalid_request")
|
|
103
|
+
except RateLimitError as e:
|
|
104
|
+
sleep(e.retry_after or 1) # HTTP 429
|
|
105
|
+
except NotFoundError as e:
|
|
106
|
+
... # HTTP 404
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The full catalogue, with every `detail` value and how to fix it, is at
|
|
110
|
+
<https://docs.altpay.money/docs/http-codes>.
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
AltPay(
|
|
116
|
+
credentials,
|
|
117
|
+
base_url="https://api.altpay.money", # override for staging
|
|
118
|
+
timeout=30.0, # per-request seconds
|
|
119
|
+
max_retries=2, # retry transient errors (5xx, 429, network) with backoff
|
|
120
|
+
http_client=my_httpx_client, # bring your own httpx.Client (proxies, custom TLS)
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Authentication, in brief
|
|
125
|
+
|
|
126
|
+
Each request is signed with HMAC-SHA256 over a canonical string of the merchant id, API key,
|
|
127
|
+
timestamp, nonce, body hash, method and path. The SDK does this for you; the secret never
|
|
128
|
+
leaves your process. If you ever need the primitives (custom transport, testing), they live
|
|
129
|
+
in `altpay.signing`. Full spec: <https://docs.altpay.money/docs/authentication>.
|
|
130
|
+
|
|
131
|
+
## Requirements
|
|
132
|
+
|
|
133
|
+
- Python 3.10+
|
|
134
|
+
- `httpx >= 0.24`, `pydantic >= 2.0`
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""AltPay - the official Python SDK for the AltPay crypto-payments API.
|
|
2
|
+
|
|
3
|
+
Quick start (sync)::
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from altpay import AltPay, Credentials
|
|
7
|
+
|
|
8
|
+
client = AltPay(Credentials(
|
|
9
|
+
merchant_id="YOUR_MERCHANT_ID",
|
|
10
|
+
api_key="vc_live_...",
|
|
11
|
+
api_secret="YOUR_API_SECRET",
|
|
12
|
+
))
|
|
13
|
+
invoice = client.invoices.create(
|
|
14
|
+
uuid="order-42",
|
|
15
|
+
amount=Decimal("100.00"),
|
|
16
|
+
fiat_currency="USD",
|
|
17
|
+
url_callback="https://example.com/altpay/webhook",
|
|
18
|
+
)
|
|
19
|
+
print(invoice.url)
|
|
20
|
+
|
|
21
|
+
Quick start (async)::
|
|
22
|
+
|
|
23
|
+
from altpay import AsyncAltPay, Credentials
|
|
24
|
+
|
|
25
|
+
async with AsyncAltPay(creds) as client:
|
|
26
|
+
invoice = await client.invoices.create(uuid="order-42", amount="100.00", fiat_currency="USD")
|
|
27
|
+
|
|
28
|
+
Verifying a webhook::
|
|
29
|
+
|
|
30
|
+
from altpay import WebhookVerifier
|
|
31
|
+
verifier = WebhookVerifier(webhook_secret, target="/altpay/webhook")
|
|
32
|
+
event = verifier.parse(raw_body, request_headers) # raises on a bad signature
|
|
33
|
+
|
|
34
|
+
Documentation: https://docs.altpay.money/docs
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from .__meta__ import __version__
|
|
38
|
+
from .client import AltPay, AsyncAltPay
|
|
39
|
+
from .credentials import Credentials
|
|
40
|
+
from .enums import FiatCurrency, MerchantStatus, PaymentMethod, PaymentStatus, WithdrawalStatus
|
|
41
|
+
from .errors import (
|
|
42
|
+
AltPayError,
|
|
43
|
+
AltPayTransportError,
|
|
44
|
+
APIError,
|
|
45
|
+
AuthenticationError,
|
|
46
|
+
ConflictError,
|
|
47
|
+
Forbidden,
|
|
48
|
+
NotFoundError,
|
|
49
|
+
PermissionError_,
|
|
50
|
+
RateLimitError,
|
|
51
|
+
ServerError,
|
|
52
|
+
ValidationError,
|
|
53
|
+
)
|
|
54
|
+
from .models import (
|
|
55
|
+
Account,
|
|
56
|
+
AssetBalance,
|
|
57
|
+
AssetBalanceItem,
|
|
58
|
+
Balance,
|
|
59
|
+
BalanceAsset,
|
|
60
|
+
Invoice,
|
|
61
|
+
InvoicePage,
|
|
62
|
+
MethodBalance,
|
|
63
|
+
MethodBalanceItem,
|
|
64
|
+
Service,
|
|
65
|
+
ServiceCommission,
|
|
66
|
+
Statistics,
|
|
67
|
+
Wallet,
|
|
68
|
+
WalletDeposit,
|
|
69
|
+
WebhookEvent,
|
|
70
|
+
Withdrawal,
|
|
71
|
+
WithdrawalFee,
|
|
72
|
+
)
|
|
73
|
+
from .webhook import WebhookVerifier
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
"__version__",
|
|
77
|
+
# Clients
|
|
78
|
+
"AltPay",
|
|
79
|
+
"AsyncAltPay",
|
|
80
|
+
"Credentials",
|
|
81
|
+
# Webhooks
|
|
82
|
+
"WebhookVerifier",
|
|
83
|
+
# Enums
|
|
84
|
+
"PaymentStatus",
|
|
85
|
+
"PaymentMethod",
|
|
86
|
+
"FiatCurrency",
|
|
87
|
+
"MerchantStatus",
|
|
88
|
+
"WithdrawalStatus",
|
|
89
|
+
# Models
|
|
90
|
+
"Invoice",
|
|
91
|
+
"InvoicePage",
|
|
92
|
+
"Service",
|
|
93
|
+
"ServiceCommission",
|
|
94
|
+
"Balance",
|
|
95
|
+
"BalanceAsset",
|
|
96
|
+
"AssetBalance",
|
|
97
|
+
"AssetBalanceItem",
|
|
98
|
+
"Wallet",
|
|
99
|
+
"WalletDeposit",
|
|
100
|
+
"Account",
|
|
101
|
+
"MethodBalance",
|
|
102
|
+
"MethodBalanceItem",
|
|
103
|
+
"Statistics",
|
|
104
|
+
"Withdrawal",
|
|
105
|
+
"WithdrawalFee",
|
|
106
|
+
"WebhookEvent",
|
|
107
|
+
# Errors
|
|
108
|
+
"AltPayError",
|
|
109
|
+
"AltPayTransportError",
|
|
110
|
+
"APIError",
|
|
111
|
+
"AuthenticationError",
|
|
112
|
+
"Forbidden",
|
|
113
|
+
"PermissionError_",
|
|
114
|
+
"NotFoundError",
|
|
115
|
+
"ValidationError",
|
|
116
|
+
"ConflictError",
|
|
117
|
+
"RateLimitError",
|
|
118
|
+
"ServerError",
|
|
119
|
+
]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""The asynchronous client, :class:`AsyncAltPay`.
|
|
2
|
+
|
|
3
|
+
The async twin of :class:`~altpay.client.sync_client.AltPay`. Same API, same resources, same
|
|
4
|
+
request/response logic from :mod:`altpay.client.base`; only the I/O is awaited.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from typing import Any, TypeVar
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from ..credentials import Credentials
|
|
15
|
+
from ..errors import AltPayTransportError, RateLimitError, ServerError
|
|
16
|
+
from ..methods.account import AccountResource
|
|
17
|
+
from ..methods.base import APICall
|
|
18
|
+
from ..methods.invoices import Invoices
|
|
19
|
+
from ..methods.withdrawals import Withdrawals
|
|
20
|
+
from .base import parse_response, prepare
|
|
21
|
+
from .sync_client import DEFAULT_BASE_URL, _backoff, _retry_delay
|
|
22
|
+
|
|
23
|
+
T = TypeVar("T")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AsyncAltPay:
|
|
27
|
+
"""Asynchronous AltPay API client.
|
|
28
|
+
|
|
29
|
+
Example::
|
|
30
|
+
|
|
31
|
+
from decimal import Decimal
|
|
32
|
+
from altpay import AsyncAltPay, Credentials
|
|
33
|
+
|
|
34
|
+
async with AsyncAltPay(Credentials(
|
|
35
|
+
merchant_id="...", api_key="vc_live_...", api_secret="...",
|
|
36
|
+
)) as client:
|
|
37
|
+
invoice = await client.invoices.create(
|
|
38
|
+
uuid="order-1", amount=Decimal("100.00"), fiat_currency="USD",
|
|
39
|
+
)
|
|
40
|
+
print(invoice.url)
|
|
41
|
+
|
|
42
|
+
Args mirror :class:`~altpay.client.sync_client.AltPay`; ``http_client`` here is an
|
|
43
|
+
:class:`httpx.AsyncClient`.
|
|
44
|
+
|
|
45
|
+
API reference: https://docs.altpay.money/docs
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
credentials: Credentials,
|
|
51
|
+
*,
|
|
52
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
53
|
+
timeout: float = 30.0,
|
|
54
|
+
max_retries: int = 2,
|
|
55
|
+
http_client: httpx.AsyncClient | None = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
self._credentials = credentials
|
|
58
|
+
self._base_url = base_url.rstrip("/")
|
|
59
|
+
self._max_retries = max_retries
|
|
60
|
+
self._owns_client = http_client is None
|
|
61
|
+
self._http = http_client or httpx.AsyncClient(timeout=timeout)
|
|
62
|
+
|
|
63
|
+
#: Invoice and static-wallet operations.
|
|
64
|
+
self.invoices = Invoices(self.call)
|
|
65
|
+
#: Payout (withdrawal) operations.
|
|
66
|
+
self.withdrawals = Withdrawals(self.call)
|
|
67
|
+
#: Account identity, balance and statistics.
|
|
68
|
+
self.account = AccountResource(self.call)
|
|
69
|
+
|
|
70
|
+
async def __aenter__(self) -> "AsyncAltPay":
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
async def __aexit__(self, *exc: object) -> None:
|
|
74
|
+
await self.aclose()
|
|
75
|
+
|
|
76
|
+
async def aclose(self) -> None:
|
|
77
|
+
"""Close the underlying HTTP connection pool (unless you supplied your own client)."""
|
|
78
|
+
if self._owns_client:
|
|
79
|
+
await self._http.aclose()
|
|
80
|
+
|
|
81
|
+
async def call(self, call: APICall[T]) -> T:
|
|
82
|
+
"""Execute a described :class:`~altpay.methods.base.APICall` and return its typed result."""
|
|
83
|
+
request = prepare(
|
|
84
|
+
credentials=self._credentials,
|
|
85
|
+
base_url=self._base_url,
|
|
86
|
+
path=call.path,
|
|
87
|
+
payload=call.payload,
|
|
88
|
+
)
|
|
89
|
+
attempt = 0
|
|
90
|
+
while True:
|
|
91
|
+
try:
|
|
92
|
+
response = await self._http.request(
|
|
93
|
+
request.method, request.url, headers=request.headers, content=request.content
|
|
94
|
+
)
|
|
95
|
+
except httpx.HTTPError as exc:
|
|
96
|
+
if attempt >= self._max_retries:
|
|
97
|
+
raise AltPayTransportError(str(exc)) from exc
|
|
98
|
+
await asyncio.sleep(_backoff(attempt))
|
|
99
|
+
attempt += 1
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
result = parse_response(
|
|
104
|
+
status_code=response.status_code,
|
|
105
|
+
body_text=response.text,
|
|
106
|
+
headers=response.headers,
|
|
107
|
+
)
|
|
108
|
+
except (RateLimitError, ServerError) as exc:
|
|
109
|
+
if attempt >= self._max_retries:
|
|
110
|
+
raise
|
|
111
|
+
await asyncio.sleep(_retry_delay(exc, attempt))
|
|
112
|
+
attempt += 1
|
|
113
|
+
continue
|
|
114
|
+
return call.parse(result)
|