所有文章
Shopifywebhook debugginglocal testinge-commerce

在本地无意外地测试 Shopify Webhook

本地开发中大多数 Shopify Webhook 错误归结于一行:HMAC 是 base64 编码的,不是 hex。如果你调用 .digest('hex') 并与 X-Shopify-Hmac-Sha256 比较,检查永远不会通过——即使有正确的共享密钥。我们见过资深工程师为这一个字符耗掉一个下午。

base64 陷阱

Stripe 用 hex。GitHub 用 hex(带 sha256= 前缀)。Shopify 用 base64。三个提供商,三种编码。你的验证器第一版几乎肯定会从另一个项目复制片段并悄悄失败。

验证应当是这样:

const hmac = crypto
  .createHmac('sha256', SHOPIFY_API_SECRET)
  .update(rawBody)         // raw body, not JSON-parsed
  .digest('base64');       // base64, not hex

const valid = crypto.timingSafeEqual(
  Buffer.from(hmac),
  Buffer.from(req.headers['x-shopify-hmac-sha256']),
);

编码之外还有两点重要。body 必须是原始的、未解析的字节。比较必须是时间安全的——字符串相等会一次泄漏一个字节的信息。

为什么隧道让这更容易

你也可以用 Shopify 的 shopify app dev 命令,它会启动一个内部隧道。能用。但它也把你的开发服务器包进自己的进程,以特定方式吞掉日志,且不给你重放按钮。对于超出 "hello world" 的应用开发,一个稳定的公开 URL 加上带请求捕获的 localhost 隧道,省下的时间比 CLI 在初始设置上省下的更多。

npx portpreview 3000

你把 HTTPS URL 粘贴到 Partners 仪表盘中应用的 webhook 配置里,从测试店触发一个事件,请求就带着所有 header 完好地落到你的本地处理器。

测试店:Shopify 文档略过的部分

接线之前要知道两件事:

  • 开发店会触发所有 webhook 事件。订单创建、履约、库存——全都。你不需要伪造任何东西;只要把应用装到开发店上点点看。
  • webhook 密钥按应用和按投递通道而不同。如果你还订阅了 EventBridge 或 Pub/Sub,HMAC 行为不同。这里说的是纯 HTTPS webhook 投递。

分步设置

  1. 在你使用的端口上本地启动应用(3000 常见)。
  2. 运行 npx portpreview 3000 并复制它打印的 HTTPS URL。
  3. 在 Shopify Partners 仪表盘打开你的应用,进入 Configuration → Webhooks
  4. 把 webhook 端点设为 https://your-tunnel.portpreview.dev/api/webhooks/shopify(或你应用使用的路径)。
  5. 把应用装到开发店,然后触发事件——创建草稿订单、履约一个商品、改库存。
  6. 看请求落地。检查 header 和 body。每次修复处理器后重放捕获的请求。

我们反复看到的错误

验证前就解析了 body

如果你把 app.use(express.json()) 放在 webhook 路由之前,等你尝试验证时原始字节已经没了。只在 webhook 路径上挂载原始 body 解析器,或者在任何 JSON 解析之前手动从请求流里拉 body。

搞混了密钥

Partners 应用密钥不等于 Storefront API 令牌。webhook HMAC 用的是应用密钥。如果你盯着 env 文件里的 shp_xxx,你拿错了。

看起来完全一样的测试事件

Shopify 的 "Send test notification" 按钮投递一个带占位 body 的合成事件。那个测试事件上的签名是真的,但 payload 是固定的。要做真实的 payload 形状测试,从开发店本身触发事件。

对应用开发来说重放不可妥协

Shopify 对失败的 webhook 以指数退避重试最多 48 小时。很慷慨,但当你迭代时你不想等下一次重试。在 你隧道的请求历史里捕获首次投递,并按需对你的本地处理器重放它。

本地何时不再够用

店铺注册流程、GDPR webhook 和订阅计费变更更容易对着已部署环境验证,因为它们与 Shopify 自己的账户状态交互。其余的一切——payload 解析、签名验证、业务逻辑——本地更快。

关于所有提供商底层的签名数学,阅读我们的webhook 签名验证指南。如果你想要一个内置重放的隧道,加入 PortPreview 等候名单

常见问题

我如何在 localhost 上测试 Shopify Webhook?
启动一个像 PortPreview 的隧道,获取公开 HTTPS URL,并在 Shopify Partners 仪表盘中将它注册为 webhook 端点。把应用装到开发店上,触发事件,投递就会到达你的本地处理器。
为什么我的 Shopify HMAC 验证总是失败?
大多数情况是编码。Shopify 用 HMAC SHA-256 签名并把结果做 base64 编码。如果你的代码用 .digest('hex') 而不是 .digest('base64'),每次比较都失败,即使密钥正确。
我需要 shopify app dev 还是可以用任何隧道?
任何隧道都行。shopify app dev 为方便包含自己的隧道,但带请求捕获和重放的通用隧道对持续开发更有用。