payzcore 1.0.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.
- payzcore-1.0.0/.gitignore +8 -0
- payzcore-1.0.0/LICENSE +21 -0
- payzcore-1.0.0/PKG-INFO +266 -0
- payzcore-1.0.0/README.md +239 -0
- payzcore-1.0.0/payzcore/__init__.py +160 -0
- payzcore-1.0.0/payzcore/client.py +169 -0
- payzcore-1.0.0/payzcore/errors.py +86 -0
- payzcore-1.0.0/payzcore/resources/__init__.py +4 -0
- payzcore-1.0.0/payzcore/resources/payments.py +254 -0
- payzcore-1.0.0/payzcore/resources/projects.py +90 -0
- payzcore-1.0.0/payzcore/types.py +209 -0
- payzcore-1.0.0/payzcore/webhook.py +150 -0
- payzcore-1.0.0/pyproject.toml +37 -0
payzcore-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PayzCore
|
|
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.
|
payzcore-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: payzcore
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: PayzCore Python SDK - Blockchain transaction monitoring API client
|
|
5
|
+
Project-URL: Homepage, https://payzcore.com
|
|
6
|
+
Project-URL: Documentation, https://docs.payzcore.com
|
|
7
|
+
Project-URL: Repository, https://github.com/payzcore/python-sdk
|
|
8
|
+
Project-URL: Changelog, https://docs.payzcore.com/docs/changelog/v1-0-0
|
|
9
|
+
Author-email: PayzCore <dev@payzcore.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,blockchain,cryptocurrency,monitoring,payzcore,stablecoin,usdc,usdt,webhook
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: httpx>=0.24.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# PayzCore Python SDK
|
|
29
|
+
|
|
30
|
+
Python client for the [PayzCore](https://payzcore.com) blockchain transaction monitoring API.
|
|
31
|
+
|
|
32
|
+
## Important
|
|
33
|
+
|
|
34
|
+
**PayzCore is a blockchain monitoring service, not a payment processor.** All payments are sent directly to your own wallet addresses. PayzCore never holds, transfers, or has access to your funds.
|
|
35
|
+
|
|
36
|
+
- **Your wallets, your funds** — You provide your own wallet (HD xPub or static addresses). Customers pay directly to your addresses.
|
|
37
|
+
- **Read-only monitoring** — PayzCore watches the blockchain for incoming transactions and sends webhook notifications. That's it.
|
|
38
|
+
- **Protection Key security** — Sensitive operations like wallet management, address changes, and API key regeneration require a Protection Key that only you set. PayzCore cannot perform these actions without your authorization.
|
|
39
|
+
- **Your responsibility** — You are responsible for securing your own wallets and private keys. PayzCore provides monitoring and notification only.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install payzcore
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### Payment Monitoring
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from payzcore import PayzCore
|
|
53
|
+
|
|
54
|
+
client = PayzCore("pk_live_xxx")
|
|
55
|
+
|
|
56
|
+
# Create a payment monitoring request (network specified)
|
|
57
|
+
response = client.payments.create(
|
|
58
|
+
amount=50,
|
|
59
|
+
external_ref="user-123",
|
|
60
|
+
network="TRC20",
|
|
61
|
+
)
|
|
62
|
+
payment = response["payment"]
|
|
63
|
+
print(f"Send {payment['amount']} {payment['token']} to {payment['address']}")
|
|
64
|
+
print(f"Expires at: {payment['expires_at']}")
|
|
65
|
+
# Static wallet projects may also return: payment['notice'], payment['original_amount'], payment['requires_txid']
|
|
66
|
+
|
|
67
|
+
# Or let the customer choose the network on the payment page
|
|
68
|
+
response = client.payments.create(
|
|
69
|
+
amount=50,
|
|
70
|
+
external_ref="user-123",
|
|
71
|
+
)
|
|
72
|
+
p = response["payment"]
|
|
73
|
+
print(p["awaiting_network"]) # True
|
|
74
|
+
print(p["payment_url"]) # "https://app.payzcore.com/pay/xxx"
|
|
75
|
+
print(p["available_networks"]) # [{"network": "TRC20", "name": "Tron", "tokens": ["USDT"]}, ...]
|
|
76
|
+
|
|
77
|
+
# Create a USDC payment on Polygon
|
|
78
|
+
response = client.payments.create(
|
|
79
|
+
amount=100,
|
|
80
|
+
external_ref="order-456",
|
|
81
|
+
network="POLYGON",
|
|
82
|
+
token="USDC",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Create a payment with static wallet (dedicated mode)
|
|
86
|
+
response = client.payments.create(
|
|
87
|
+
amount=50,
|
|
88
|
+
external_ref="user-789",
|
|
89
|
+
network="TRC20",
|
|
90
|
+
address="Txxxx...", # optional, static wallet dedicated mode only
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# List payments
|
|
94
|
+
payments = client.payments.list(status="pending", limit=10)
|
|
95
|
+
for p in payments["payments"]:
|
|
96
|
+
print(f"{p['id']}: {p['status']} ({p['expected_amount']} {p['token']})")
|
|
97
|
+
|
|
98
|
+
# Get payment details (latest cached status from database)
|
|
99
|
+
detail = client.payments.get("payment-uuid")
|
|
100
|
+
print(f"Status: {detail['payment']['status']}")
|
|
101
|
+
for tx in detail["payment"]["transactions"]:
|
|
102
|
+
print(f" TX: {tx['tx_hash']} - {tx['amount']} {detail['payment']['token']}")
|
|
103
|
+
|
|
104
|
+
# Cancel a pending payment
|
|
105
|
+
result = client.payments.cancel("payment-uuid")
|
|
106
|
+
# result["payment"]["status"] == "cancelled"
|
|
107
|
+
|
|
108
|
+
# Submit tx hash for verification (pool + txid mode)
|
|
109
|
+
result = client.payments.confirm("payment-uuid", "abc123def456...")
|
|
110
|
+
# result["verified"], result["status"], result["amount_received"]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Supported Networks and Tokens
|
|
114
|
+
|
|
115
|
+
| Network | Blockchain | Tokens |
|
|
116
|
+
|---------|------------|--------|
|
|
117
|
+
| `TRC20` | Tron | USDT |
|
|
118
|
+
| `BEP20` | BNB Smart Chain | USDT, USDC |
|
|
119
|
+
| `ERC20` | Ethereum | USDT, USDC |
|
|
120
|
+
| `POLYGON` | Polygon | USDT, USDC |
|
|
121
|
+
| `ARBITRUM` | Arbitrum | USDT, USDC |
|
|
122
|
+
|
|
123
|
+
The `token` parameter defaults to `"USDT"` if not specified, ensuring backward compatibility.
|
|
124
|
+
|
|
125
|
+
### Project Management (Admin)
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from payzcore import PayzCore
|
|
129
|
+
|
|
130
|
+
admin = PayzCore("mk_xxx", master_key=True)
|
|
131
|
+
|
|
132
|
+
# Create a project
|
|
133
|
+
project = admin.projects.create(
|
|
134
|
+
name="My Store",
|
|
135
|
+
slug="my-store",
|
|
136
|
+
webhook_url="https://example.com/webhooks/payzcore",
|
|
137
|
+
)
|
|
138
|
+
print(f"API Key: {project['project']['api_key']}")
|
|
139
|
+
|
|
140
|
+
# List projects
|
|
141
|
+
projects = admin.projects.list()
|
|
142
|
+
for p in projects["projects"]:
|
|
143
|
+
print(f"{p['name']} ({p['slug']}): {'active' if p['is_active'] else 'inactive'}")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Webhook Verification
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from payzcore import verify_signature, construct_event, WebhookSignatureError
|
|
150
|
+
|
|
151
|
+
# In your webhook handler
|
|
152
|
+
def handle_webhook(request):
|
|
153
|
+
body = request.body.decode("utf-8")
|
|
154
|
+
signature = request.headers["X-PayzCore-Signature"]
|
|
155
|
+
secret = "whsec_xxx" # From project creation
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
event = construct_event(body, signature, secret)
|
|
159
|
+
except WebhookSignatureError:
|
|
160
|
+
return {"error": "Invalid signature"}, 401
|
|
161
|
+
|
|
162
|
+
if event["event"] == "payment.completed":
|
|
163
|
+
print(f"Payment {event['payment_id']} completed!")
|
|
164
|
+
print(f"Amount: {event['paid_amount']} {event['token']} on {event['network']}")
|
|
165
|
+
|
|
166
|
+
return {"ok": True}, 200
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
> **Note:** The `address` parameter is only used with static wallet projects in dedicated mode. For HD wallet projects, this parameter is ignored.
|
|
170
|
+
|
|
171
|
+
## Static Wallet Mode
|
|
172
|
+
|
|
173
|
+
When the PayzCore project is configured with a static wallet, the API works the same way but may return additional fields in the response:
|
|
174
|
+
|
|
175
|
+
| Field | Type | Description |
|
|
176
|
+
|-------|------|-------------|
|
|
177
|
+
| `notice` | `str` | Instructions for the payer (e.g. "Send exact amount") |
|
|
178
|
+
| `original_amount` | `str` | The original requested amount before any adjustments |
|
|
179
|
+
| `requires_txid` | `bool` | Whether the payer must submit their transaction ID |
|
|
180
|
+
|
|
181
|
+
In dedicated address mode, you can specify which static address to assign to a customer using the `address` parameter on payment creation. In shared address mode, the project's single static address is used automatically.
|
|
182
|
+
|
|
183
|
+
## Before Going Live
|
|
184
|
+
|
|
185
|
+
**Always test your setup before accepting real payments:**
|
|
186
|
+
|
|
187
|
+
1. **Verify your xPub** — In the PayzCore dashboard, click "Verify Key" when adding your wallet. Compare address #0 with your wallet app's first receiving address. They must match.
|
|
188
|
+
2. **Send a test payment** — Create a monitoring request for $1–5 and send the funds to the assigned address. Verify they arrive in your wallet.
|
|
189
|
+
3. **Test sweeping** — Send the test funds back out to confirm you control the derived addresses with your private keys.
|
|
190
|
+
|
|
191
|
+
> **Warning:** A wrong xPub key generates addresses you don't control. Funds sent to those addresses are permanently lost. PayzCore is watch-only and cannot recover funds. Please take 2 minutes to verify.
|
|
192
|
+
|
|
193
|
+
## Configuration
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
client = PayzCore(
|
|
197
|
+
"pk_live_xxx",
|
|
198
|
+
base_url="https://api.payzcore.com", # API base URL
|
|
199
|
+
timeout=30.0, # Request timeout (seconds)
|
|
200
|
+
max_retries=2, # Retries on 5xx errors
|
|
201
|
+
master_key=False, # Use x-master-key header
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Error Handling
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from payzcore import (
|
|
209
|
+
PayzCore,
|
|
210
|
+
PayzCoreError,
|
|
211
|
+
AuthenticationError,
|
|
212
|
+
ValidationError,
|
|
213
|
+
RateLimitError,
|
|
214
|
+
NotFoundError,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
client = PayzCore("pk_live_xxx")
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
payment = client.payments.create(
|
|
221
|
+
amount=50, network="TRC20", external_ref="user-123"
|
|
222
|
+
)
|
|
223
|
+
except AuthenticationError:
|
|
224
|
+
print("Invalid API key")
|
|
225
|
+
except ValidationError as e:
|
|
226
|
+
print(f"Invalid params: {e.message}")
|
|
227
|
+
if e.details:
|
|
228
|
+
for d in e.details:
|
|
229
|
+
print(f" {d['path']}: {d['message']}")
|
|
230
|
+
except RateLimitError as e:
|
|
231
|
+
print(f"Rate limited. Daily: {e.is_daily}, retry after: {e.retry_after}")
|
|
232
|
+
except NotFoundError:
|
|
233
|
+
print("Resource not found")
|
|
234
|
+
except PayzCoreError as e:
|
|
235
|
+
print(f"API error {e.status}: {e.message}")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Error Classes
|
|
239
|
+
|
|
240
|
+
| Class | Status | When |
|
|
241
|
+
|-------|--------|------|
|
|
242
|
+
| `AuthenticationError` | 401 | Invalid/missing API key |
|
|
243
|
+
| `ForbiddenError` | 403 | Project deactivated |
|
|
244
|
+
| `NotFoundError` | 404 | Payment/resource not found |
|
|
245
|
+
| `ValidationError` | 400 | Invalid request body |
|
|
246
|
+
| `RateLimitError` | 429 | Rate limit or daily plan limit exceeded |
|
|
247
|
+
| `IdempotencyError` | 409 | `external_order_id` reused with different `external_ref` |
|
|
248
|
+
| `WebhookSignatureError` | — | Invalid webhook signature |
|
|
249
|
+
|
|
250
|
+
## Requirements
|
|
251
|
+
|
|
252
|
+
- Python 3.9+
|
|
253
|
+
- httpx
|
|
254
|
+
|
|
255
|
+
## See Also
|
|
256
|
+
|
|
257
|
+
- [Getting Started](https://docs.payzcore.com/getting-started) — Account setup and first payment
|
|
258
|
+
- [Authentication & API Keys](https://docs.payzcore.com/guides/authentication) — API key management
|
|
259
|
+
- [Webhooks Guide](https://docs.payzcore.com/guides/webhooks) — Events, headers, and signature verification
|
|
260
|
+
- [Supported Networks](https://docs.payzcore.com/guides/networks) — Available networks and tokens
|
|
261
|
+
- [Error Reference](https://docs.payzcore.com/guides/errors) — HTTP status codes and troubleshooting
|
|
262
|
+
- [API Reference](https://docs.payzcore.com) — Interactive API documentation (Scalar UI)
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT
|
payzcore-1.0.0/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# PayzCore Python SDK
|
|
2
|
+
|
|
3
|
+
Python client for the [PayzCore](https://payzcore.com) blockchain transaction monitoring API.
|
|
4
|
+
|
|
5
|
+
## Important
|
|
6
|
+
|
|
7
|
+
**PayzCore is a blockchain monitoring service, not a payment processor.** All payments are sent directly to your own wallet addresses. PayzCore never holds, transfers, or has access to your funds.
|
|
8
|
+
|
|
9
|
+
- **Your wallets, your funds** — You provide your own wallet (HD xPub or static addresses). Customers pay directly to your addresses.
|
|
10
|
+
- **Read-only monitoring** — PayzCore watches the blockchain for incoming transactions and sends webhook notifications. That's it.
|
|
11
|
+
- **Protection Key security** — Sensitive operations like wallet management, address changes, and API key regeneration require a Protection Key that only you set. PayzCore cannot perform these actions without your authorization.
|
|
12
|
+
- **Your responsibility** — You are responsible for securing your own wallets and private keys. PayzCore provides monitoring and notification only.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install payzcore
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Payment Monitoring
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from payzcore import PayzCore
|
|
26
|
+
|
|
27
|
+
client = PayzCore("pk_live_xxx")
|
|
28
|
+
|
|
29
|
+
# Create a payment monitoring request (network specified)
|
|
30
|
+
response = client.payments.create(
|
|
31
|
+
amount=50,
|
|
32
|
+
external_ref="user-123",
|
|
33
|
+
network="TRC20",
|
|
34
|
+
)
|
|
35
|
+
payment = response["payment"]
|
|
36
|
+
print(f"Send {payment['amount']} {payment['token']} to {payment['address']}")
|
|
37
|
+
print(f"Expires at: {payment['expires_at']}")
|
|
38
|
+
# Static wallet projects may also return: payment['notice'], payment['original_amount'], payment['requires_txid']
|
|
39
|
+
|
|
40
|
+
# Or let the customer choose the network on the payment page
|
|
41
|
+
response = client.payments.create(
|
|
42
|
+
amount=50,
|
|
43
|
+
external_ref="user-123",
|
|
44
|
+
)
|
|
45
|
+
p = response["payment"]
|
|
46
|
+
print(p["awaiting_network"]) # True
|
|
47
|
+
print(p["payment_url"]) # "https://app.payzcore.com/pay/xxx"
|
|
48
|
+
print(p["available_networks"]) # [{"network": "TRC20", "name": "Tron", "tokens": ["USDT"]}, ...]
|
|
49
|
+
|
|
50
|
+
# Create a USDC payment on Polygon
|
|
51
|
+
response = client.payments.create(
|
|
52
|
+
amount=100,
|
|
53
|
+
external_ref="order-456",
|
|
54
|
+
network="POLYGON",
|
|
55
|
+
token="USDC",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Create a payment with static wallet (dedicated mode)
|
|
59
|
+
response = client.payments.create(
|
|
60
|
+
amount=50,
|
|
61
|
+
external_ref="user-789",
|
|
62
|
+
network="TRC20",
|
|
63
|
+
address="Txxxx...", # optional, static wallet dedicated mode only
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# List payments
|
|
67
|
+
payments = client.payments.list(status="pending", limit=10)
|
|
68
|
+
for p in payments["payments"]:
|
|
69
|
+
print(f"{p['id']}: {p['status']} ({p['expected_amount']} {p['token']})")
|
|
70
|
+
|
|
71
|
+
# Get payment details (latest cached status from database)
|
|
72
|
+
detail = client.payments.get("payment-uuid")
|
|
73
|
+
print(f"Status: {detail['payment']['status']}")
|
|
74
|
+
for tx in detail["payment"]["transactions"]:
|
|
75
|
+
print(f" TX: {tx['tx_hash']} - {tx['amount']} {detail['payment']['token']}")
|
|
76
|
+
|
|
77
|
+
# Cancel a pending payment
|
|
78
|
+
result = client.payments.cancel("payment-uuid")
|
|
79
|
+
# result["payment"]["status"] == "cancelled"
|
|
80
|
+
|
|
81
|
+
# Submit tx hash for verification (pool + txid mode)
|
|
82
|
+
result = client.payments.confirm("payment-uuid", "abc123def456...")
|
|
83
|
+
# result["verified"], result["status"], result["amount_received"]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Supported Networks and Tokens
|
|
87
|
+
|
|
88
|
+
| Network | Blockchain | Tokens |
|
|
89
|
+
|---------|------------|--------|
|
|
90
|
+
| `TRC20` | Tron | USDT |
|
|
91
|
+
| `BEP20` | BNB Smart Chain | USDT, USDC |
|
|
92
|
+
| `ERC20` | Ethereum | USDT, USDC |
|
|
93
|
+
| `POLYGON` | Polygon | USDT, USDC |
|
|
94
|
+
| `ARBITRUM` | Arbitrum | USDT, USDC |
|
|
95
|
+
|
|
96
|
+
The `token` parameter defaults to `"USDT"` if not specified, ensuring backward compatibility.
|
|
97
|
+
|
|
98
|
+
### Project Management (Admin)
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from payzcore import PayzCore
|
|
102
|
+
|
|
103
|
+
admin = PayzCore("mk_xxx", master_key=True)
|
|
104
|
+
|
|
105
|
+
# Create a project
|
|
106
|
+
project = admin.projects.create(
|
|
107
|
+
name="My Store",
|
|
108
|
+
slug="my-store",
|
|
109
|
+
webhook_url="https://example.com/webhooks/payzcore",
|
|
110
|
+
)
|
|
111
|
+
print(f"API Key: {project['project']['api_key']}")
|
|
112
|
+
|
|
113
|
+
# List projects
|
|
114
|
+
projects = admin.projects.list()
|
|
115
|
+
for p in projects["projects"]:
|
|
116
|
+
print(f"{p['name']} ({p['slug']}): {'active' if p['is_active'] else 'inactive'}")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Webhook Verification
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from payzcore import verify_signature, construct_event, WebhookSignatureError
|
|
123
|
+
|
|
124
|
+
# In your webhook handler
|
|
125
|
+
def handle_webhook(request):
|
|
126
|
+
body = request.body.decode("utf-8")
|
|
127
|
+
signature = request.headers["X-PayzCore-Signature"]
|
|
128
|
+
secret = "whsec_xxx" # From project creation
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
event = construct_event(body, signature, secret)
|
|
132
|
+
except WebhookSignatureError:
|
|
133
|
+
return {"error": "Invalid signature"}, 401
|
|
134
|
+
|
|
135
|
+
if event["event"] == "payment.completed":
|
|
136
|
+
print(f"Payment {event['payment_id']} completed!")
|
|
137
|
+
print(f"Amount: {event['paid_amount']} {event['token']} on {event['network']}")
|
|
138
|
+
|
|
139
|
+
return {"ok": True}, 200
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> **Note:** The `address` parameter is only used with static wallet projects in dedicated mode. For HD wallet projects, this parameter is ignored.
|
|
143
|
+
|
|
144
|
+
## Static Wallet Mode
|
|
145
|
+
|
|
146
|
+
When the PayzCore project is configured with a static wallet, the API works the same way but may return additional fields in the response:
|
|
147
|
+
|
|
148
|
+
| Field | Type | Description |
|
|
149
|
+
|-------|------|-------------|
|
|
150
|
+
| `notice` | `str` | Instructions for the payer (e.g. "Send exact amount") |
|
|
151
|
+
| `original_amount` | `str` | The original requested amount before any adjustments |
|
|
152
|
+
| `requires_txid` | `bool` | Whether the payer must submit their transaction ID |
|
|
153
|
+
|
|
154
|
+
In dedicated address mode, you can specify which static address to assign to a customer using the `address` parameter on payment creation. In shared address mode, the project's single static address is used automatically.
|
|
155
|
+
|
|
156
|
+
## Before Going Live
|
|
157
|
+
|
|
158
|
+
**Always test your setup before accepting real payments:**
|
|
159
|
+
|
|
160
|
+
1. **Verify your xPub** — In the PayzCore dashboard, click "Verify Key" when adding your wallet. Compare address #0 with your wallet app's first receiving address. They must match.
|
|
161
|
+
2. **Send a test payment** — Create a monitoring request for $1–5 and send the funds to the assigned address. Verify they arrive in your wallet.
|
|
162
|
+
3. **Test sweeping** — Send the test funds back out to confirm you control the derived addresses with your private keys.
|
|
163
|
+
|
|
164
|
+
> **Warning:** A wrong xPub key generates addresses you don't control. Funds sent to those addresses are permanently lost. PayzCore is watch-only and cannot recover funds. Please take 2 minutes to verify.
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
client = PayzCore(
|
|
170
|
+
"pk_live_xxx",
|
|
171
|
+
base_url="https://api.payzcore.com", # API base URL
|
|
172
|
+
timeout=30.0, # Request timeout (seconds)
|
|
173
|
+
max_retries=2, # Retries on 5xx errors
|
|
174
|
+
master_key=False, # Use x-master-key header
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Error Handling
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from payzcore import (
|
|
182
|
+
PayzCore,
|
|
183
|
+
PayzCoreError,
|
|
184
|
+
AuthenticationError,
|
|
185
|
+
ValidationError,
|
|
186
|
+
RateLimitError,
|
|
187
|
+
NotFoundError,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
client = PayzCore("pk_live_xxx")
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
payment = client.payments.create(
|
|
194
|
+
amount=50, network="TRC20", external_ref="user-123"
|
|
195
|
+
)
|
|
196
|
+
except AuthenticationError:
|
|
197
|
+
print("Invalid API key")
|
|
198
|
+
except ValidationError as e:
|
|
199
|
+
print(f"Invalid params: {e.message}")
|
|
200
|
+
if e.details:
|
|
201
|
+
for d in e.details:
|
|
202
|
+
print(f" {d['path']}: {d['message']}")
|
|
203
|
+
except RateLimitError as e:
|
|
204
|
+
print(f"Rate limited. Daily: {e.is_daily}, retry after: {e.retry_after}")
|
|
205
|
+
except NotFoundError:
|
|
206
|
+
print("Resource not found")
|
|
207
|
+
except PayzCoreError as e:
|
|
208
|
+
print(f"API error {e.status}: {e.message}")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Error Classes
|
|
212
|
+
|
|
213
|
+
| Class | Status | When |
|
|
214
|
+
|-------|--------|------|
|
|
215
|
+
| `AuthenticationError` | 401 | Invalid/missing API key |
|
|
216
|
+
| `ForbiddenError` | 403 | Project deactivated |
|
|
217
|
+
| `NotFoundError` | 404 | Payment/resource not found |
|
|
218
|
+
| `ValidationError` | 400 | Invalid request body |
|
|
219
|
+
| `RateLimitError` | 429 | Rate limit or daily plan limit exceeded |
|
|
220
|
+
| `IdempotencyError` | 409 | `external_order_id` reused with different `external_ref` |
|
|
221
|
+
| `WebhookSignatureError` | — | Invalid webhook signature |
|
|
222
|
+
|
|
223
|
+
## Requirements
|
|
224
|
+
|
|
225
|
+
- Python 3.9+
|
|
226
|
+
- httpx
|
|
227
|
+
|
|
228
|
+
## See Also
|
|
229
|
+
|
|
230
|
+
- [Getting Started](https://docs.payzcore.com/getting-started) — Account setup and first payment
|
|
231
|
+
- [Authentication & API Keys](https://docs.payzcore.com/guides/authentication) — API key management
|
|
232
|
+
- [Webhooks Guide](https://docs.payzcore.com/guides/webhooks) — Events, headers, and signature verification
|
|
233
|
+
- [Supported Networks](https://docs.payzcore.com/guides/networks) — Available networks and tokens
|
|
234
|
+
- [Error Reference](https://docs.payzcore.com/guides/errors) — HTTP status codes and troubleshooting
|
|
235
|
+
- [API Reference](https://docs.payzcore.com) — Interactive API documentation (Scalar UI)
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""PayzCore Python SDK - Blockchain transaction monitoring API client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .client import HttpClient
|
|
6
|
+
from .resources.payments import Payments
|
|
7
|
+
from .resources.projects import Projects
|
|
8
|
+
|
|
9
|
+
# Webhook utilities
|
|
10
|
+
from .webhook import (
|
|
11
|
+
SUPPORTED_NETWORKS,
|
|
12
|
+
SUPPORTED_TOKENS,
|
|
13
|
+
construct_event,
|
|
14
|
+
parse_webhook,
|
|
15
|
+
verify_signature,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Error classes
|
|
19
|
+
from .errors import (
|
|
20
|
+
AuthenticationError,
|
|
21
|
+
ForbiddenError,
|
|
22
|
+
IdempotencyError,
|
|
23
|
+
NotFoundError,
|
|
24
|
+
PayzCoreError,
|
|
25
|
+
RateLimitError,
|
|
26
|
+
ValidationError,
|
|
27
|
+
WebhookSignatureError,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Types
|
|
31
|
+
from .types import (
|
|
32
|
+
AvailableNetwork,
|
|
33
|
+
Network,
|
|
34
|
+
CreatePaymentParams,
|
|
35
|
+
CreatePaymentResponse,
|
|
36
|
+
CreateProjectParams,
|
|
37
|
+
CreateProjectResponse,
|
|
38
|
+
GetPaymentResponse,
|
|
39
|
+
ListPaymentsParams,
|
|
40
|
+
ListPaymentsResponse,
|
|
41
|
+
ListProjectsResponse,
|
|
42
|
+
Payment,
|
|
43
|
+
PaymentDetail,
|
|
44
|
+
PaymentListItem,
|
|
45
|
+
PaymentStatus,
|
|
46
|
+
Project,
|
|
47
|
+
ProjectListItem,
|
|
48
|
+
Token,
|
|
49
|
+
Transaction,
|
|
50
|
+
WebhookEventType,
|
|
51
|
+
WebhookPayload,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__version__ = "1.0.0"
|
|
55
|
+
__all__ = [
|
|
56
|
+
"PayzCore",
|
|
57
|
+
# Webhook
|
|
58
|
+
"verify_signature",
|
|
59
|
+
"construct_event",
|
|
60
|
+
"parse_webhook",
|
|
61
|
+
"SUPPORTED_NETWORKS",
|
|
62
|
+
"SUPPORTED_TOKENS",
|
|
63
|
+
# Errors
|
|
64
|
+
"PayzCoreError",
|
|
65
|
+
"AuthenticationError",
|
|
66
|
+
"ForbiddenError",
|
|
67
|
+
"IdempotencyError",
|
|
68
|
+
"NotFoundError",
|
|
69
|
+
"ValidationError",
|
|
70
|
+
"RateLimitError",
|
|
71
|
+
"WebhookSignatureError",
|
|
72
|
+
# Types
|
|
73
|
+
"AvailableNetwork",
|
|
74
|
+
"Network",
|
|
75
|
+
"Token",
|
|
76
|
+
"PaymentStatus",
|
|
77
|
+
"WebhookEventType",
|
|
78
|
+
"CreatePaymentParams",
|
|
79
|
+
"Payment",
|
|
80
|
+
"CreatePaymentResponse",
|
|
81
|
+
"PaymentListItem",
|
|
82
|
+
"ListPaymentsParams",
|
|
83
|
+
"ListPaymentsResponse",
|
|
84
|
+
"Transaction",
|
|
85
|
+
"PaymentDetail",
|
|
86
|
+
"GetPaymentResponse",
|
|
87
|
+
"CreateProjectParams",
|
|
88
|
+
"Project",
|
|
89
|
+
"CreateProjectResponse",
|
|
90
|
+
"ProjectListItem",
|
|
91
|
+
"ListProjectsResponse",
|
|
92
|
+
"WebhookPayload",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class PayzCore:
|
|
97
|
+
"""PayzCore API client.
|
|
98
|
+
|
|
99
|
+
Usage::
|
|
100
|
+
|
|
101
|
+
from payzcore import PayzCore
|
|
102
|
+
|
|
103
|
+
# Project API (payment monitoring)
|
|
104
|
+
client = PayzCore("pk_live_xxx")
|
|
105
|
+
payment = client.payments.create(
|
|
106
|
+
amount=50,
|
|
107
|
+
external_ref="user-123",
|
|
108
|
+
network="TRC20", # optional
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Admin API (project management)
|
|
112
|
+
admin = PayzCore("mk_xxx", master_key=True)
|
|
113
|
+
projects = admin.projects.list()
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
payments: Payments
|
|
117
|
+
projects: Projects
|
|
118
|
+
|
|
119
|
+
def __init__(
|
|
120
|
+
self,
|
|
121
|
+
api_key: str,
|
|
122
|
+
*,
|
|
123
|
+
base_url: str = "https://api.payzcore.com",
|
|
124
|
+
timeout: float = 30.0,
|
|
125
|
+
max_retries: int = 2,
|
|
126
|
+
master_key: bool = False,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Initialize PayzCore client.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
api_key: Your API key (pk_live_xxx) or master key (mk_xxx).
|
|
132
|
+
base_url: API base URL. Default: https://api.payzcore.com
|
|
133
|
+
timeout: Request timeout in seconds. Default: 30.0
|
|
134
|
+
max_retries: Max retries on 5xx/network errors. Default: 2
|
|
135
|
+
master_key: Use master key auth (x-master-key header). Default: False
|
|
136
|
+
"""
|
|
137
|
+
if not api_key:
|
|
138
|
+
raise ValueError(
|
|
139
|
+
"PayzCore API key is required. Pass your pk_live_xxx or mk_xxx key."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self._http_client = HttpClient(
|
|
143
|
+
api_key,
|
|
144
|
+
base_url=base_url,
|
|
145
|
+
timeout=timeout,
|
|
146
|
+
max_retries=max_retries,
|
|
147
|
+
master_key=master_key,
|
|
148
|
+
)
|
|
149
|
+
self.payments = Payments(self._http_client)
|
|
150
|
+
self.projects = Projects(self._http_client)
|
|
151
|
+
|
|
152
|
+
def close(self) -> None:
|
|
153
|
+
"""Close the underlying HTTP connection pool."""
|
|
154
|
+
self._http_client.close()
|
|
155
|
+
|
|
156
|
+
def __enter__(self) -> "PayzCore":
|
|
157
|
+
return self
|
|
158
|
+
|
|
159
|
+
def __exit__(self, *args: object) -> None:
|
|
160
|
+
self.close()
|