跳到导航

Webhook 签名验证与幂等处理

使用原始请求体验签,按 HTTP 状态码确认回调,并可靠处理重试和重复事件。

以 Markdown 格式查看

本文同步自 Agent Java SDK WEBHOOKS.md(提交 fa6c7b6)。本次仅更新文档,没有发布包含预警辅助方法的新 SDK 制品。

四类事件共用商户配置的一个回调地址,按正文 event 分流。事件字段和示例见 Webhook 事件与强平预警。

Webhook 请求头

Header说明
X-Agent-TimestampUnix 秒级时间戳。
X-Agent-Nonce随机串。
X-Agent-SignatureHMAC-SHA256 十六进制签名。
timestamp + nonce + rawBody

事件幂等

去重必须在商户与 environment 范围内持久化,并与业务处理原子提交。不同 notificationId 的 FIRST/UPGRADE/REMINDER 都是独立通知;不要按用户、轮次或 eventType 合并丢弃。空幂等键表示无法提取有效身份,不能拿空串去重。SDK 只返回辅助键,不会保存或执行业务。相同身份不同正文应作为冲突记录,不覆盖已处理正文。

验签和接收

请求头为 X-Agent-Timestamp(Unix 秒)、X-Agent-Nonce、X-Agent-Signature。签名是商户 API Secret 对 timestamp + nonce + rawBody 计算的 HMAC-SHA256 十六进制字符串。必须用收到的原始字节验签,不能先格式化 JSON、改名或重序列化。

先验签,再校验时间窗口(建议 ±300 秒)、商户和环境,随后提取幂等键并可靠处理。未知事件须保留原值以便排查,不能当作资金成功。验签辅助方法本身不执行时间、归属或持久去重校验。

处理完成或确认同正文重复后返回 HTTP 2xx;失败返回非 2xx。预警发送端 HTTP 超时为 5 秒,最多 8 次尝试,旧正文固定,仅签名时间与 nonce 更新。可靠接管后尽快响应,耗时业务可由商户自己的持久任务异步处理。

商户通知与用户在线状态无关。平台用户推送仅在线尽力投递,离线不补发;第一期没有站内信。

响应成功判定与停止重试

资金回调和强平预警都只根据 HTTP 状态码 判断本次投递结果,不解析响应正文中的 code、success、result 或 message,也不要求固定返回 OK / success。

商户返回结果平台行为
HTTP 200–299,包括 200、202、204判定本次投递成功;成功状态持久化后,不再自动重发这条通知
最终 HTTP 响应为非 2xx,包括 400、401、403、409、429、500、503本次投递失败,按既有退避策略重试,达到尝试上限后标记 DEAD;不会因为 4xx 自动视为已确认
连接失败或等待响应超时,未取得可用响应本次结果未确认,按失败策略重试

响应正文可以为空、文本或 JSON,以下 result 等字段只供商户自身阅读,并非平台协议要求。202 Accepted 同样会终止平台重试,因此只能在商户已经把通知可靠保存到自己的持久任务队列后返回,不能只启动一个内存异步任务就确认成功。

成功响应例子

业务处理完成,或已可靠保存到商户自己的待处理队列:

HTTP/1.1 200 OK
Content-Type: application/json
{"result":"RECEIVED"}

无需响应正文时,也可以返回:

HTTP/1.1 204 No Content

同一通知已成功处理,且确认正文一致时,跳过重复业务,仍然返回 2xx:

HTTP/1.1 200 OK
Content-Type: application/json
{"result":"DUPLICATE"}

失败响应例子

例如数据库暂时不可用,商户既未完成处理,也未可靠保存通知,应返回非 2xx,让平台后续重试:

HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"result":"FAILED","message":"temporary storage failure"}

验签失败时拒绝处理,不更新业务或成功去重记录:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"result":"INVALID_SIGNATURE"}

这些响应正文不会改变平台的重试判断。不要期待返回 401、409 等状态即可永久停止重试;需要排查签名、通知内容或商户配置。

错误示例:以下响应仍会被平台判定为投递成功,不会因 success:false 而重试。

HTTP/1.1 200 OK
Content-Type: application/json
{"success":false,"code":500,"message":"processing failed"}

如果 Web 框架或统一异常处理默认把业务错误包装成 HTTP 200,回调入口需要显式设置正确的 HTTP 失败状态。

可靠确认与重复投递

推荐顺序:验签、检查时间/商户/环境 → 提取幂等身份 → 在事务内完成业务和成功去重记录,或可靠保存待处理任务 → 提交成功后返回 200/204。数据库写入失败时不要提前返回 2xx,也不要先把通知标记为已处理。

商户处理成功后仍可能收到重复请求,例如响应在网络中丢失,或平台收到 2xx 后未能持久化 SUCCESS。HTTP 回调不保证只投递一次:必须在商户与 environment 范围内持久去重。资金使用 orderType:orderId:targetStatus,预警使用 liquidation.warning:notificationId;已处理的同正文通知返回 2xx,避免重复入账或重复提醒。同 ID 异正文应记录冲突并返回非 2xx,不覆盖原记录。

这里的“成功”指商户成功接收/处理通知,不代表通知所描述的资金业务成功。例如收到 targetStatus=FAILED 的合法资金结果并完成记录后,也应返回 2xx,不能因为订单失败就拒绝确认通知。

Funding 与环境边界

原合约划转 Webhook 及 queryOrder 流程不自动适用于 Funding。Funding 操作使用 queryWalletTransfer 按原单和原环境确认。Funding 使用 status,原合约划转响应使用 orderStatus。HTTP 成功或 code=0 不单独证明资金划转已完成。

显式模拟合约划转通知包含 environment=SIMULATED。先验证原始请求体,再与本地业务单核对用户、金额、订单身份和环境。

Funding 恢复指南 · 环境指南