事件回调 Webhook

开发者文档

看店通可通过 HTTPS Webhook 向您的服务器推送订单支付、发货、退款、售后等事件,实现与 ERP、WMS、SCRM 的准实时同步。本文描述订阅配置、报文结构、验签、重试语义与幂等处理建议。

一、配置与可达性

在商户后台「开发者 → 事件订阅」填写公网 HTTPS 回调 URL(TLS 1.2 及以上),勾选需要接收的事件类型,并生成一把 Webhook Secret。这把密钥与调用 OpenAPI 用的 AppSecret 是两把不同的密钥,用途不可混用:AppSecret 用于你调用我们,Webhook Secret 用于你验证我们。

回调地址有三条硬性要求:必须是 HTTPS 且证书在有效期内(自签名证书会被拒绝);必须是固定的公网域名,测试期用内网穿透工具可以,上线前必须替换;必须在 5 秒内返回响应,超时即判定失败。此外防火墙需放行看店通出口 IP 段,具体列表在交付文档中提供,变更时会提前公告。

二、事件类型

事件名 触发时机 关键载荷
order.paid 支付成功 order_no、pay_amount_fen、pay_channel、paid_at
order.shipped 订单发货 order_no、express_company、waybill_no
order.closed 超时未支付或手动关闭 order_no、close_reason
order.refunded 退款完成(含部分退款) order_no、refund_no、refund_amount_fen
aftersale.updated 售后单状态变更 aftersale_no、order_no、status
product.stock_low 库存低于阈值(需开启) sku_id、available_qty、threshold
export.completed 异步导出任务结束(成功或失败) job_id、status、row_count

字段命名与 OpenAPI 保持一致,统一 snake_case,订单号一律是 order_no,时间字段一律是 ISO 8601 带时区格式。未订阅的事件类型不会推送,新增事件类型属于向下兼容变更,接收端应忽略无法识别的 event_type 而不是报错。

三、报文结构

推送方式为 POST,请求体是 UTF-8 编码的 JSON:

POST /webhook/kandiantong HTTP/1.1
Content-Type: application/json
X-Kdt-Event: order.paid
X-Kdt-Event-Id: evt_5a1c39f8be7d
X-Kdt-Timestamp: 1790000000
X-Kdt-Nonce: 3f9a2c7d41b8e605
X-Kdt-Signature: 4c1f...(小写十六进制)

{
  "event_id": "evt_5a1c39f8be7d",
  "event_type": "order.paid",
  "occurred_at": "2026-09-29T20:30:00+08:00",
  "data": {
    "order_no": "KDT202609290001",
    "pay_amount_fen": 19900,
    "pay_channel": "wechat",
    "paid_at": "2026-09-29T20:30:00+08:00"
  }
}

四、验签

验签算法与API 鉴权说明中的请求签名同源,都是 HMAC-SHA256,区别只在于密钥换成 Webhook Secret、签名对象换成回调报文:

canonical = timestamp + "\n" + nonce + "\n" + raw_body
expected  = hmac_sha256_hex(<WEBHOOK_SECRET>, canonical)
// 与 X-Kdt-Signature 做定长比较(constant-time compare)

四个必须遵守的点:第一,签名基于 raw body,请在框架解析 JSON 之前先把原始字节留一份,很多框架的 body 解析器会重排字段顺序导致验签失败;第二,比较时使用定长比较函数,不要用 ==,否则存在时序侧信道;第三,校验 X-Kdt-Timestamp 是否在 ±300 秒内,超窗直接丢弃;第四,验签失败应返回 HTTP 401 并落日志告警,绝不能「验不过就当成功处理」——那等于给任何人开了一个伪造订单灌库的入口。

五、响应与重试

业务处理成功后返回 HTTP 200,响应体为 {"code":0}。返回非 2xx、响应超时(5 秒)、或响应体无法解析,都会被判定为投递失败并进入重试队列。重试采用指数退避,间隔依次为 1 分钟、5 分钟、30 分钟、2 小时、6 小时,累计最长 24 小时,共 5 次;24 小时后仍失败的事件转入死信队列,可在后台「事件订阅 → 投递记录」手动重投。

这里有一个常见的错误做法:在回调处理函数里同步执行完整业务逻辑(写 ERP、发短信、调物流),结果处理耗时超过 5 秒被判超时,我们重试,你的系统又执行了一遍。正确做法是先落库再异步处理:收到推送后只做验签和入库,立刻返回 200,真正的业务逻辑交给后台队列消费。

六、幂等与顺序

由于存在重试,同一个事件可能会重复投递,接收端必须保证幂等。推荐用 event_id 作为去重键,在数据库中建唯一索引,插入冲突即说明已处理过,直接返回 200。用「订单号 + 事件类型」做去重键也可以,但要注意部分退款场景下同一订单会有多条 order.refunded,此时应改用 refund_no。

事件之间不保证顺序。网络抖动或重试会导致后发的事件先到,极端情况下你可能先收到 order.refunded 再收到 order.paid。防御方式有两层:一是本地维护状态机,只接受合法的状态迁移,非法迁移直接丢弃并告警;二是对比 occurred_at,如果收到的事件时间早于本地记录的最后更新时间,说明是旧事件,忽略即可。数据库层面务必对业务主键加唯一约束,这是最后一道防线。

七、本地开发与调试

后台「事件订阅 → 模拟推送」可以向你的回调地址发送指定类型的样例事件,报文结构与签名方式与生产完全一致,用来跑通验签逻辑非常合适。投递记录页面会保留最近 7 天的请求头、响应码与响应体,排查「到底有没有推过来」时先看这里。

日志方面请注意脱敏:记录 event_id、event_type、request_id 和 raw body 的哈希值即可,不要把包含收货人姓名、手机号、地址的完整报文明文写进日志文件。字段与 ERP 的映射关系见ERP 对接指南。

八、监控与补偿

建议对三个指标设告警:投递失败率(超过 1% 告警)、处理延迟 P99(超过 3 秒告警)、死信队列堆积量(大于 0 就告警)。大促前请按峰值的 3 倍压测回调接收接口,重点看数据库写入是否成为瓶颈。

回调只是「准实时」通道,不能作为唯一的数据来源。正确的兜底方式是事件驱动加定时校准:每天凌晨用 OpenAPI 的订单列表接口按 updated_at 拉一次增量,与本地数据比对,补齐丢失的事件。接口用法见OpenAPI 总览。如果你的服务需要长时间维护,可以在后台临时关闭订阅,修复后再开启并通过增量拉取补数据,这样不会让死信队列堆积。

联系我们

客服热线

+86 139-2520-6166

邮箱地址

service@kandiantong.com

公司地址

深圳市南山区粤海街道高新区社区高新南七道201号深港产学研基地W903

0/500 字符
看店通客服微信

扫码添加客服微信

微信号:tarshee