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.
@@ -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.
@@ -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,5 @@
1
+ """Package metadata, kept in one place so the version has a single source of truth."""
2
+
3
+ __version__ = "0.1.0"
4
+ __title__ = "altpay-py"
5
+ __user_agent__ = f"altpay-py/{__version__}"
@@ -0,0 +1,6 @@
1
+ """HTTP clients: the synchronous :class:`AltPay` and asynchronous :class:`AsyncAltPay`."""
2
+
3
+ from .async_client import AsyncAltPay
4
+ from .sync_client import AltPay
5
+
6
+ __all__ = ["AltPay", "AsyncAltPay"]
@@ -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)