usdpay 1.0.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.
usdpay/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """Official Python SDK for the USDPAY Payments API."""
2
+
3
+ from .client import UsdpayClient
4
+ from .exceptions import UsdpayApiError
5
+ from .webhook import verify_webhook_signature
6
+
7
+ __all__ = [
8
+ "UsdpayClient",
9
+ "UsdpayApiError",
10
+ "verify_webhook_signature",
11
+ ]
12
+
13
+ __version__ = "1.0.0"
usdpay/client.py ADDED
@@ -0,0 +1,266 @@
1
+ """Synchronous client for the public USDPAY Payments API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import math
7
+ import re
8
+ from collections.abc import Mapping
9
+ from typing import Any
10
+ from urllib.parse import quote, urlsplit
11
+
12
+ from .exceptions import UsdpayApiError
13
+ from .transport import HttpResponse, HttpTransport, Transport, TransportError, TransportTimeout
14
+
15
+ SDK_VERSION = "1.0.0"
16
+ DEFAULT_BASE_URL = "https://usdpay.me"
17
+ DEFAULT_TIMEOUT = 10.0
18
+ DEFAULT_CONNECT_TIMEOUT = 5.0
19
+
20
+ _IDEMPOTENCY_KEY = re.compile(r"^[A-Za-z0-9._:-]{1,160}$")
21
+ _SENSITIVE_KEY = re.compile(r"authorization|api.?key|secret|token|password", re.IGNORECASE)
22
+
23
+
24
+ class UsdpayClient:
25
+ """A typed, synchronous client for creating and inspecting invoices."""
26
+
27
+ def __init__(
28
+ self,
29
+ secret_key: str,
30
+ base_url: str = DEFAULT_BASE_URL,
31
+ timeout: float = DEFAULT_TIMEOUT,
32
+ connect_timeout: float = DEFAULT_CONNECT_TIMEOUT,
33
+ *,
34
+ _transport: Transport | None = None,
35
+ ) -> None:
36
+ if not isinstance(secret_key, str) or not secret_key.strip():
37
+ raise ValueError("secret_key is required")
38
+ if "\r" in secret_key or "\n" in secret_key:
39
+ raise ValueError("secret_key must not contain line breaks")
40
+
41
+ self._secret_key = secret_key
42
+ self.base_url = _normalize_base_url(base_url)
43
+ self.timeout = _positive_timeout(timeout, "timeout")
44
+ self.connect_timeout = _positive_timeout(connect_timeout, "connect_timeout")
45
+ self._transport = _transport or HttpTransport()
46
+
47
+ def __repr__(self) -> str:
48
+ return (
49
+ f"UsdpayClient(base_url={self.base_url!r}, timeout={self.timeout!r}, "
50
+ f"connect_timeout={self.connect_timeout!r})"
51
+ )
52
+
53
+ def create_invoice(
54
+ self,
55
+ payload: Mapping[str, Any],
56
+ idempotency_key: str | None = None,
57
+ ) -> dict[str, Any]:
58
+ """Create a payment invoice with an optional stable idempotency key."""
59
+
60
+ if not isinstance(payload, Mapping):
61
+ raise TypeError("payload must be a mapping")
62
+ amount = payload.get("amount")
63
+ if not isinstance(amount, str) or not amount.strip():
64
+ raise ValueError("amount must be a non-empty decimal string")
65
+
66
+ headers: dict[str, str] = {}
67
+ if idempotency_key is not None:
68
+ if not isinstance(idempotency_key, str) or not _IDEMPOTENCY_KEY.fullmatch(
69
+ idempotency_key
70
+ ):
71
+ raise ValueError(
72
+ "idempotency_key must contain 1-160 letters, digits, dots, "
73
+ "underscores, colons, or hyphens"
74
+ )
75
+ headers["Idempotency-Key"] = idempotency_key
76
+
77
+ try:
78
+ body = json.dumps(
79
+ dict(payload),
80
+ ensure_ascii=False,
81
+ separators=(",", ":"),
82
+ allow_nan=False,
83
+ ).encode("utf-8")
84
+ except (TypeError, ValueError) as exc:
85
+ raise ValueError("payload must contain JSON-serializable values") from exc
86
+
87
+ return self._request(
88
+ "POST",
89
+ "/api/invoices",
90
+ body=body,
91
+ authenticated=True,
92
+ extra_headers=headers,
93
+ )
94
+
95
+ def get_invoice(self, invoice_id: str) -> dict[str, Any]:
96
+ """Retrieve public checkout status without sending the secret key."""
97
+
98
+ if not isinstance(invoice_id, str) or not invoice_id.strip():
99
+ raise ValueError("invoice_id is required")
100
+ return self._request(
101
+ "GET",
102
+ "/api/invoices/" + quote(invoice_id.strip(), safe=""),
103
+ authenticated=False,
104
+ )
105
+
106
+ def list_invoices(self) -> dict[str, Any]:
107
+ """List invoices that belong to the store selected by the secret key."""
108
+
109
+ return self._request("GET", "/api/invoices", authenticated=True)
110
+
111
+ def _request(
112
+ self,
113
+ method: str,
114
+ path: str,
115
+ *,
116
+ body: bytes | None = None,
117
+ authenticated: bool,
118
+ extra_headers: Mapping[str, str] | None = None,
119
+ ) -> dict[str, Any]:
120
+ headers = {
121
+ "Accept": "application/json",
122
+ "User-Agent": f"usdpay-python/{SDK_VERSION}",
123
+ }
124
+ if authenticated:
125
+ headers["Authorization"] = "Bearer " + self._secret_key
126
+ if body is not None:
127
+ headers["Content-Type"] = "application/json"
128
+ if extra_headers:
129
+ headers.update(extra_headers)
130
+
131
+ try:
132
+ response = self._transport.send(
133
+ method,
134
+ self.base_url + path,
135
+ headers,
136
+ body,
137
+ self.timeout,
138
+ self.connect_timeout,
139
+ )
140
+ except TransportTimeout:
141
+ raise UsdpayApiError(
142
+ "USDPAY request timed out",
143
+ code="request_timeout",
144
+ ) from None
145
+ except TransportError:
146
+ raise UsdpayApiError(
147
+ "Could not reach USDPAY",
148
+ code="network_error",
149
+ ) from None
150
+ except Exception:
151
+ # Custom transports are permitted for tests, but their internals and
152
+ # request headers must never leak through a public exception.
153
+ raise UsdpayApiError(
154
+ "Could not reach USDPAY",
155
+ code="network_error",
156
+ ) from None
157
+
158
+ if not 200 <= response.status < 300:
159
+ raise self._http_error(response)
160
+ return self._decode_success(response)
161
+
162
+ def _decode_success(self, response: HttpResponse) -> dict[str, Any]:
163
+ if not response.body:
164
+ return {}
165
+ decoded = _decode_json_object(response.body)
166
+ if decoded is None:
167
+ raise UsdpayApiError(
168
+ "USDPAY returned invalid JSON",
169
+ status=response.status,
170
+ code="invalid_response",
171
+ request_id=_clean_header(response.header("X-Request-ID"), self._secret_key),
172
+ )
173
+ return decoded
174
+
175
+ def _http_error(self, response: HttpResponse) -> UsdpayApiError:
176
+ decoded = _decode_json_object(response.body) if response.body else None
177
+ redacted = _redact(decoded, self._secret_key) if decoded is not None else None
178
+
179
+ code = "request_failed"
180
+ if decoded is not None and isinstance(decoded.get("error"), str):
181
+ candidate = decoded["error"].strip()
182
+ if candidate and self._secret_key not in candidate:
183
+ code = candidate
184
+
185
+ request_id = _clean_header(response.header("X-Request-ID"), self._secret_key)
186
+ if decoded is not None and isinstance(decoded.get("requestId"), str):
187
+ candidate_request_id = _clean_header(decoded["requestId"], self._secret_key)
188
+ if candidate_request_id:
189
+ request_id = candidate_request_id
190
+
191
+ retry_after = _retry_after(response.header("Retry-After"), self._secret_key)
192
+ return UsdpayApiError(
193
+ f"USDPAY request failed with HTTP {response.status} ({code})",
194
+ status=response.status,
195
+ code=code,
196
+ details=redacted,
197
+ retry_after=retry_after,
198
+ request_id=request_id,
199
+ )
200
+
201
+
202
+ def _normalize_base_url(base_url: str) -> str:
203
+ if not isinstance(base_url, str):
204
+ raise TypeError("base_url must be a string")
205
+ normalized = base_url.strip().rstrip("/")
206
+ target = urlsplit(normalized)
207
+ if (
208
+ not normalized
209
+ or target.scheme != "https"
210
+ or not target.hostname
211
+ or target.username is not None
212
+ or target.password is not None
213
+ or target.query
214
+ or target.fragment
215
+ or target.path not in ("", "/")
216
+ ):
217
+ raise ValueError("base_url must be an HTTPS origin")
218
+ return normalized
219
+
220
+
221
+ def _positive_timeout(value: float, name: str) -> float:
222
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
223
+ raise TypeError(f"{name} must be a number")
224
+ number = float(value)
225
+ if not math.isfinite(number) or number <= 0:
226
+ raise ValueError(f"{name} must be greater than zero")
227
+ return number
228
+
229
+
230
+ def _decode_json_object(body: bytes) -> dict[str, Any] | None:
231
+ try:
232
+ decoded = json.loads(body.decode("utf-8"))
233
+ except (UnicodeDecodeError, json.JSONDecodeError):
234
+ return None
235
+ return decoded if isinstance(decoded, dict) else None
236
+
237
+
238
+ def _clean_header(value: str | None, secret: str) -> str | None:
239
+ if value is None:
240
+ return None
241
+ cleaned = value.strip()
242
+ if not cleaned or secret in cleaned:
243
+ return None
244
+ return cleaned
245
+
246
+
247
+ def _retry_after(value: str | None, secret: str) -> int | str | None:
248
+ cleaned = _clean_header(value, secret)
249
+ if cleaned is None:
250
+ return None
251
+ return int(cleaned) if cleaned.isdigit() else cleaned
252
+
253
+
254
+ def _redact(value: Any, secret: str, key: str | None = None) -> Any:
255
+ if key is not None and _SENSITIVE_KEY.search(key):
256
+ return "[REDACTED]"
257
+ if isinstance(value, str):
258
+ return value.replace(secret, "[REDACTED]")
259
+ if isinstance(value, list):
260
+ return [_redact(item, secret) for item in value]
261
+ if isinstance(value, dict):
262
+ return {
263
+ item_key: _redact(item_value, secret, str(item_key))
264
+ for item_key, item_value in value.items()
265
+ }
266
+ return value
usdpay/exceptions.py ADDED
@@ -0,0 +1,26 @@
1
+ """Exceptions raised by the USDPAY SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ class UsdpayApiError(RuntimeError):
9
+ """A structured HTTP, transport, or response error from the USDPAY API."""
10
+
11
+ def __init__(
12
+ self,
13
+ message: str,
14
+ *,
15
+ status: int = 0,
16
+ code: str = "request_failed",
17
+ details: Any = None,
18
+ retry_after: int | str | None = None,
19
+ request_id: str | None = None,
20
+ ) -> None:
21
+ super().__init__(message)
22
+ self.status = status
23
+ self.code = code
24
+ self.details = details
25
+ self.retry_after = retry_after
26
+ self.request_id = request_id
usdpay/py.typed ADDED
@@ -0,0 +1 @@
1
+
usdpay/transport.py ADDED
@@ -0,0 +1,109 @@
1
+ """Small HTTPS transport used by the synchronous USDPAY client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import http.client
6
+ import ssl
7
+ from collections.abc import Mapping
8
+ from dataclasses import dataclass
9
+ from typing import Protocol
10
+ from urllib.parse import SplitResult, urlsplit
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class HttpResponse:
15
+ """Transport response consumed by :class:`usdpay.UsdpayClient`."""
16
+
17
+ status: int
18
+ body: bytes
19
+ headers: Mapping[str, str]
20
+
21
+ def header(self, name: str) -> str | None:
22
+ return self.headers.get(name.lower())
23
+
24
+
25
+ class Transport(Protocol):
26
+ """Internal transport interface used to keep HTTP behavior testable."""
27
+
28
+ def send(
29
+ self,
30
+ method: str,
31
+ url: str,
32
+ headers: Mapping[str, str],
33
+ body: bytes | None,
34
+ timeout: float,
35
+ connect_timeout: float,
36
+ ) -> HttpResponse: ...
37
+
38
+
39
+ class TransportError(Exception):
40
+ """The remote service could not be reached."""
41
+
42
+
43
+ class TransportTimeout(TransportError):
44
+ """The connection or response exceeded its configured timeout."""
45
+
46
+
47
+ class HttpTransport:
48
+ """Zero-dependency HTTPS transport with certificate verification enabled."""
49
+
50
+ def __init__(self, *, ssl_context: ssl.SSLContext | None = None) -> None:
51
+ self._ssl_context = ssl_context or ssl.create_default_context()
52
+
53
+ def send(
54
+ self,
55
+ method: str,
56
+ url: str,
57
+ headers: Mapping[str, str],
58
+ body: bytes | None,
59
+ timeout: float,
60
+ connect_timeout: float,
61
+ ) -> HttpResponse:
62
+ target = _validated_https_url(url)
63
+ host = target.hostname
64
+ if host is None: # Kept explicit for static type checkers.
65
+ raise TransportError("HTTPS URL has no hostname")
66
+
67
+ connection = http.client.HTTPSConnection(
68
+ host,
69
+ target.port or 443,
70
+ timeout=connect_timeout,
71
+ context=self._ssl_context,
72
+ )
73
+ request_target = target.path or "/"
74
+ if target.query:
75
+ request_target += "?" + target.query
76
+
77
+ try:
78
+ connection.request(method, request_target, body=body, headers=dict(headers))
79
+ if connection.sock is not None:
80
+ connection.sock.settimeout(timeout)
81
+ response = connection.getresponse()
82
+ response_body = response.read()
83
+ response_headers: dict[str, str] = {}
84
+ for name, value in response.getheaders():
85
+ normalized = name.lower()
86
+ if normalized in response_headers:
87
+ response_headers[normalized] += ", " + value
88
+ else:
89
+ response_headers[normalized] = value
90
+ return HttpResponse(response.status, response_body, response_headers)
91
+ except TimeoutError as exc:
92
+ raise TransportTimeout("HTTPS request timed out") from exc
93
+ except (OSError, http.client.HTTPException) as exc:
94
+ raise TransportError("HTTPS request failed") from exc
95
+ finally:
96
+ connection.close()
97
+
98
+
99
+ def _validated_https_url(url: str) -> SplitResult:
100
+ target = urlsplit(url)
101
+ if (
102
+ target.scheme != "https"
103
+ or not target.hostname
104
+ or target.username is not None
105
+ or target.password is not None
106
+ or target.fragment
107
+ ):
108
+ raise TransportError("transport requires an HTTPS URL without credentials or fragment")
109
+ return target
usdpay/webhook.py ADDED
@@ -0,0 +1,40 @@
1
+ """Helpers for authenticating USDPAY webhook deliveries."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import hmac
7
+
8
+
9
+ def verify_webhook_signature(
10
+ raw_body: bytes | bytearray | memoryview | str,
11
+ signature: str,
12
+ secret: bytes | str,
13
+ ) -> bool:
14
+ """Return whether ``signature`` is the HMAC-SHA256 of the unparsed body.
15
+
16
+ USDPAY sends signatures as ``sha256=<lowercase hex digest>``. Call this
17
+ function before decoding JSON, and pass the exact bytes received over HTTP.
18
+ """
19
+
20
+ if isinstance(raw_body, str):
21
+ body = raw_body.encode("utf-8")
22
+ elif isinstance(raw_body, (bytes, bytearray, memoryview)):
23
+ body = bytes(raw_body)
24
+ else:
25
+ raise TypeError("raw_body must be bytes-like or str")
26
+
27
+ if isinstance(secret, str):
28
+ secret_bytes = secret.encode("utf-8")
29
+ elif isinstance(secret, bytes):
30
+ secret_bytes = secret
31
+ else:
32
+ raise TypeError("secret must be bytes or str")
33
+ if not secret_bytes:
34
+ raise ValueError("webhook secret is required")
35
+
36
+ received = signature.strip() if isinstance(signature, str) else ""
37
+ expected = "sha256=" + hmac.new(secret_bytes, body, hashlib.sha256).hexdigest()
38
+ if len(received) != len(expected):
39
+ return False
40
+ return hmac.compare_digest(received, expected)
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: usdpay
3
+ Version: 1.0.0
4
+ Summary: Python SDK for accepting USDT payments directly to your wallet with automatic payment verification and webhooks.
5
+ Project-URL: Homepage, https://usdpay.me/
6
+ Project-URL: Documentation, https://usdpay.me/docs/payments
7
+ Project-URL: Webhooks, https://usdpay.me/webhooks
8
+ Project-URL: Security, https://usdpay.me/security
9
+ Project-URL: Support, https://usdpay.me/contact
10
+ Project-URL: Source, https://github.com/probizi/usdpay-python
11
+ Project-URL: Issues, https://github.com/probizi/usdpay-python/issues
12
+ Author: USDPAY
13
+ License-Expression: MIT
14
+ License-File: LICENSE
15
+ Keywords: bep20,bsc,crypto-payments,payment-api,payment-gateway,payments,ton,trc20,tron,usdpay,usdt,usdt-api,webhook
16
+ Classifier: Development Status :: 5 - Production/Stable
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Provides-Extra: dev
28
+ Requires-Dist: build<2,>=1.2; extra == 'dev'
29
+ Requires-Dist: pytest<10,>=8; extra == 'dev'
30
+ Requires-Dist: ruff<1,>=0.9; extra == 'dev'
31
+ Requires-Dist: twine<7,>=6; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # USDPAY Python SDK
35
+
36
+ Official Python SDK for USDPAY.
37
+
38
+ Accept USDT directly to your wallet. USDPAY verifies the payment on-chain and notifies your application automatically with signed webhooks.
39
+
40
+ ## Requirements
41
+
42
+ - Python 3.10 or newer
43
+ - A USDPAY store secret key for authenticated methods
44
+ - A server-side application; never expose the secret key in browser or mobile code
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install usdpay
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```python
55
+ import os
56
+
57
+ from usdpay import UsdpayClient
58
+
59
+ client = UsdpayClient(
60
+ secret_key=os.environ["USDPAY_SECRET"]
61
+ )
62
+
63
+ result = client.create_invoice(
64
+ {
65
+ "amount": "49.00",
66
+ "orderId": "ORDER-1042",
67
+ "network": "TRC20",
68
+ "callbackUrl": "https://merchant.example/usdpay/webhook",
69
+ "returnUrl": "https://merchant.example/orders/1042",
70
+ },
71
+ idempotency_key="ORDER-1042-create",
72
+ )
73
+
74
+ print(result["invoice"]["checkoutUrl"])
75
+ ```
76
+
77
+ ## Create an Invoice
78
+
79
+ `create_invoice()` sends `POST /api/invoices`. JSON field names match the REST API exactly.
80
+
81
+ ```python
82
+ result = client.create_invoice(
83
+ {
84
+ "amount": "49.00",
85
+ "orderId": "ORDER-1042",
86
+ "network": "TRC20",
87
+ "expiresInMinutes": 30,
88
+ "callbackUrl": "https://merchant.example/usdpay/webhook",
89
+ "returnUrl": "https://merchant.example/orders/1042",
90
+ },
91
+ idempotency_key="ORDER-1042-create",
92
+ )
93
+ ```
94
+
95
+ Omit `network` to let the customer choose an enabled network in the hosted checkout.
96
+
97
+ ## Get an Invoice
98
+
99
+ ```python
100
+ result = client.get_invoice("inv_7Fq2xK9")
101
+ print(result["invoice"]["status"])
102
+ ```
103
+
104
+ The production API exposes this status endpoint to anyone who has the unguessable invoice ID. The SDK therefore does not send your Bearer key with `get_invoice()`.
105
+
106
+ ## List Invoices
107
+
108
+ ```python
109
+ result = client.list_invoices()
110
+ for invoice in result["invoices"]:
111
+ print(invoice["id"], invoice["status"])
112
+ ```
113
+
114
+ `list_invoices()` is authenticated and returns invoices for the store selected by the secret key. The current API does not define filtering or pagination parameters, so the SDK does not invent any.
115
+
116
+ ## Fiat Order Amounts
117
+
118
+ Keep monetary values as decimal strings. USDPAY performs the currency conversion; the SDK does not use floating-point math or calculate FX rates.
119
+
120
+ ```python
121
+ result = client.create_invoice(
122
+ {
123
+ "amount": "49.00",
124
+ "currency": "EUR",
125
+ "orderId": "ORDER-1042",
126
+ "callbackUrl": "https://merchant.example/usdpay/webhook",
127
+ },
128
+ idempotency_key="ORDER-1042-create",
129
+ )
130
+ ```
131
+
132
+ ## Idempotency
133
+
134
+ Pass one stable `idempotency_key` for a logical create operation. If a timeout, `429`, or retryable `5xx` occurs, retry with the same key. Do not generate a new key for each attempt.
135
+
136
+ USDPAY accepts 1–160 letters, digits, dots, underscores, colons, or hyphens. The SDK validates the key but does not automatically retry requests.
137
+
138
+ ## Verify Webhooks
139
+
140
+ Verify the signature against the exact raw request body before parsing JSON.
141
+
142
+ ```python
143
+ import os
144
+
145
+ from usdpay import verify_webhook_signature
146
+
147
+ raw_body = request_body_bytes
148
+ signature = request_headers.get("X-USDPAY-Signature", "")
149
+
150
+ if not verify_webhook_signature(
151
+ raw_body,
152
+ signature,
153
+ os.environ["USDPAY_WEBHOOK_SECRET"],
154
+ ):
155
+ # Return HTTP 401.
156
+ ...
157
+ ```
158
+
159
+ The signature format is `sha256=<hex HMAC-SHA256>`. Store `X-USDPAY-Idempotency-Key` under a unique database constraint before fulfilling an order, and acknowledge an already processed delivery with a `2xx` response.
160
+
161
+ ## Error Handling
162
+
163
+ ```python
164
+ from usdpay import UsdpayApiError
165
+
166
+ try:
167
+ client.create_invoice(
168
+ {"amount": "49.00", "orderId": "ORDER-1042"},
169
+ idempotency_key="ORDER-1042-create",
170
+ )
171
+ except UsdpayApiError as exc:
172
+ print(exc.status)
173
+ print(exc.code)
174
+ print(exc.retry_after)
175
+ print(exc.request_id)
176
+ ```
177
+
178
+ `UsdpayApiError` covers HTTP failures, timeouts, network failures, and malformed JSON. Its public attributes are:
179
+
180
+ - `status`: HTTP status, or `0` when no HTTP response was received
181
+ - `code`: stable API or SDK error code
182
+ - `details`: redacted response object when available
183
+ - `retry_after`: parsed `Retry-After` seconds or HTTP-date
184
+ - `request_id`: response request identifier for support
185
+
186
+ ## Security
187
+
188
+ - Keep `USDPAY_SECRET` and the webhook signing secret on your server.
189
+ - The default transport accepts HTTPS only and uses Python's verified system trust store with hostname verification.
190
+ - Requests have finite connect and response timeouts; configure them with `connect_timeout` and `timeout`.
191
+ - The client does not follow redirects or make network calls when imported.
192
+ - Secrets are not included in `repr(client)`, public exception messages, or exception details.
193
+ - Never disable TLS verification.
194
+
195
+ ## Documentation
196
+
197
+ - [Official website](https://usdpay.me/)
198
+ - [Payment API documentation](https://usdpay.me/docs/payments)
199
+ - [Webhook documentation](https://usdpay.me/webhooks)
200
+ - [Security](https://usdpay.me/security)
201
+ - [Support](https://usdpay.me/contact)
202
+
203
+ ## License
204
+
205
+ [MIT](LICENSE) © 2026 PIXELTIDE LLC.
@@ -0,0 +1,10 @@
1
+ usdpay/__init__.py,sha256=7L0w5VZTuHH0yhyWPGBQj95I12TIicVRpDWBunMijow,286
2
+ usdpay/client.py,sha256=cllUU3RMtL3Clo2fc_lJpNJXLKq1t_LS84NdHikFbgk,9341
3
+ usdpay/exceptions.py,sha256=NAEt-P9O2rR7WKijxc75_dht7AbrwaznWMJpXd683ps,673
4
+ usdpay/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
5
+ usdpay/transport.py,sha256=vuEXnsKtEifD9BLTTGYOTQUqqs6JYD2fndwQeL3AYlE,3421
6
+ usdpay/webhook.py,sha256=GGbBZZT2Li8uxCxuEGIIHInNzThZvPfbK8TMbrvB0jc,1304
7
+ usdpay-1.0.0.dist-info/METADATA,sha256=21n-1vui61OeGivhkq2beozEi2PbTcxTYAXV49gA5Uw,6485
8
+ usdpay-1.0.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
9
+ usdpay-1.0.0.dist-info/licenses/LICENSE,sha256=kTY14J2FROteaZgWL09PFrUFdbYohOnJVAI3YVe5EDQ,1070
10
+ usdpay-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PIXELTIDE LLC
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.