Official ZyndPay Python SDK — accept USDT payments with a few lines of code
Project description
zyndpay
Official ZyndPay Python SDK — accept USDT TRC20 payments with a few lines of code.
Requirements
- Python 3.8+
- A ZyndPay account and API key
Installation
pip install zyndpay
Quickstart
from zyndpay import ZyndPay
zyndpay = ZyndPay("zyp_live_sk_...")
# Create a payment request
payin = zyndpay.payins.create(amount="100")
print(payin["data"]["address"]) # Send USDT TRC20 here
print(payin["data"]["paymentUrl"]) # Redirect your customer here
# Check your balance
balance = zyndpay.balances.get()
print(balance["data"]["balance"]) # e.g. "97.00"
Configuration
zyndpay = ZyndPay(
api_key="zyp_live_sk_...", # required
webhook_secret="whsec_...", # optional — needed for webhook verification
base_url="https://api.zyndpay.io/v1", # optional — override for self-hosted
timeout=30, # optional — seconds (default: 30)
max_retries=2, # optional — retries on network errors (default: 2)
)
API key types
| Prefix | Type |
|---|---|
zyp_live_sk_ |
Live secret key |
zyp_live_pk_ |
Live publishable key |
zyp_test_sk_ |
Sandbox secret key |
zyp_test_pk_ |
Sandbox publishable key |
Payins
Create a payin
payin = zyndpay.payins.create(
amount="100", # USDT amount (minimum 1)
external_ref="order_9f8e7d", # your internal order ID (optional)
expires_in_seconds=3600, # 1 hour — default is 30min (optional)
metadata={"user_id": "usr_123"}, # stored as-is (optional)
success_url="https://yoursite.com/success",
cancel_url="https://yoursite.com/cancel",
)
print(payin["data"]["transactionId"]) # "uuid"
print(payin["data"]["address"]) # TRC20 deposit address
print(payin["data"]["paymentUrl"]) # hosted payment page URL
print(payin["data"]["qrCodeUrl"]) # QR code data URL
print(payin["data"]["amount"]) # "100"
print(payin["data"]["status"]) # "AWAITING_PAYMENT"
print(payin["data"]["expiresAt"]) # ISO timestamp
Get a payin
payin = zyndpay.payins.get("pay_abc123")
List payins
result = zyndpay.payins.list(status="CONFIRMED", page=1, limit=20)
for payin in result["data"]["items"]:
print(payin["id"], payin["status"])
print(result["data"]["total"])
Payin statuses
| Status | Description |
|---|---|
PENDING |
Just created |
AWAITING_PAYMENT |
Deposit address assigned, waiting for funds |
CONFIRMING |
Payment detected, waiting for confirmations |
CONFIRMED |
Payment confirmed — balance credited |
EXPIRED |
Payment window elapsed |
OVERPAID |
More than expected was sent |
UNDERPAID |
Less than expected was sent |
FAILED |
Processing failed |
Sandbox / Test Mode
Use your sandbox API key (zyp_test_sk_...) and pass sandbox=True when creating a payin. Then call simulate to instantly confirm it without real funds.
zyndpay = ZyndPay("zyp_test_sk_...")
# Create a sandbox payin
payin = zyndpay.payins.create(amount="100", sandbox=True)
# Instantly simulate confirmation
confirmed = zyndpay.payins.simulate(payin["data"]["transactionId"])
print(confirmed["status"]) # "CONFIRMED"
Withdrawals
Request a withdrawal
withdrawal = zyndpay.withdrawals.create(
amount="50", # USDT amount
idempotency_key="idempotency-key-123" # optional
)
print(withdrawal["status"]) # "PENDING_REVIEW"
print(withdrawal["fee"]) # "1.50" (1% fee, min $1.50)
print(withdrawal["netAmount"]) # "48.50"
Get / list withdrawals
withdrawal = zyndpay.withdrawals.get("wdr_abc123")
result = zyndpay.withdrawals.list(status="CONFIRMED", page=1, limit=20)
Cancel a withdrawal
zyndpay.withdrawals.cancel("wdr_abc123") # only while PENDING_REVIEW
Withdrawal statuses
| Status | Description |
|---|---|
PENDING_REVIEW |
Awaiting admin approval |
APPROVED |
Approved, queued for processing |
PROCESSING |
Being broadcast to blockchain |
BROADCAST |
Transaction sent |
CONFIRMED |
On-chain confirmed |
REJECTED |
Rejected by admin |
CANCELLED |
Cancelled by merchant |
FAILED |
Broadcast failed |
Transactions
# Get a single transaction
tx = zyndpay.transactions.get("txn_abc123")
# List with filters
result = zyndpay.transactions.list(
type="PAYIN", # "PAYIN" | "PAYOUT"
status="CONFIRMED",
from_date="2026-01-01",
to_date="2026-03-31",
page=1,
limit=50,
)
Balances
balance = zyndpay.balances.get()
print(balance["data"]["currency"]) # "USDT_TRC20"
print(balance["data"]["balance"]) # current balance
Webhooks
ZyndPay sends signed webhook events to your endpoint. Always verify the signature before processing.
Verify a webhook (Flask example)
from flask import Flask, request, abort
from zyndpay import ZyndPay
zyndpay = ZyndPay(
api_key="zyp_live_sk_...",
webhook_secret="whsec_...",
)
app = Flask(__name__)
@app.route("/webhooks/zyndpay", methods=["POST"])
def handle_webhook():
# IMPORTANT: use raw body — do not parse JSON before verifying
payload = request.get_data(as_text=True)
signature = request.headers.get("X-ZyndPay-Signature", "")
try:
event = zyndpay.webhooks.verify(payload, signature)
except ValueError as e:
return str(e), 400
# All payin events include: transactionId, status, currency, chain, externalRef
if event["event"] == "payin.confirmed":
# Also has: amount, amountRequested, txHash, confirmedAt
print("Payment confirmed:", event["data"]["externalRef"], event["data"]["amount"])
elif event["event"] == "payin.expired":
print("Payment expired:", event["data"]["externalRef"])
elif event["event"] == "withdrawal.confirmed":
print("Withdrawal confirmed:", event["data"])
return {"received": True}, 200
Webhook event types
| Event | Trigger |
|---|---|
payin.created |
Payin created |
payin.confirming |
Payment detected on-chain |
payin.confirmed |
Payment fully confirmed |
payin.expired |
Payin expired before payment |
payin.overpaid |
More than expected received |
payin.underpaid |
Less than expected received |
payin.failed |
Processing error |
withdrawal.requested |
Withdrawal created |
withdrawal.approved |
Approved by admin |
withdrawal.rejected |
Rejected by admin |
withdrawal.broadcast |
Sent to blockchain |
withdrawal.confirmed |
On-chain confirmed |
withdrawal.failed |
Broadcast failed |
Error Handling
All SDK errors inherit from ZyndPayError and include status_code and an optional request_id.
from zyndpay import (
ZyndPayError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
)
try:
payin = zyndpay.payins.create(amount="5") # below minimum
except ValidationError as e:
print("Bad request:", e) # "amount must be >= 25"
print("Status code:", e.status_code) # 400
except AuthenticationError:
print("Invalid API key")
except NotFoundError:
print("Resource not found")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except ZyndPayError as e:
print(f"API error {e.status_code}: {e}")
License
MIT — see LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zyndpay-1.5.0.tar.gz.
File metadata
- Download URL: zyndpay-1.5.0.tar.gz
- Upload date:
- Size: 23.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe0d0caed39638b07a72915372fca4d652ddbf7230a8ba7403feb27ea5c51060
|
|
| MD5 |
36aebe7babe2113e8ab9ecc84579590d
|
|
| BLAKE2b-256 |
d03fac80a0eb3d81ff778c784011a50cda929d470ca3ee0a11e6a12ec5501a96
|
File details
Details for the file zyndpay-1.5.0-py3-none-any.whl.
File metadata
- Download URL: zyndpay-1.5.0-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e44acb8cec3788df7795162c686428b0174e65d04c0695bc3cd01cc728751642
|
|
| MD5 |
5a321d43dd35c89a05cbdf8d9c1ace67
|
|
| BLAKE2b-256 |
c37b5d5d7c922c25a2ceb3a9328eb7b73a1548a64ff4bde155bca351718d1bea
|