Files
qianyuandClaude Fable 5.1 0e0f73e2c7 多收款账号:API 动态添加币安账号,订单按账号隔离轮询与匹配
- 新增 accounts 表与接口:POST /api/v1/accounts(用只读接口试拉流水校验,失败 ERR_BINANCE)、
  GET /api/v1/accounts/{id}、POST .../verify、.../disable、.../enable;Secret 以 AES-256-GCM 加密存储,密钥由 API_AUTH_KEY 派生
- 订单增加 account_id(旧库自动补列,默认 default);唯一金额按账号分配;Matcher 按账号各自轮询、缓存与匹配,
  无待核销订单的账号不调币安接口;回填(claim)只在订单所属账号的流水里找
- 收银页按订单所属账号展示 UID / 收款链接 / 二维码;订单与回调体新增 account_id,订单响应新增 receive_link / receive_email
- Go / Python SDK 同步(AccountID、账号方法);协议文档 §2.6;e2e 覆盖多账号隔离、回填、停用与默认账号保护

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 09:39:48 +08:00

7.5 KiB
Raw Permalink Blame History

BinancePayTool 接入协议 v1

签名头与错误码沿用 X-BPG- / BPG 前缀,它们是协议稳定标识,与仓库名无关。

所有接口均为 HTTP + JSON(UTF-8)。金额一律为字符串十进制(如 "10.0037"),最多 8 位小数。 时间戳一律为毫秒 Unix 时间。

1. 签名

商户与网关共享一个密钥 API_AUTH_KEY(下称 secret),同时用于「商户调用网关」与「网关回调商户」。

1.1 商户 → 网关(API 请求签名)

请求头:

Header 说明
X-BPG-Timestamp 当前毫秒时间戳;与服务器偏差超过 300 秒拒绝
X-BPG-Nonce 随机串(≥16 字符);5 分钟内不得重复
X-BPG-Signature 小写十六进制签名

签名串(\n 为换行符;path 只含路径不含查询串与域名;GET/无 body 时 body 视为空串):

string_to_sign = timestamp + "\n" + nonce + "\n" + METHOD + "\n" + path + "\n" + SHA256_HEX(body)
signature      = HMAC_SHA256_HEX(secret, string_to_sign)

1.2 网关 → 商户(回调签名)

回调为 POST callback_url,Content-Type: application/json,请求头同上三个,签名串少 METHOD 与 path:

string_to_sign = timestamp + "\n" + nonce + "\n" + SHA256_HEX(body)

商户校验:时间戳偏差 ≤300 秒、签名一致(常数时间比较)。校验通过返回 HTTP 2xx 即视为成功; 否则网关按 0s, 15s, 60s, 5m, 15m, 1h, 3h 重试 7 次后放弃。回调可能重复投递,商户须按 order_id 幂等处理。

2. 接口

2.1 创建订单 POST /api/v1/orders

请求体:

字段 类型 必填 说明
merchant_order_id string 是 商户侧唯一订单号(≤64 字符);重复返回 ERR_DUPLICATE
account_id string 否 收款账号 ID(多账号模式,见 §2.6);缺省为网关 config.env 里的默认账号 default
currency string 是 币种,须在网关 CURRENCIES 白名单内(如 USDT)
amount string 是 基础金额,>0,小数位 ≤ 网关 AMOUNT_DECIMALS
callback_url string 否 支付结果回调地址
return_url string 否 支付成功后收银页跳转地址(自动附加 ?order_id=&status=)
timeout int 否 有效期秒数,120–86400,缺省用网关 ORDER_TTL

响应 data:

字段 说明
order_id 网关订单号
account_id 收款账号 ID(默认账号为 default)
merchant_order_id 原样返回
status pending
currency / base_amount / pay_amount pay_amount 为实际应付的唯一金额,付款方必须严格按它支付
note_code 6 位备注码;付款方转账备注里带上它可加速/兜底确认(可选)
pay_url 网关托管收银页地址,直接跳转即可
receive_uid 收款方币安 UID(即该订单所属账号的 UID)
receive_link / receive_email 该账号配置的收款链接(二维码内容,可自建收银页:生成二维码、手机端唤起 App)与收款邮箱,未配置为空
created_at / expires_at 毫秒时间戳

2.2 查询订单 GET /api/v1/orders/{order_id}

响应 data 在 2.1 基础上增加:actual_amount、paid_at、matched_by(amount/note/claim)、 binance_order_id(币安 18 位订单编号)、payer_id(付款方 Pay 账户 ID)、overpaid(bool)。 也可用 GET /api/v1/orders/by-merchant/{merchant_order_id} 按商户单号查询。

2.3 关闭订单 POST /api/v1/orders/{order_id}/close

仅 pending 可关闭;成功后状态为 closed,唯一金额进入冷却。

2.4 收银页(免签名,公开)

  • GET /pay/{token}:托管收银页(token 含在 pay_url 中)
  • GET /pay/{token}/status:{status, paid_at, return_url},页面轮询用
  • POST /pay/{token}/claim:{"binance_order_id":"18位数字"}——「我已付款」回填入口,限流 5 次/分钟

2.5 响应外层

{"code":"OK","data":{...}}
{"code":"ERR_AUTH","message":"..."}

code 取值:OK、ERR_AUTH、ERR_PARAM、ERR_DUPLICATE、ERR_NOT_FOUND、ERR_STATE、 ERR_RATE_LIMIT、ERR_ALLOC(唯一金额耗尽)、ERR_BINANCE(账号接口:币安校验失败)、ERR_INTERNAL。

2.6 收款账号(多账号模式)

网关默认只用 config.env 里的一个币安账号收款。以下接口可动态添加更多收款账号(如平台型商户 让每个入驻用户用自己的币安账户收款):每个账号提供自己的只读 API Key/Secret 与 UID,网关按账号 分别轮询流水,订单通过 account_id 指定收款账号,不同账号的订单与流水严格隔离(唯一金额也按账号分配)。

接口 说明
POST /api/v1/accounts 添加账号。请求体:api_key、api_secret、uid(必填),label、receive_link(https 收款链接)、receive_email(可选)。网关会先用该 Key 试拉一条流水,失败返回 ERR_BINANCE(消息含币安原因,如 Key 无效 / IP 未白名单 / 地区限制)。同一 api_key 重复添加视为更新并重新启用
GET /api/v1/accounts/{id} 查询账号(default 可查)
POST /api/v1/accounts/{id}/verify 立即校验该账号的 Key;失败返回 ERR_BINANCE
POST /api/v1/accounts/{id}/disable / /enable 停用 / 启用;停用后不能再用它下单(default 不可停用)

账号响应 data:account_id、label、api_key_masked、uid、receive_link、receive_email、 status(active/disabled)、last_ok(最近一次轮询成功时间,毫秒)、last_err(最近轮询错误)、created_at。 永不返回 Key 与 Secret。

安全提示:Secret 以 AES-256-GCM 加密存储,密钥由 API_AUTH_KEY 派生——更换 API_AUTH_KEY 后已存账号需重新添加。 各账号的 Key 只需「允许读取」权限,IP 白名单填网关服务器出口 IP。

3. 回调体

{
  "event": "paid",                    // paid | underpaid | expired
  "order_id": "…",
  "account_id": "default",            // 收款账号 ID
  "merchant_order_id": "…",
  "status": "paid",
  "currency": "USDT",
  "base_amount": "10",
  "pay_amount": "10.0037",
  "actual_amount": "10.0037",
  "overpaid": false,
  "matched_by": "amount",
  "binance_order_id": "452021922068888888",
  "binance_txn_id": "P_EXAMPLE1234567890",
  "payer_id": "1160000000",
  "paid_at": 1788325910559,
  "timestamp": 1788325912000
}

underpaid:备注/回填命中但实付低于基础金额,需人工处理;expired:超时未支付。

4. 订单状态机

pending ──精确金额/备注码/回填命中──▶ paid
pending ──实付不足(备注/回填)──▶ underpaid
pending ──到期──▶ expired ──宽限期内精确金额到账/回填──▶ paid
pending ──商户关闭──▶ closed

5. 匹配规则(网关内部,供理解)

对每条入账流水(金额>0、币种在白名单、币安 orderId 未被消费过):

  1. 金额与某待支付订单的 pay_amount 完全相等,且到账时间在订单窗口内 → 自动确认;
  2. 否则备注含某订单 note_code(不区分大小写)→ 实付 ≥ 基础金额则确认,否则 underpaid;
  3. 否则保留,等付款方在收银页回填币安订单编号(7 天内有效)。

同一笔币安流水只能核销一个订单(以币安 orderId 幂等)。