跳到主要内容

Webhook

订单到达终态时,平台向你的 webhookUrl POST 一段明文 JSON,用平台 Ed25519 密钥签名。SDK 验证请求并返回解析后的事件,你不需要自己处理 header。线路格式见 API Reference 的代收 webhook和代付 webhook。

交给 SDK 的必须是收到的原始字节。任何在你的 handler 之前解析 JSON 的框架都会改变字节、破坏摘要。为 webhook 路由关闭 body 解析,或直接读原始流。

两类方法​

只验证验证并解析
GoVerifyWebhook(r) ([]byte, error)ParsePaymentWebhook(r)、ParsePayoutWebhook(r)
JavaScriptverifyWebhook({ method, path, headers, body, rawQuery })parsePaymentWebhook(...)、parsePayoutWebhook(...)
Pythonverify_webhook(method=, path=, headers=, body=, raw_query=)parse_payment_webhook(...)、parse_payout_webhook(...)
PHPverifyWebhook($method, $path, $headers, $body, $rawQuery = '')parsePaymentWebhook(...)、parsePayoutWebhook(...)
JavaverifyWebhook(method, path, headers, body, rawQuery)parsePaymentWebhook(...)、parsePayoutWebhook(...)

验证方法返回已验签的原始 body。解析方法在此基础上解码、检查五个必填字段并强制 orderType:把代付事件交给代收解析器会被拒绝,路由错的 webhook 不会更新错误的表。除非你自己路由事件,否则用解析方法。

检查顺序固定:请求外形、对 body 的 Content-Digest、Webhook-Event-Id 等于 body 的 eventId、created/expires 时间窗、按 keyid 选平台公钥、Ed25519 签名。任一失败即为 webhook 错误,回 4xx 且不处理 body。

Handler 示例​

func paymentWebhook(w http.ResponseWriter, r *http.Request) {
wh, err := c.ParsePaymentWebhook(r) // 自己读取并验证 r.Body
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
if seen(wh.EventID) { // 至少一次投递
w.WriteHeader(http.StatusOK)
return
}
switch wh.Status {
case deepayment.StatusSucceeded:
credit(wh.MerchantOrderNo, wh.PaidAmount)
case deepayment.StatusFailed:
fail(wh.MerchantOrderNo, wh.Failure.Msg)
}
w.WriteHeader(http.StatusOK)
}

事件字段​

字段代收代付含义
eventId有有每个事件唯一,重试时不变。你的去重键。
orderTypePAYMENTPAYOUT解析方法强制校验。
orderNo、merchantOrderNo有有两个单号。
status有有只有终态:SUCCEEDED、FAILED,代收还有 EXPIRED、CANCELED。
currency、amount有有decimal 字符串。
paidAmount有无实际结算金额。
channelTradeNo可选可选渠道侧流水号。
failure失败时失败时code、msg、message。
attach原样原样

失败​

status 为 FAILED 时按 failure.msg 分支;failure.message 是给日志和客服看的自由文本。

failure.msg含义处置
ORDER_REJECTED风控或业务规则拒绝不要用同一请求重试
CHANNEL_ERROR上游渠道失败订单已终态;业务允许时新建一笔
INSUFFICIENT_BALANCE代付资金不足充值后新建代付

规则​

  • 事件持久化之后才回 2xx。 其他响应都会触发重试。重试会用新的时间戳和 nonce 重新签名,body 和 eventId 不变。
  • 按 eventId 去重。 投递语义是至少一次。
  • 按 merchantOrderNo 做幂等的状态更新。 不同订单的 webhook 到达顺序不保证。
  • 有疑问就查询。 与本地状态冲突或迟到的 webhook,以查询订单的结果为准。
  • 轮换期间保留两把 webhook 密钥。 见配置。