Skip to content

接收 Webhook 异步通知与验签

为什么要使用 Webhook

当您使用 BasicEx 构建集成时,您能希望您的服务接收来自 BasicEx 发生的事件,以便您的系统可以执行相应的操作,例如当您使用币趣支付时,想要在票据支付完成后进行相应的操作。

要在 BasicEx 中启用 Webhook 事件,您可以参考特定的 BasicEx 提供的服务来传入 Webhook 通知地址,例如在创建支付票据或构建代付请求时传入 Webhook 通知地址。根据您使用的特定服务,BasicEx 可以将实时事件数据推送到您服务的 Webhook 端点。BasicEx 通过使用 HTTPS 将 Webhook 事件作为 EVENT 对象的 JSON 负载发送到您的服务中。

Webhook 事件

BasicEx 会根据特定的情况生成事件数据,并发送这些数据,以通知特定活动。

当事件发生后,BasicEx 会生成一个新的 Event 对象,单个 API 请求可能会导致创建多个事件。例如,如果您创建了支付票据,根据特定的选项,您可能将收到: invoice.paid, invoice.completed, invoice.expired事件。

通过在 BasicEx 中注册 Webhook 端点,您可以使 BasicEx 自动将事件对象作为 POST 请求的一部分发送到您服务的已注册 Webhook 端点。在您的 Webhook 端点收到事件后,您的服务可以进行相对应的操作

WARNING

收到 Webhook 通知后需要一个正文为空的 HTTP 200 响应,任何其他 HTTP 响应均视为发送失败。
币趣支付 服务器会多次尝试发送 Webhook 推送,直到发送成功或 币趣支付 服务器放弃。

异步事件对象 (Event Object)

每次我们向您的 Webhook 端点推送的数据,结构如下所示:

json
{
  "id": "500e8384-61b1-4219-855d-2a196330af52",
  "object": "event",
  "objectId": "40620230325105039975309362381014",
  "created": 1680064028243,
  "type": "invoice.completed",
  "data": {
    "orderNo": "40620230325105039975309362381014",
    ... // 具体业务对象数据
  },
  "retriesNum": 0
}

Event Object 结构说明

字段名称类型描述
idstring事件 ID,每一个 Webhook 事件的事件 ID 唯一
objectIdstringWebhook 承载事件详情 ID
objectstringWebhook 时间类型,默认: event
createdlongWebhook 的创建时间戳(13 位)
typestringWebhook 的通知事件类型,参考: Event Type
dataobjectWebhook 事件的具体内容,根据事件类型而定
retriesNumint此 Webhook 事件的重试次数

核心事件类型大全 (Event Type)

在您解析 Event Object 时,首先应当判断 type 字段的值来决定调用何种处理逻辑:

通知事件类型 (type)业务场景描述
invoice.paid支付票据已支付成功事件通知,该事件通知在支付票据支付成功后触发,但是并不代表该支付票据已经完成,以 invoice.completed 事件通知为准
invoice.partial_completed支付票据在有效期内存在部分支付,但支付的金额小于票据的金额,商户务必根据paidAmount字段获取当前已支付的金额
invoice.completed支付票据已完成事件通知,该事件通知在支付票据完成后触发,代表该支付票据已经完成
invoice.expired支付票据已过期事件通知,该事件通知在支付票据过期后触发,代表该支付票据已经过期
payout.completed代付成功事件,该事件通知在代付成功过后触发,代表代付成功
payout.failed代付失败事件,该事件通知在代付失败过后触发,代表代付失败

Webhook 安全验签

为了防止恶意用户模拟币趣支付向您的服务器发送虚假的“支付成功”回调,您必须对每一个触达 notificationUrl 的 POST 请求进行安全签名验证!

注意:必须使用原始的、未被解析的 HTTP Body 文本流

无论采用哪种验签方式,参与验签的“待签名字符串” 规定为: 您当时传入的 Webhook 通知回调地址 + BasicEx发送给您的原始 HTTP Body(JSON文字流)切记:绝不要使用已经被框架拦截、反序列化成 Object 再转换回来的 JSON 字符串,因为这会改变属性排序、空格或换行等格式,从而必然导致哈希对比不通过!

验签的第一步:判断采用哪种验签机制

币趣发起的推送会在 HTTP Header 中携带名为 X-Webhook-Signature-Type 的声明,您应当以此决定走哪条验签逻辑分支:

  • 值为 key:说明您配置使用的是 API Key 模式。
  • 值为 cert:说明您配置使用的是平台API证书模式。

分支 A:处理 key (API Key 模式 HMAC 验签)

若采用 API Key 鉴权,我们在向您推送时,会使用您后台配置的 Secret Key 对推送体进行 HMAC-SHA512 签名。

  1. 提取签名标头 从 HTTP Header 中获取关键签名:X-Webhook-Signature

  2. 自行计算并配对

    • 取出您在商户端提取的 Secret Key 作为密钥。
    • 对前述的 待签名数据体(URL + 原始 JSON流) 处计算 HMAC-SHA512 哈希散列。
    • 将这串二进制结果转换为 全部小写16进制字符串 (Hex)
    • 对比计算出的 Hex 字符串与 第一步您拿到的 X-Webhook-Signature 是否完全相等。相等则证明请求安全无改动。

分支 B:处理 cert (平台证书模式 RSA 验签)

在此模式中,推送信息是由币趣平台证书私钥进行签名的。因此,商户需要获取平台证书来进行配对验签。

  1. 提取相关标头

    • 提取签名头:X-Webhook-Signature
    • 提取平台证书序列号:X-Webhook-Signature-Serial
  2. 解析寻找对应的平台证书

    • 根据上一步获取的证书序列号,调用我们下方的 获取平台证书接口 (或读取本地缓存),找到与该序列号对应的平台证书数据字符串。
    • 注意:此处是拿平台证书进行验签,不能使用商户自己的证书进行验签

  3. 解密验证签名真伪

    • 调用所使用开发语言环境中的 Verify (验签) 函数。
    • 加密方法指定选:SHA-256 with RSA (即 SHA256withRSA / RSA-SHA256)。
    • 将第一步中提取出来的 X-Webhook-Signature 的值进行 Base64 Decode 解析回原始二进制流数组。
    • 调用验签函数,同时把待签名数据体(URL + 原始 JSON流)Base64 Decode解码后的二进制流 投入,使用上述平台公钥验证哈希一致性。函数若返回 True 则鉴权通过。

TIP

如果您觉得这步操作过于繁杂易出错,强烈建议您参考我们 BasicEx Java SDK 这里关于 Webhook验签封装好的代码 的写法并迁移到您自己的系统中。

获取平台证书接口说明

当且仅当您使用 cert 模式接收回调时,才需要拉取:

提交地址:https://openapi.basicex.com/v2/platform/certificate

请求方式:GET

返回参数 (response.data):

字段类型描述
certificatestring平台证书数据
serialNumberstring平台证书序列号