跳到主要内容

代付 webhook

创建代付订单时传入 webhookUrl。订单进入商户可处理的最终状态后,DEEPayment 会向该地址发送明文 JSON webhook,并使用平台 Ed25519 私钥签名(RFC 9421)。body 不加密。

代付 webhook 只投递最终状态。商户必须先验签再处理出款结果,回调重复、延迟或状态冲突时,用查询代付订单接口确认最终状态。SDK 的 parsePayoutWebhook 会完成下面全部校验。

投递状态​

SUCCEEDED终态

代付成功,会投递 webhook。

FAILED终态

代付失败,会投递 webhook。

REFUNDED:原代付成功后,上游退回资金,平台完成商户退款入账后发送。继续使用原代付通知地址和签名协议,退款事件使用独立于原成功通知的新 eventId。

PENDING、PROCESSING 只在查询接口可见,不触发 webhook。内部异常处理状态不会投递给商户;订单恢复到最终状态后再投递最终结果。

HTTP Header​

POST /webhook/payout HTTP/1.1
Content-Type: application/json
Content-Digest: sha-256=:base64-sha256-of-body:
Webhook-Event-Id: evt_0123456789ABCDEFGHJKMNPQRT
Signature-Input: platform=("@method" "@path" "@query" "content-type" "content-digest" "webhook-event-id");created=1787803200;expires=1787803500;nonce="b7754a6c-4a9c-4cf0-b77f-6f2d4b7e5f5a";keyid="pwhk_20260827_01";alg="ed25519"
Signature: platform=:base64-ed25519-signature:
Content-Digest

sha-256=:base64(SHA-256(rawBody)):,对实际 body 字节计算(RFC 9530)。

Webhook-Event-Id

事件 id,恒等于 body 中的 eventId。

Signature-Input

label 固定 platform。keyid 对应商户后台展示的平台 webhook 公钥。created、expires 为 Unix 秒,间隔不超过 300 秒。nonce 是 UUID v4。

Signature

platform=:base64(Ed25519 签名):。

签名规则​

签名基串格式与商户请求签名相同(RFC 9421),覆盖字段固定:

"@method": POST
"@path": /webhook/payout
"@query": ?
"content-type": application/json
"content-digest": sha-256=:base64-sha256-of-body:
"webhook-event-id": evt_0123456789ABCDEFGHJKMNPQRT
"@signature-params": ("@method" "@path" "@query" "content-type" "content-digest" "webhook-event-id");created=1787803200;expires=1787803500;nonce="b7754a6c-4a9c-4cf0-b77f-6f2d4b7e5f5a";keyid="pwhk_20260827_01";alg="ed25519"
  • @path、@query 取实际收到的 webhookUrl。@query 是 ? 加原始 query;URL 没有 query 时为 ?。
  • 各行用 \n 拼接,末尾不追加换行。最后一行是去掉 platform= label 的 Signature-Input 值。
  • 校验:Ed25519.Verify(platformWebhookPublicKey[keyid], signatureBase, signature)。

每次投递都重新签名:重试时 created、expires、nonce、Signature 都会变化,Webhook-Event-Id 和 body 保持不变。商户按 eventId 去重。

事件示例​

{
"eventId": "evt_0123456789ABCDEFGHJKMNPQRT",
"orderType": "PAYOUT",
"orderNo": "FO202605060001",
"merchantOrderNo": "P202605060001",
"status": "SUCCEEDED",
"currency": "BRL",
"amount": "100.00",
"channelTradeNo": "E1234567891",
"attach": "withdraw_123"
}

字段说明​

eventId

通知事件 id,同一事件的重试投递复用同一个值;与 Header Webhook-Event-Id 相同。

orderType

固定为 PAYOUT。

orderNo

DEEPayment 代付订单号。

merchantOrderNo

商户代付订单号。

status

代付最终状态。

currency

订单币种。

amount

订单金额。

channelTradeNo

通道、清算网络或支付网络返回的交易号;BRL PIX 映射 End-to-end ID。

attach

商户创建订单时传入的附加信息。

代付 Webhook 不返回出款方或收款方身份信息;渠道提供的代付账户信息通过代付回单接口获取。

验签示例​

代付 webhook 的 Header 和签名基串与代收 webhook 完全相同,验签代码见 代收 webhook 的验签示例,只有 orderType 和 body 字段不同。

商户处理顺序​

  1. 读取原始请求

    保留 HTTP raw body 原始字节、请求 path 和原始 query 用于验签。

  2. 校验签名

    重算 Content-Digest,确认 Webhook-Event-Id 等于 body eventId,校验时间窗口和 nonce,按 keyid 选择平台公钥,对重建的签名基串校验 Signature。任一步失败直接拒绝,不解析业务字段。

  3. 幂等落库

    以 eventId 做投递去重,按 orderNo 或 merchantOrderNo 对订单状态更新做业务幂等,放款结果以终态为准。

  4. 返回 2xx

    商户响应任意 HTTP 2xx 视为成功,body 内容不作要求。非 2xx 会触发重试。

状态冲突处理​

如果本地状态与 webhook 状态冲突,或者同一订单收到不同终态事件,先冻结本地自动处理并调用查询代付订单接口确认。放款、对账等动作应以已验签 webhook 和查询接口共同确认后的终态为准。

代付退回​

{
"eventId": "evt_00000000000000000000000002",
"orderType": "PAYOUT",
"orderNo": "PO202609240001",
"merchantOrderNo": "M202609240001",
"status": "REFUNDED",
"currency": "MXN",
"amount": "100.00",
"refundNo": "R202609240001",
"refundAmount": "100.00",
"refundTime": 1790208000000
}
  • refundNo:退款单号。
  • refundAmount:全额退款本金,币种主单位的十进制字符串;不是扣除退款费后的余额净增加额。
  • refundTime:退款实际入账时间,Unix 毫秒;重试时不改变。

原代付查单接口返回相同的 REFUNDED 和退款字段;退款尚未入账时保留原支付结果,不返回退款字段。商户按原订单或退款单防重,同一退款只能入账一次;晚到的原成功通知不能覆盖已退款状态。退款事件重试复用自身 eventId 和正文,不覆盖原成功事件。

自动通知在收到 HTTP 2xx 或退款通知创建满 24 小时后停止重试;期限从退款通知创建时间计算,不从原代付订单创建时间计算。通知失败记录保留,商户仍可通过原代付查单接口确认退款结果。

商户只有在处理完成或任务可靠入队后才能返回 HTTP 2xx;入队失败必须返回实际非 2xx,不能仅在 HTTP 200 的正文中返回错误。商户受理后自行重试临时查单和处理失败。

本能力仅支持普通代付全额退回,不新增申请退款接口,不涉及代收或部分退款。