返回测试后台API 接入说明
# 接入协议 v1
这是本项目实现的接口说明;隔离验收见验证记录,不能据此宣称真实接入方余额验收完成。
## 认证与建单
接入方通过服务端持有的 API token 调用,头为 `Authorization: Bearer <token>`。token 只存哈希;创建/轮换时仅向明确操作的管理员返回一次,不能交给用户充值浏览器。所有客户与订单查询均按认证接入方隔离。
`POST /v1/orders` 需要 `Idempotency-Key`(1~128 字符)及严格 JSON:
```json
{"externalCustomerId":"customer-001","externalOrderId":"recharge-001","network":"tron-mainnet","asset":"USDT","expectedAmount":"100.000001"}
```
金额只接受正十进制字符串、最多 6 位小数,不接受浮点、科学计数、超精度、负数或零。相同键和语义相同参数返回原订单;换参数返回 409 IDEMPOTENCY_CONFLICT;相同外部订单号不得用新请求键创建另一订单。不同外部订单号可以同金额。
返回 orderId、bindingId、外部标识、固定 address、expectedAmount、createdAt、expiresAt、cancelledAt、depositId、status。有效期严格为服务端创建后 20 分钟。`GET /v1/orders/{orderId}` 查询;`POST /v1/orders/{orderId}/cancel` 取消未关联订单,重复取消幂等,已关联返回 409 ORDER_ALREADY_MATCHED,其他接入方对象返回 404。
无可靠最新链头或无公开地址库存时拒绝新绑定;已绑定地址不跨客户回收。地址余额不是新增到账依据。用户充值页面由接入方实现,首期无公开收银页。
## 到账与查询
到账必须来自 TRON 主网指定 USDT 合约的固化成功回执及 Transfer 日志,使用完整回执日志序号。每笔到账以 network+transactionHash+eventIndex 定位,不能仅用交易哈希、金额或业务订单号去重。
在同接入方/客户/网络/资产下,对未取消且未关联订单按:同金额未过期 → 同金额过期 → 异金额未过期 → 异金额过期;各档按创建时间、订单 ID。过期以首次匹配事务数据库时间判断,到期等号算过期。少付、多付、迟付、无候选均如实通知。
`GET /v1/deposits/{depositId}` 返回当前关联及历史事件快照。`GET /v1/deposits` 支持 limit(1~100,默认50)、cursor、externalCustomerId、from/to(ISO UTC)。返回 items 与 nextCursor。必须使用返回游标连续补查,不能只取第一页。
链上到账早于订单创建时,订单必须在该到账后六小时内创建(等号包含),即使索引服务很晚才发现到账也不能延长。先空订单回调后补建单会产生独立关联通知;已经关联具体订单的到账不改绑,始终一单一笔。多个历史候选先同金额,再按链上时间/区块/交易顺序/日志序号选择。
## 两类事件
事件完整字段见 [接口契约](../specs/001-watch-only-recharge/contracts/api.md)。
- `deposit.confirmed`:真实到账,含 eventId、depositId、真实金额/最小单位/精度、网络/合约/地址/链证据、客户与当时订单关联。无订单时 orderId、externalOrderId、expectedAmount 为 null。
- `deposit.order_linked`:已有到账补充订单关联,另一个 eventId,仍是原 depositId,附 originalEventId、linkedAt 和新订单。它不是新资金通知,也不要求接入方再次加余额。
两种消息各自保存不可变原始 JSON。关联通知可能先交付,原到账可能之后重试;不能用当前查询结果覆盖原回调正文。接入方应分别按 eventId 去重消息、按 depositId/链事件去重资金事实,原子决定自身业务处理。本项目不实现其余额。
## 签名及 ACK
使用 HTTPS+独立接入方 HMAC-SHA256。头:X-Recharge-Key-Id、X-Recharge-Timestamp(Unix秒)、X-Recharge-Event-Id、X-Recharge-Delivery-Id、X-Recharge-Signature(小写 hex)。签名输入为下列五行,以真实 LF 连接,无末尾换行:
```text
recharge.v1
{keyId}
{timestamp}
{eventId}
{sha256原始UTF8正文,小写hex}
```
接收方在解析前保留原始正文,验证签名及 5 分钟时间窗口,再持久防重;JSON 重新序列化可能改变签名。重试会刷新投递时间、签名与 deliveryId,原正文/eventId/depositId 不变。
公开测试向量:secret=`fixture-secret`,keyId=`v1`,timestamp=`1700000000`,eventId=`test`,原正文为 `{"eventId":"test","actualAmount":"95"}`。正文 SHA256 为 `bbfaac700f75931cf2266144434abe02c90901e38208d1bc1fff98553e6020a6`;签名为 `73f9d6299008c62bffbd36fd4073814c00b99e64bfbb348f03914a5d7a2ecf57`。这是测试材料,不可用于生产。
接收方持久接受后返回 2xx 和严格对应的 JSON:
```json
{"eventId":"收到的事件ID","received":true}
```
任意 2xx、错误 eventId、空响应、超时都不会视为成功。确认只表示已持久接受消息,不代表最终余额成功。10 秒请求期限、64KiB 响应限制;指数退避至约 1 小时(另有少量抖动)、持续重试,管理员可重发原事件。
## 管理与安全
管理员使用独立密码+TOTP;登录 POST /admin/auth/login,GET /admin/auth/session 领取 CSRF,POST /admin/auth/logout 退出。会话 cookie HttpOnly、SameSite=Strict、生产 Secure,8小时有效;修改操作必须携带与 PUBLIC_ORIGIN 一致的 Origin 及 X-CSRF-Token。无公开注册、到账伪造、资金签名或余额修改接口。
管理 API 提供地址/订单/到账/回调/尝试/审计分页查询、地址导入、接入方创建启停与轮换、原事件重发、worker 状态。保存完整回调地址和密钥即自动启用本接入方,不需手工配置服务器域名列表。修改时未交付事件自动使用新配置;已确认交付不重发,正在发送的尝试保留原配置,失败重试使用最新配置。ID、原正文和尝试次数保留,操作留审计。服务端仅投递到后台登记的完整HTTPS URL,检查公网DNS、443端口、固定解析连接及禁止重定向;不能用订单正文临时指定回调URL。部署总外发开关用于隔离测试与紧急停发,实际测试部署已打开。
管理订单列表显示商户名称、订单状态及独立关联状态。订单状态按本系统实际记录区分待付款、已过期、已取消、已关联到账;过期由数据库时间判断,只影响展示及既有匹配排序,不等同取消。商户名称来自接入方配置,可按商户名称搜索;对外订单查询接口的原有status含义保持不变。