Browse all articles

在 ChannelDock 中设置 Webhooks

Last updated

为什么使用 webhook?

ChannelDock 提供 webhook,以便账户中发生变化时立即通知您的系统。无需按计划轮询 API,当创建或更新订单、创建发货、库存变化或登记退货时,您会自动收到通知。Webhook 有助于自动化流程并减少不必要的 API 请求。

主要特性

  • 事件时机: Webhook 在事件发生后数分钟触发,提供近实时更新。
  • 载荷一致性: webhook 的 JSON 载荷与对应 API 端点返回的结构一致。
  • 自动重试: 如果 ChannelDock 无法投递 webhook,最多尝试五次,延迟递增(0、30、60、120 和 240 秒)。十次投递失败后,为安全起见会禁用该 webhook。
  • 安全性: 每个 webhook 可以拥有各自的密钥。设置密钥后,ChannelDock 会在 X-Channeldock-Signature 请求头中对每次投递附加 HMAC-SHA256 签名,您可据此验证请求确实来自 ChannelDock。

设置 webhook

  1. 前往设置: 登录 ChannelDock,打开 Settings → API & Webhooks。您会看到两个部分:API keys 和 Webhooks。

  2. 创建新 webhook: 点击 Create new webhook。将出现 “Webhook Configuration” 窗口。

  3. 填写字段:

    • Webhook name: 选择描述性的内部名称(例如 “Order updates”)。
    • Webhook URL: 输入 ChannelDock 可向其发送 HTTP POST 请求的端点 URL。确保该 URL 可从公网访问,并在 5 秒内响应。
    • Event to trigger webhook: 选择要接收通知的事件类型。可能的事件包括 order.created、order.updated、order.status.changed、order.picking、order.picked、order.deleted、shipment.created、stock.updated、return.created、return.handled 和 return.product.updated。
    • Status: 保持为 Active。十次投递失败后 webhook 会自动停用。
    • Webhook 密钥(可选但推荐): 填写您自己的密钥,或点击 生成 创建一个强密钥。ChannelDock 使用该密钥对每次投递进行签名,您可据此验证请求来自 ChannelDock 且在传输途中未被篡改。
  4. 保存: 点击 Save webhook。ChannelDock 会保存您的 webhook,并将所选类型的事件发送到您的端点。

载荷结构与事件

所选事件发生时,ChannelDock 会向您的端点发送 JSON 载荷。载荷至少包含以下字段:

{   
  "event": "order.created",  
  "payload": {   
    ...  
  },  
  "signature": "<hash>" (已弃用,见下文)  
}
  • event – 该 webhook 配置的事件(例如 order.created)。
  • payload – 包含订单、发货、退货或库存变动的详细信息。其结构与对应对象的 API 响应一致。
  • signature – 仅在配置了密钥时出现。这是旧签名,已弃用;请改为验证 X-Channeldock-Signature 请求头。

验证签名

您的 webhook URL 必须可公开访问,而请求本身不携带任何其他发送方凭证——没有 API 密钥,也没有密码。因此,凡是能到达该 URL 的请求,在您的端点看来都像是真正的 ChannelDock 投递:可能是通过日志文件、代理或支持工单泄露的 URL,也可能是他人截获后重新发送的历史投递。若不加验证就处理内容,一条伪造的 stock.updated 可能把您的库存清零,一条伪造的 order.updated 可能在您自己的系统中把订单标记为已发货。

签名正是这份缺失的凭证。只有您和 ChannelDock 知道该密钥,因此只有你们双方才能生成与眼前这次投递相匹配的值。

设置 webhook 密钥后,该 webhook 的每次投递都会带上一个额外的 HTTP 请求头:

X-Channeldock-Signature: sha256=8b415f2c0cd241a21109e007942b6ce43cbfcf97f7b2d7829084258c0ae0a66f

该值是请求正文的 HMAC-SHA256 哈希,以您的密钥作为密钥计算得出。您在自己一侧计算同样的哈希,并检查两者是否完全相同。未设置密钥时,不会发送该请求头。

在应用中验证签名

  1. 读取原始正文。 在 JSON 被解析之前,以文本或字节形式取得请求正文。大多数框架会解析 JSON 并丢弃原始文本,因此请显式获取:PHP 用 php://input,Laravel 用 $request->getContent(),Flask 用 request.get_data(),Django 用 request.body,Rails 用 request.raw_post,Express 用 express.json({ verify: (req, res, buf) => { req.rawBody = buf } })。
  2. 读取请求头。 取 X-Channeldock-Signature 并去掉 sha256= 前缀。查找请求头时请忽略大小写——取决于连接方式,它可能以 x-channeldock-signature 的形式到达。
  3. 计算自己的哈希。 以您的 webhook 密钥为密钥,对原始正文计算 HMAC-SHA256,并以十六进制表示。在 PHP 中即 hash_hmac('sha256', $raw, $secret)。
  4. 比较,请使用 hash_equals 等恒定时间函数。两者相同则投递为真,可以处理;否则返回 HTTP 401 并忽略正文。只有该步骤成功后才解析 JSON。

重要:请对您收到的正文原样计算哈希。 签名是对字节的哈希,而非对 JSON 含义的哈希。因此不要先解析 JSON 再转回文本去计算哈希。两种语言可以写出含义完全相同但文本并不相同的 JSON:PHP 把斜杠写成 c\/o,而 Node.js 和 Python 写成 c/o。它们解析回来是相同的数据,却会产生完全不同的哈希。也不要在计算哈希前移除 signature 字段——它属于被签名的正文。

已弃用的 signature 字段

在该请求头出现之前,ChannelDock 把 signature 字段放在正文里。该字段仍会原样发送,因此现有对接不会中断,但它已被弃用。要验证它,必须移除该字段,再把其余 JSON 完全按照 PHP 的写法重建为文本,包括 PHP 把 / 写成 \/ 的习惯。在其他语言中几乎无法做到完全一致。

新的对接请基于请求头开发。如果您目前验证的是该字段,也不会中断:两者在每次投递中都会发送,因此您可以在方便的时候切换。这两个值永远不会相同,因为它们覆盖的数据不同——切勿相互比较。

安全处理提示

  • 为每个 webhook 分配各自的密钥,并定期轮换。
  • 使用恒定时间比较函数(例如 PHP 的 hash_equals 或 Node.js 的 crypto.timingSafeEqual)以防止时序攻击。
  • 在对载荷内容采取任何操作之前先验证签名。
  • 在执行操作前对载荷内容做额外校验(例如确认订单存在)。

最佳实践

ChannelDock 建议以下做法,以便安全可靠地处理 webhook:

  • HTTP 200 响应: 端点在成功接收载荷后尽快返回 HTTP 200 OK。否则 ChannelDock 会将该次尝试视为失败并重试。
  • 幂等性: webhook 消息有时可能发送两次(例如因网络问题或重试)。确保处理逻辑幂等,以免重复消息导致重复工作。
  • 监控: 使用 ChannelDock 仪表板监控 webhook 状态并识别错误。
  • 载荷处理: 确保端点能处理较大载荷,并在**合理时间内(≤ 5 秒)**响应。十次投递失败后 webhook 会自动停用。

Was this helpful?