API 文档
公开可查的接入文档:认证与签名、接口参考、错误码与 Webhook 回调。
开始前
接入流程
网关只做两件事:按你的请求创建账单,并在链上到账确认后回调你。资金全程经收款合约直达你的归集地址,不经手网关。
你的服务器 → 网关: POST /v1/invoice 创建账单,拿到 pay_url
买家 → 网关: 打开 pay_url,选链选币并付款
网关 → 你的服务器: Webhook 回调(账单完成,confirmed=true)
你的服务器 → 网关: 返回 200 且响应体为 ok你的服务器 → 网关: GET /v1/deposit/address?uid=xxx&chain=base&crypto=USDC
网关 → 你的服务器: 返回 deposit_address(同一 uid + 链固定不变)
用户 → 区块链: 向该地址转账
网关 → 你的服务器: Webhook 回调(充值到账)- 账单收款是定额、限时的一次性账单,适合下单、订阅计费。
- 充值收款是给每个用户分配的专属地址,可长期反复转入,适合钱包、交易所式入金。
- 金额一律用字符串传输;时间一律为 ISO 8601 UTC。
- 所有接口路径不带尾部斜杠。
获取凭据
每个项目有独立的 AppID 与 HMAC 密钥,用于接口鉴权与签名。密钥只显示一次,请存入密钥管理服务;一旦泄露,立即在设置页重置。
前往设置查看 AppID 与 HMAC 密钥网关地址与规则
本控制台对应的网关地址:
https://pay.usdzova.com- 接口路径形如 /v1/invoice,不带尾部斜杠。
- 币种符号一律大写,如 USDT、USDC。
- 链码用小写,如 ethereum、bsc、base、tron,完整列表见下方「链与币种」。
- 账单的计价法币与买家实际支付的币种是两件事:currency 决定 amount 的计价单位,买家付什么由 methods 决定。
认证与签名
请求头
除公开接口(查询账单、支付页)外,所有 /v1 接口都要带这四个头。
| 请求头 | 说明 |
|---|---|
| XC-Appid | 项目 AppID |
| XC-Timestamp | Unix 秒;与服务器时间相差不得超过 300 秒 |
| XC-Nonce | 随机串,必填;同一 AppID 下 300 秒内不可重复 |
| XC-Signature | HMAC-SHA256 签名,小写十六进制 |
签名算法
签名内容为「nonce + 时间戳 + 原始请求体」,用 HMAC 密钥做 HMAC-SHA256。
message = XC-Nonce + XC-Timestamp + request_body
signature = HMAC-SHA256(message, hmac_key) # 小写十六进制签名接口按固定顺序校验,命中即返回,便于定位问题:
- 1001 AppID 无效 → 1002 IP 不在白名单 → 1004 项目未就绪 → 1008 时间戳缺失或超窗
- 1003 签名错误(签名校验在时间戳之后)
- 缺少 Nonce 返回 1000,Nonce 在 300 秒内重复返回 1009
各语言示例
示例一:法币计价——创建一笔 29.99 USD 的账单,买家按汇率折算后支付。
import hashlib
import hmac
import json
import time
import uuid
import requests
base = "https://pay.usdzova.com"
appid = "XC-A3BK7NMG"
secret = "your_hmac_key"
payload = {
"out_no": "order-20260602-001",
"title": "Premium Plan",
"currency": "USD",
"amount": "29.99",
}
# separators 去掉空格:签名与发送必须是同一串字节
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
nonce = uuid.uuid4().hex
signature = hmac.new(
secret.encode(),
f"{nonce}{timestamp}{body}".encode(),
hashlib.sha256,
).hexdigest()
resp = requests.post(
f"https://pay.usdzova.com/v1/invoice",
data=body.encode("utf-8"), # 传原始字节,不要用 json=payload
headers={
"XC-Appid": appid,
"XC-Timestamp": timestamp,
"XC-Nonce": nonce,
"XC-Signature": signature,
"Content-Type": "application/json",
},
timeout=10,
)
print(resp.status_code, resp.json())示例二:币本位计价——要求买家直接支付 10 USDT,金额不经过汇率换算。与上面的差异只有 payload 里的 pricing_type / currency / amount。
import hashlib
import hmac
import json
import time
import uuid
import requests
base = "https://pay.usdzova.com"
appid = "XC-A3BK7NMG"
secret = "your_hmac_key"
# 币本位:直接以币数计价,不做汇率换算
payload = {
"out_no": "order-20260701-002",
"title": "Premium Plan",
"pricing_type": "crypto",
"currency": "USDT",
"amount": "10",
}
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
nonce = uuid.uuid4().hex
signature = hmac.new(
secret.encode(),
f"{nonce}{timestamp}{body}".encode(),
hashlib.sha256,
).hexdigest()
resp = requests.post(
f"https://pay.usdzova.com/v1/invoice",
data=body.encode("utf-8"),
headers={
"XC-Appid": appid,
"XC-Timestamp": timestamp,
"XC-Nonce": nonce,
"XC-Signature": signature,
"Content-Type": "application/json",
},
timeout=10,
)
print(resp.status_code, resp.json())接口参考
接口列表
| 方法 | 路径 | 说明 | 签名 | 限流 |
|---|---|---|---|---|
| POST | /v1/invoice | 创建账单收款 | 需要 | 256/分钟,按 IP |
| GET | /v1/invoice/{sys_no} | 查询账单收款 | 不需要 | 120/分钟,按单号 + IP |
| GET | /v1/deposit/address | 获取充值收款地址 | 需要 | 60/分钟,按项目 + IP |
| GET | /pay/{sys_no} | 托管支付页(给买家打开) | 不需要 | — |
响应格式
成功时直接返回业务 JSON。失败时返回统一结构,HTTP 状态码随错误码变化。
{
"code": "1001",
"message": "AppID无效",
"detail": ""
}错误码
以 code 判断错误类型,HTTP 状态码随错误码变化。只列商户调用会遇到的错误码。
| 错误码 | 说明 | HTTP |
|---|---|---|
| 1000 | 参数错误 | 400 |
| 1001 | AppID 无效 | 400 |
| 1002 | IP 不在白名单 | 403 |
| 1003 | 签名错误 | 403 |
| 1004 | 项目未配置完整,detail 会列出缺哪项 | 400 |
| 1005 | 无访问权限 | 403 |
| 1006 | 手续费不足 | 403 |
| 1007 | out_no 已存在但参数不一致 | 400 |
| 1008 | 时间戳缺失或超出 300 秒 | 400 |
| 1009 | Nonce 重复 | 400 |
| 2000 / 2001 / 2002 | 无效链 / 无效币种 / 该链不支持此币种 | 400 |
| 2003 / 2005 | 地址非校验和格式 / 链与币种设置不匹配 | 400 |
| 3006 | 金额精度超过该链上代币支持的小数位 | 400 |
| 4000 / 4001 | 无效 uid / 未配置该链类型的归集地址 | 400 |
| 5000 | 不支持的计价法币 | 400 |
| 5003 / 5005 | 账单时间错误 / 无效 sys_no | 400 |
| 5007 / 5008 | 不允许的链与币种 / 无可用账单收款方式 | 400 |
| 5009 | 待支付账单过多,请勿滥用 | 400 |
| 5011 / 5012 | 账单不存在 / 账单已过期 | 400 |
| 5014 | 计价币种缺少可用价格源 | 400 |
创建账单收款
POST /v1/invoice(需要签名)。创建成功后返回 pay_url,把买家引导到该地址完成选链、选币与付款。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| out_no | string | 是 | 商户订单号,最长 32 位,项目内唯一 |
| title | string | 是 | 账单标题,最长 32 位 |
| pricing_type | string | 否 | 计价方式:fiat(默认,法币计价)/ crypto(币本位,amount 即应付币数) |
| currency | string | 是 | 计价单位:法币代码(如 USD、CNY);pricing_type=crypto 时填币种符号(如 USDT) |
| amount | string | 是 | 计价金额,0.00000001 – 1000000,最多 8 位小数;币本位时是应付币数 |
| duration | integer | 否 | 有效期分钟数,5 – 30,默认 10 |
| methods | object | 否 | 限定收款方式,形如 {"USDT": ["ethereum"]} |
| notify_url | string | 否 | 账单级回调地址,优先于项目默认通知地址 |
| return_url | string | 否 | 支付完成后买家同步跳回的地址 |
- methods 不传时使用项目当前全部可用组合;传了必须是子集,否则报 5008(未知币种 2001、未知链 2000)。
- 付款数量以响应中的 pay_amount 为准。它由汇率换算得出,钱包直收模式下还可能带区分账单的尾差;链上到账必须与 pay_amount 完全相等才会结单,少付、多付都不会自动完成。
- out_no 幂等:已存在且 title、currency、amount、notify_url、return_url 与旧账单一致时返回原账单(HTTP 200),不一致返回 1007。
- 项目待支付账单达到 100 笔时拒绝新建,返回 5009。
- 如果最终只剩一个收款组合,创建时就会自动选定,chain、crypto、pay_address、pay_amount 直接有值。
币本位计价:把 pricing_type 设为 crypto,currency 填币种符号,amount 就是应付的币数——不经过汇率换算,链上到账必须精确等于该数量。
{
"out_no": "order-20260701-001",
"title": "Premium Plan",
"pricing_type": "crypto",
"currency": "USDT",
"amount": "10",
"methods": { "USDT": ["ethereum", "tron"] }
}- 直收与合约收款都支持:直收按精确金额匹配、不加尾差,同金额并发受直收地址数量限制(地址耗尽返回 5008)。
- 金额必须能被该链上代币的最小单位精确表示,否则返回 3006。
- 计价币种必须有价格源(稳定币按 1:1,其他币需行情),否则返回 5014;该估值只用于计费与风控,不影响应付币数。
{
"out_no": "order-20260602-001",
"title": "Premium Plan",
"currency": "USD",
"amount": "29.99",
"duration": 15,
"methods": {
"USDT": ["ethereum", "base"]
},
"notify_url": "https://merchant.example.com/pay/webhook",
"return_url": "https://merchant.example.com/pay/success"
}{
"appid": "XC-A3BK7NMG",
"sys_no": "INV2606028X7K2P9Q",
"out_no": "order-20260602-001",
"title": "Premium Plan",
"pricing_type": "fiat",
"pricing_crypto": null,
"currency": "USD",
"amount": "29.99",
"methods": { "USDT": ["ethereum", "base"] },
"chain": null,
"crypto": null,
"crypto_address": null,
"pay_address": null,
"pay_amount": null,
"pay_url": "https://pay.example.com/pay/INV2606028X7K2P9Q",
"started_at": "2026-06-02T12:00:00.123456Z",
"created_at": "2026-06-02T12:00:00.123456Z",
"expires_at": "2026-06-02T12:15:00.123456Z",
"notify_url": "https://merchant.example.com/pay/webhook",
"return_url": "https://merchant.example.com/pay/success",
"payment": null,
"status": "waiting",
"risk_level": null,
"risk_score": null
}查询账单收款
GET /v1/invoice/{sys_no}(公开,无需签名),用于账单页或你的服务器轮询对账。该接口不返回 appid、out_no、notify_url 等商户内部信息;账单不存在返回 404。
| 字段 | 说明 |
|---|---|
| sys_no | 系统单号,形如 INV + 日期 + 随机串 |
| pricing_type / currency | 计价方式与计价单位:fiat + 法币代码,或 crypto + 币种符号 |
| chain / crypto | 买家选定的链与币,未选时为 null |
| pay_address | 收款地址 |
| pay_amount | 买家应付数量(以该值为准) |
| payment | 匹配到的链上转账:hash、block、from/to、amount、status、confirm_progress |
| payment_uri | EVM 链的 EIP-681 支付 URI,非 EVM 或无法精确编码时为 null |
| status | waiting / completed / expired |
| risk_level / risk_score | AML 风险等级与分数(LOW / MODERATE / HIGH / SEVERE) |
- waiting:待支付。链上已出现匹配转账但未达确认数时,payment 有值、status 仍是 waiting(控制台展示为「确认中」)。
- completed:已确认,终态。
- expired:已过期,但不是终态——过期前已上链的付款在确认后仍会转为 completed 并发送回调。
- 账单价值低于项目「快速确认阈值」(默认 10 USD)时,观察到付款即确认,不等待区块确认数。
获取充值收款地址
GET /v1/deposit/address(需要签名)。为你的用户分配专属充值地址:同一项目、同一 uid、同一链会稳定返回同一地址。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| uid | string | 是 | 用户标识,1 – 128 位,仅字母、数字、下划线、中划线 |
| chain | string | 是 | 链码,如 ethereum、base、tron |
| crypto | string | 是 | 币种符号,如 USDT |
GET https://pay.usdzova.com/v1/deposit/address?uid=user-10001&chain=base&crypto=USDC{
"deposit_address": "0xAbCd1234..."
}- 请求的链、币种与链上币种关系都必须已启用,且项目属性(测试 / 主网)与链匹配。
- 必须已配置该链类型的归集地址,否则返回 4001。
- EVM 原生币充值地址是合约,付款方转账的 gas limit 需高于 21000(钱包自动估算即可)。
- 常见错误:4000 uid 非法;2000 链不可用;2001 币种不可用;2002 该币未在此链启用。
Webhook 回调
投递与验签
账单完成或充值到账后,网关向你投递 POST application/json 请求,带与接口调用相同的 HMAC 头。账单事件优先发到账单级 notify_url,为空时用项目通知地址;充值事件只用项目通知地址。
| 请求头 | 说明 |
|---|---|
| XC-Appid | 项目 AppID |
| XC-Timestamp | 本次投递的发送时间(Unix 秒) |
| XC-Nonce | 同一事件的多次重试保持不变,用它做幂等去重 |
| XC-Signature | HMAC-SHA256,算法与接口调用一致 |
- 验签必须用收到的原始请求体,不要解析后再序列化。
- 成功条件:HTTP 200,且响应体去掉首尾空白后等于 ok(区分大小写)。
- 单次投递超时 5 秒;网络错误、5xx、408、425、429 视为可重试,其余状态码判为失败不再重试。
- 默认最多投递 5 次(含首次),指数退避约 4 / 8 / 16 / 32 秒,单次退避上限 120 秒。
- 项目通知开关关闭或地址为空时,事件挂起并每 10 分钟重试一次,同样计入投递次数。
- 默认只允许投递到 HTTPS 公网地址;http、localhost、内网地址直接判失败。
- 重试窗口有限,账单状态请用查询接口兜底对账,不要只依赖回调。
账单事件
账单进入 completed 后发送一次。
{
"type": "invoice",
"data": {
"sys_no": "INV2606028X7K2P9Q",
"out_no": "order-20260602-001",
"crypto": "USDT",
"chain": "ethereum",
"pay_address": "0xAbCd1234...",
"pay_amount": "29.870001",
"hash": "0xabc123...",
"block": 12345678,
"confirmed": true,
"risk_level": null,
"risk_score": null
}
}充值事件
充值对应转账达到确认要求后发送一次;已停用的币种到账只入账、不发回调。
{
"type": "deposit",
"data": {
"sys_no": "DXC2606026K9P2QWX",
"uid": "user-10001",
"chain": "base",
"block": 12345678,
"hash": "0xabc123...",
"crypto": "USDC",
"amount": "500",
"confirmed": true,
"risk_level": null,
"risk_score": null
}
}链与币种
公链
Usdzova 当前支持的链如下,chain 参数直接使用「链码」列。
| 名称 | 链码 | 类型 | 原生币 |
|---|---|---|---|
| Ethereum | ethereum | EVM | ETH |
| BNB Smart Chain | bsc | EVM | BNB |
| Polygon PoS | polygon | EVM | POL |
| Arbitrum One | arbitrum-one | EVM | ETH |
| Optimism | optimism | EVM | ETH |
| Base | base | EVM | ETH |
| Avalanche C-Chain | avalanche | EVM | AVAX |
| Linea | linea | EVM | ETH |
| Scroll | scroll | EVM | ETH |
| Tron | tron | Tron | TRX |
币种
crypto 参数使用币种符号,一律大写。实际可用的「链 + 币」组合以创建账单时的校验结果为准。
| 符号 | 名称 | 类型 | 说明 |
|---|---|---|---|
| USDT | Tether USD | stablecoin | ERC-20 / TRC-20 |
| USDC | USD Coin | stablecoin | ERC-20 |
| DAI | Dai | stablecoin | ERC-20 |
| ETH | Ether | native | Ethereum / Arbitrum / Optimism / Base |
| BNB | BNB | native | BNB Smart Chain |
| POL | Polygon | native | Polygon PoS |
| TRX | TRON | native | Tron |
计价法币
账单的计价法币,区分大小写。
| 代码 | 货币 |
|---|---|
| USD | 美元 |
| CNY | 人民币 |
| EUR | 欧元 |
| JPY | 日元 |
| HKD | 港币 |