1. 验证原始请求

读取原始 body;检查 X-Access-Key/X-Timestamp/X-Signature 各出现一次、通知凭据匹配、Unix 秒在 300 秒内。用 Webhook 密钥和原请求五段内容计算 HMAC,以恒定时间比较签名。不通过时返回非 2xx,不能处理业务。算法见身份验证。

2. 校验事件并复核必要状态

验签后解析 JSON,按事件表校验 type/status/phase 及该场景的字段。充值核对玩家、链与登记合约;未知或不匹配资产持久化告警,不入账。已失效或已冲正后收到成功通知,按充值恢复规则先单笔查询,旧通知本身不能恢复入账。

提现先按 withdrawOrderId 找到本地订单,核对 amount 与首次提交金额一致;旧付款终态没有 playerId,玩家由本地订单关联。withdrawal_rejected 另含玩家、链、地址与额度快照,按提现拒绝规则完整校验并绑定原单。未知订单、身份或金额不符时持久化异常并核查,不自动确认扣款或释放预扣。

3. 幂等处理并持久化

在本地事务内保存事件与业务处理结果。通知用 eventId 去重,充值账务用 txRef,提现账务用 withdrawOrderId。已处理的重复事件仍须验签并应答。充值恢复还需按 txRef 串行或锁定记录检查版本,防止并发查询、通知把新状态覆盖为旧状态。

eventId 去重只防止重复处理通知,不阻止后续查询驱动同一 txRef 的失效和恢复。例如:confirmed → invalidated → confirmed 对应“入账 → 冲正 → 当前单笔复核后恢复”,仍是同一笔充值。

TEXT
读取并验证原始请求;校验事件结构
若充值资产不匹配:持久化忽略原因并告警;不入账;返回 200 与正确回签
读取本地业务记录及版本
若充值已失效或已冲正后收到成功通知:
    在账务事务之外查询当前充值;查询失败或 404 时不恢复,返回非 2xx
开启事务,锁定对应 txRef 或 withdrawOrderId
若本地版本已变化:回滚;退出事务后重新读取和复核
按事件表处理:进度只记录;充值入账或恢复;提现确认扣款或释放预扣
    充值失效由查询发现并冲正,不等待失效通知
    充值恢复仅允许复核结果为 confirmed 且玩家、链、合约匹配
    withdrawal_rejected 使用完整校验及原单绑定后的专用一次释放分支
    同一业务结果已处理则跳过;冲突终态停止自动改账并核实
保存 eventId、原通知和处理结果,提交事务
返回 200 和正确的 X-Signature

4. 对原请求回签

应答签名仍覆盖原请求的方法、路径、时间戳、query、body,使用相同的 Webhook 密钥;不签应答正文,也不换成当前时间。因此,成功验签后计算的签名值可直接用于应答。

项目要求
状态2xx,推荐 200
X-Signature恰好一个,值为重新计算的原请求 HMAC
正文不超过 4096 字节,可以为空或 {}
时限发送端 5 秒超时,原请求时间戳仍需在 300 秒内;不跟随 3xx

本地提交完成前不要回 2xx;2xx 但回签错误仍会重试。预期合法请求完成一次持久处理后成功应答。失败先查原始字节、通知密钥与应答头,再查事务是否已提交,不依赖未来一定重投。

固定签名演算

以下三段报文用于本地算法自测。example-hmac-secret、固定时间戳以及正文里的链、地址、哈希均为演示值,不能直接调用真实环境。实际路径取你的通知 URL;当前不支持为通知 URL 配置 query。

HTTP
POST /notify HTTP/1.1
Host: notify.example
Content-Type: application/json
X-Access-Key: example-access-key
X-Timestamp: 1720000000
X-Signature: 7868cb5b05e07d9f9e891f551792704ee1d078ed53f4e6e1850d14cd4f67c681

{"eventId":"deposit-confirmed-1","type":"deposit_confirmed","playerId":"og-user-1001","chainName":"eth-sepolia","chainId":"11155111","txHash":"0xabababababababababababababababababababababababababababababababab","logIndex":"0","tokenContract":"0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd","amount":"1000000","status":"confirmed","blockNumber":"250","txRef":"72471b2053670f0824c0b51bed193e56c5da22d62f67fb62d6012cc905d609a0"}

以完全相同的原始正文计算;不要添加换行或改动键顺序:

BASH
SECRET='example-hmac-secret'
TIMESTAMP='1720000000'
BODY='{"eventId":"deposit-confirmed-1","type":"deposit_confirmed","playerId":"og-user-1001","chainName":"eth-sepolia","chainId":"11155111","txHash":"0xabababababababababababababababababababababababababababababababab","logIndex":"0","tokenContract":"0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd","amount":"1000000","status":"confirmed","blockNumber":"250","txRef":"72471b2053670f0824c0b51bed193e56c5da22d62f67fb62d6012cc905d609a0"}'
PAYLOAD="POST|/notify|${TIMESTAMP}||${BODY}"
printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex

结果应为 7868cb5b05e07d9f9e891f551792704ee1d078ed53f4e6e1850d14cd4f67c681。不符时逐字检查路径、空 query、正文与换行。

完成业务处理后,以下应答符合签名规则:

HTTP
HTTP/1.1 200 OK
Content-Type: application/json
X-Signature: 7868cb5b05e07d9f9e891f551792704ee1d078ed53f4e6e1850d14cd4f67c681

{}

输入关键词搜索指南、接口和字段。