Official Python SDK for the SurePay Payment Gateway API
Project description
surepay-python-sdk
Official Python client library for the SurePay Merchant API.
Requirements
- Python 3.9+
- Zero non-stdlib dependencies
Install
pip install surepay
Quick start
import os
from surepay import SurePay, SurePayException
client = SurePay.builder(
os.environ["SUREPAY_API_KEY"], # tpay_live_... or tpay_test_...
os.environ["SUREPAY_API_SECRET"], # tpay_sec_... — enables auto HMAC signing
).build()
# Check wallet balance
balance = client.balance.get()
print(f"Available: {balance.available} VND")
# Create a deposit order (thu hộ)
deposit = client.deposits.create(
amount=100_000,
request_id="ORD-20260610-001",
)
print("Checkout URL:", deposit.checkout_url)
# Create a payout (chi hộ)
try:
payout = client.payouts.create(
amount=500_000,
bank_code="VCB",
bank_account="1234567890",
account_name="NGUYEN VAN A",
memo="Salary June 2026",
)
print("Payout ID:", payout.id)
except SurePayException as e:
if e.is_insufficient_balance():
print("Not enough balance — top up first")
Configuration
client = SurePay.builder(api_key, api_secret) \
.base_url("https://api.surepay.one/merchant/v1") \
.timeout(15) \
.max_retries(3) \
.build()
Or use direct instantiation:
client = SurePay(
api_key=os.environ["SUREPAY_API_KEY"],
api_secret=os.environ["SUREPAY_API_SECRET"],
)
| Option | Default | Description |
|---|---|---|
base_url(str) |
https://api.surepay.one/merchant/v1 |
Override base URL for local dev or staging |
timeout(int) |
30 |
HTTP request timeout in seconds |
max_retries(int) |
3 |
Retry attempts on 5xx and network errors |
Authentication
Every request requires an API key sent as an X-API-Key header. When api_secret is provided to builder(), every outgoing request is automatically signed with HMAC-SHA256 — the X-Signature and X-Timestamp headers are attached with no extra code needed.
API reference
Balance
client.balance.get()
Get current wallet balance. Requires: balance:read scope.
balance = client.balance.get()
# balance.balance — total wallet balance in VND
# balance.hold — reserved for in-flight transactions
# balance.available — balance.balance - balance.hold
# balance.currency — always "VND"
Deposits
client.deposits.list(params)
Paginated list of deposit (thu hộ) orders. Requires: deposits:read scope.
from surepay.params import DepositsListParams
result = client.deposits.list(
DepositsListParams(
page=1,
page_size=20,
status="success", # pending|processing|success|failed|expired|cancelled
from_date="2026-06-01", # YYYY-MM-DD
to_date="2026-06-30",
)
)
# result.items — list[Deposit]
# result.total — total matching records
# result.total_pages — total pages
client.deposits.create(**kwargs)
Create a new deposit order. Returns a checkout_url (redirect) and qr_code (VietQR). Requires: deposits:write scope.
deposit = client.deposits.create(
amount=100_000, # VND, required
request_id="ORD-20260610-001", # your order ID — optional, for idempotency
# Chính chủ verification — all optional:
sender_bank_id="970436",
sender_bank_name="Vietcombank",
sender_account="1234567890",
sender_name="NGUYEN VAN A",
)
print(deposit.checkout_url)
print(deposit.qr_code)
Response fields:
| Field | Type | Description |
|---|---|---|
id |
str | SurePay transaction UUID |
request_id |
str | Your order ID |
amount |
int | Amount in VND |
status |
str | pending, processing, success, failed, expired, cancelled |
checkout_url |
str | Redirect URL for payer |
qr_code |
str | VietQR data string |
created_at |
str | ISO 8601 timestamp |
updated_at |
str | ISO 8601 timestamp |
client.deposits.get(id)
Fetch a single deposit order by UUID. Requires: deposits:read scope.
deposit = client.deposits.get("uuid-here")
# deposit.status: 'pending' | 'success' | ...
Payouts
client.payouts.list(params)
Paginated list of payout (chi hộ) orders. Requires: payouts:read scope.
from surepay.params import PayoutsListParams
result = client.payouts.list(
PayoutsListParams(
page=1,
page_size=20,
status="success", # pending|processing|success|failed
from_date="2026-06-01",
to_date="2026-06-30",
)
)
client.payouts.create(**kwargs)
Initiate a payout bank transfer. Funds are deducted from your wallet immediately on success. Requires: payouts:write scope.
Payouts are irreversible once status moves past
pending. Verify bank details withclient.bank_inquiry.verify()first.
payout = client.payouts.create(
amount=500_000, # VND, required
bank_code="VCB", # required (VCB, MB, TCB, ACB, ...)
bank_account="1234567890", # required
account_name="NGUYEN VAN A", # required — use UPPERCASE
memo="Salary June", # required — transfer memo
bank_name="Vietcombank", # optional
)
client.payouts.get(id)
Fetch a single payout by UUID. Requires: payouts:read scope.
payout = client.payouts.get("uuid-here")
# payout.status: 'pending' | 'success' | ...
Bank Inquiry
client.bank_inquiry.verify(bank_code, bank_account)
Look up the account holder name for a bank account. Call this before creating a payout to confirm the recipient. Requires: payouts:read scope.
result = client.bank_inquiry.verify(
bank_code="VCB",
bank_account="1234567890",
)
print("Account name:", result.account_name)
Idempotency
Pass an idempotency_key to any create() method. The key is forwarded as an Idempotency-Key header — safe to retry on network errors without risk of duplicate transactions.
deposit = client.deposits.create(
amount=100_000,
request_id="ORD-001",
idempotency_key="ORD-001",
)
Webhook verification
Every inbound webhook event from SurePay is HMAC-signed. Pass the raw request body bytes (before any JSON parsing) to client.webhooks.verify():
# FastAPI example
from fastapi import FastAPI, Request, Response
import json
app = FastAPI()
@app.post("/webhook/surepay")
async def handle_webhook(request: Request):
body = await request.body()
if not client.webhooks.verify(body):
return Response(status_code=401)
event = json.loads(body)
match event.get("event"):
case "deposit.success" | "deposit.failed":
handle_deposit(event)
case "payout.success" | "payout.failed":
handle_payout(event)
return Response(status_code=200)
Or pass the X-Surepay-Signature header value explicitly:
sig = request.headers.get("X-Surepay-Signature")
valid = client.webhooks.verify(body, sig)
Error handling
from surepay import SurePayException
try:
payout = client.payouts.create(...)
except SurePayException as e:
# Convenience helpers
if e.is_not_found(): # 404 not_found
pass
if e.is_rate_limit(): # 429 rate_limit_exceeded
pass
if e.is_insufficient_balance(): # 422 insufficient_balance
pass
if e.is_duplicate(): # 409 duplicate_request
pass
# Full details
print(f"HTTP {e.http_status} code={e.code} {e.message}")
Error codes:
| HTTP | code |
Meaning |
|---|---|---|
| 400 | validation_error |
Invalid request body or parameters |
| 401 | unauthorized |
Missing or invalid API key |
| 401 | signature_invalid |
HMAC signature failed or timestamp > 5 min |
| 403 | permission_denied |
API key lacks required scope |
| 403 | ip_not_allowed |
Request IP not in allowlist |
| 404 | not_found |
Resource not found |
| 409 | duplicate_request |
Idempotency key conflict |
| 422 | insufficient_balance |
Top up wallet first |
| 422 | invalid_state_transition |
Operation not allowed for current status |
| 429 | rate_limit_exceeded |
Slow down — back off and retry |
| 500 | internal_error |
Server error |
HMAC signing
When api_secret is set, all requests are signed automatically. The signing algorithm for manual use:
body_hash = sha256(body_bytes).hexdigest()
signing_str = f"{timestamp}\n{method}\n{path}\n{body_hash}"
signature = "sha256=" + hmac.new(api_secret.encode(), signing_str.encode(), sha256).hexdigest()
Attach as headers: X-Signature: <signature> and X-Timestamp: <unix_timestamp>.
Signatures expire after 300 seconds — generate per-request, never cache or reuse.
License
MIT
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 surepay-0.1.0.tar.gz.
File metadata
- Download URL: surepay-0.1.0.tar.gz
- Upload date:
- Size: 12.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2064e1438c2cfbc2c96cc048675e8702241aa4dc4f28d70b21a293368ccc2776
|
|
| MD5 |
47cbdaff8672e5e0437bae63d1f2d594
|
|
| BLAKE2b-256 |
b40a4827057eb13a2972edd0a25ebc60a726dc3da4b873b0c6b66c459866952e
|
Provenance
The following attestation bundles were made for surepay-0.1.0.tar.gz:
Publisher:
release.yml on surepay-one/surepay-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
surepay-0.1.0.tar.gz -
Subject digest:
2064e1438c2cfbc2c96cc048675e8702241aa4dc4f28d70b21a293368ccc2776 - Sigstore transparency entry: 1775686206
- Sigstore integration time:
-
Permalink:
surepay-one/surepay-python-sdk@8eb400b00bd09fda985fd9affa6ab0447056d363 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/surepay-one
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8eb400b00bd09fda985fd9affa6ab0447056d363 -
Trigger Event:
push
-
Statement type:
File details
Details for the file surepay-0.1.0-py3-none-any.whl.
File metadata
- Download URL: surepay-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5be6026ecdb5745de43ffb36e365b81462932baade4ca412827900d154448970
|
|
| MD5 |
26437814d207eff89f221e138010eb60
|
|
| BLAKE2b-256 |
31b0d9cb0bfdbfaec3d7fa49083836ec2597a8d1714ef07703b7055bcc3081c3
|
Provenance
The following attestation bundles were made for surepay-0.1.0-py3-none-any.whl:
Publisher:
release.yml on surepay-one/surepay-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
surepay-0.1.0-py3-none-any.whl -
Subject digest:
5be6026ecdb5745de43ffb36e365b81462932baade4ca412827900d154448970 - Sigstore transparency entry: 1775686404
- Sigstore integration time:
-
Permalink:
surepay-one/surepay-python-sdk@8eb400b00bd09fda985fd9affa6ab0447056d363 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/surepay-one
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8eb400b00bd09fda985fd9affa6ab0447056d363 -
Trigger Event:
push
-
Statement type: