Webhook 可靠性:验签、重试与对账
Shopify 等平台的事件流如何做稳:不要只信任实时推送。
实时 Webhook 很香,但不保证只到达一次,更不保证永远到达。我们维护的一个 Shopify 库存同步应用,曾因平台侧短暂 503 漏掉十七个 inventory 更新事件,导致某店铺超卖 23 单。事后复盘结论很清晰:Webhook 是加速器,不是唯一真相源。跨境 ERP 若只依赖推送,报表和库存迟早会和平台对不上。商家投诉「你们系统和 Shopify 后台不一致」时,根因往往是既没幂等也没对账。
验签是门禁,不是可选项
每个请求进来,先用 HMAC-SHA256 校验 X-Shopify-Hmac-Sha256,密钥从环境变量读取,禁止硬编码。验签失败直接返回 401,并记录来源 IP 和 shop domain,方便排查伪造请求或 Partner 后台密钥轮换遗漏。业务逻辑写在验签通过之后,避免恶意 payload 触发 SQL 或队列污染。多租户 SaaS 还要校验 shop 是否仍安装应用,防止卸载后旧 Webhook 继续写库。本地调试时用 Shopify CLI 转发,不要在生产日志里打印完整签名密钥。Rotate secret 时保留旧密钥一段重叠期,避免正在途中的请求全部失败。
重试要幂等,事件 ID 是锚点
Shopify 可能因超时重发同一事件。我们在 PostgreSQL 建 webhook_events 表,以 shop domain 加 event_id 做唯一约束。处理流程:落库、标记 processing、执行业务、标记 done。重复投递直接返回 200,不重复扣库存、不重复建单。Worker 失败时保留原始 payload,指数退避重试,超过阈值进死信队列人工介入。库存扣减和订单创建必须放在同一事务,幂等键贯穿 API 与 Webhook 两条入口。orders/create 和 orders/updated 可能乱序到达,业务层按 updated_at 或版本号丢弃过期事件,避免状态回滚。
对账补洞,实时流负责对快
每天凌晨跑对账任务:用 Admin API 拉取过去 48 小时订单和库存快照,与本地 PostgreSQL 比对差异。漏网事件补写并触发下游重算。实时 Webhook 负责秒级响应,对账负责最终一致。我们还会在 PostgreSQL 存 last_synced_at,按 shop 增量拉取,降低 API 配额消耗。大店铺订单量大时分页拉取并限速,避免触发 Shopify 429。对账报告邮件发给运营,列出补写条数和差异原因,形成闭环。
可观测性与降级
按 shop、topic、状态统计成功率与延迟,连续失败超阈值时自动降级到纯 API 轮询模式。告警带上最近失败 payload 摘要,缩短排障时间。Handler 必须在五秒内返回 200,重活扔队列;否则平台会判定超时并重试,放大重复投递。可靠性是产品信任的一部分,尤其在跨境 ERP 与 Shopify 集成场景,Webhook 稳了,售后工单才会少。我们后来在 PostgreSQL 增加 reconciliation_runs 表,记录每次对账的补写条数和耗时,运营可直接在后台查看,不必等研发查日志。建议新接 Shopify 的团队第一周就搭好验签、幂等和对账三板斧,而不是等功能做完再补。压测时模拟重复投递和乱序到达,用 fixture 回放生产脱敏 payload,比上线后靠商家投诉发现问题可靠得多。ERP 侧库存扣减务必与 Webhook 消费同事务,Partial failure 时整单回滚并标记 retryable,避免半扣库存的幽灵状态。最终我们在 SLA 里写清:Webhook 延迟五分钟内视为正常,超过则自动切 API 轮询,商家预期被管理住了,投诉率明显下降。记住:平台文档说 Webhook「至少一次投递」,你的系统必须按「可能多次、可能缺失」来设计,这才叫生产级集成。
小结
Webhook 只能当加速器,不能当唯一真相来源。验签、幂等、重试与对账四件套齐备后,订单与库存类业务才敢把实时流接到主链路。把对账差异做成可操作工单,比只记日志更能形成闭环。
实践中把上述清单变成可勾选的发布门禁,比事后补救更省时间。