要在本地主机上测试 WooCommerce Webhook,请使用 HTTPS 隧道公开本地处理程序,在 WooCommerce → 设置 → 高级 → Webhooks 下创建一个 Webhook,然后验证 X-WC-Webhook-Signature 作为原始主体的 Base64 HMAC-SHA256 摘要。 在安全的测试商店中触发订单或产品更改、检查交付情况并进行迭代,而无需部署接收器。
WooCommerce 发送的内容以及发送时间
当创建、更新或删除订单、产品、优惠券或客户时,WooCommerce 可以通知交付 URL。扩展可以添加主题,开发者可以定义自定义主题。每个配置的 Webhook 都有名称、状态、主题、交付 URL、机密和 API 版本。这 官方 WooCommerce webhook 文档 描述创建、主题、交付日志和失败行为。
Webhook 附加到主题,而不是自动附加到每个存储突变。选择您的集成所需的最狭窄的主题。创建订单的消费者不应该同时处理每个产品更新。这可以减少本地测试期间的个人数据暴露、流量和意外副作用。
创建原始 Express 端点
WooCommerce 的签名是根据其发送的正文计算的。保留这些字节直到验证完成。签名标头包含 Base64 编码的二进制 HMAC-SHA256 摘要,而不是十六进制字符串。
import express from 'express';
import crypto from 'node:crypto';
const app = express();
function validWooSignature(rawBody, supplied, secret) {
if (!supplied || !secret) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('base64');
const a = Buffer.from(supplied);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post(
'/webhooks/woocommerce',
express.raw({ type: 'application/json', limit: '2mb' }),
async (req, res) => {
const supplied = req.get('x-wc-webhook-signature');
if (!validWooSignature(req.body, supplied, process.env.WC_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const payload = JSON.parse(req.body.toString('utf8'));
await webhookInbox.insertOnce({
deliveryId: req.get('x-wc-webhook-delivery-id'),
topic: req.get('x-wc-webhook-topic'),
payload,
});
return res.sendStatus(202);
},
);
app.use(express.json());
app.listen(3000);
特定于路由的原始解析器必须在全局 JSON 解析器之前运行。如果中间件首先解析主体,则重新字符串化对象可能会更改空格或转义并使摘要无效。这与以下内容中涵盖的原始主体规则相同 Webhook 签名指南,但 WooCommerce 专门使用 Base64 输出。
启动 HTTPS 隧道
- 启动您的接收器并确认其正在收听
http://localhost:3000。 - 跑步
npx portpreview 3000在第二个航站楼。 - 复制公共 HTTPS URL 并附加
/webhooks/woocommerce。 - 在 WordPress 发送初始 ping 和主题传递时保持进程运行。
WordPress 主机(而不是您打开 wp-admin 的浏览器)必须能够访问公共 URL。隧道将公共请求与您的私人开发流程联系起来。它还提供受信任的 TLS,因此您无需公开路由器端口或安装自己的公共证书。
在 WooCommerce 中配置 Webhook
- 打开 WooCommerce → 设置 → 高级 → Webhooks。
- 选择 添加网络钩子 并给它一个可识别的本地开发名称。
- 选择 积极的 状态和特定主题,例如已创建订单。
- 粘贴完整的隧道传送 URL。
- 生成一个长随机秘密并将相同的值放入
WC_WEBHOOK_SECRET。 - 保存 Webhook,然后在测试存储中触发主题。
首次保存活动 Webhook 时,WooCommerce 会向交付 URL 发送 ping。 ping 确认连接,但不能替代真实订单有效负载。让您的端点容忍初始请求,然后创建或更新测试数据以练习所选主题。
export WC_WEBHOOK_SECRET="$(openssl rand -base64 48)"
如果将 Base64 机密粘贴到环境文件中,请引用它,以便保留标点符号。秘密是 HMAC 密钥; WooCommerce REST API 使用者密钥和 WordPress 密码是不相关的凭据。
使用标题来路由和跟踪交付
WooCommerce 包含有用的元数据标头。根据版本和环境,这些包括主题、资源、事件、源、Webhook ID 和交付 ID。按照 HTTP 的要求,不区分大小写地处理名称。使用主题进行调度,使用交付 ID 进行追踪,但始终首先对正文进行身份验证。
const handlers = {
'order.created': handleOrderCreated,
'order.updated': handleOrderUpdated,
'product.updated': handleProductUpdated,
};
const handler = handlers[topic];
if (handler) await handler(payload);
else await recordUnsupportedTopic(topic);
不要仅从 JSON 形状推断主题。创建的订单和更新的订单有效负载可能看起来相似,但正确的下游操作不同。相反,拒绝您的端点从未配置为接受的标头/主题组合。
防御性地处理订单有效负载
使用不可变标识符
按商店标识和 WooCommerce 对象 ID(而不是订单号格式、客户电子邮件或显示名称)关联记录。两个商店都可以具有订单 ID 42,因此多商店集成需要复合键。
期望扩展改变字段
付款、订阅、税务、结帐和履行扩展可以添加元数据和行项目字段。验证业务逻辑所需的字段,忽略未知字段,并保存架构版本或最小编辑的固定装置以进行回归测试。
将事件接收与履行分开
表示订单已更改的 Webhook 应进入持久队列或收件箱。库存同步、运输标签、ERP 呼叫和客户电子邮件应在确认后运行。这可以防止缓慢的依赖关系使 WooCommerce 将成功的接收解释为失败的交付。
模型随着状态转换而更新
订单可能会经历待处理、处理中、暂停、已完成、取消、退款或失败状态。更新可能会很快发生,并且交付顺序并不能安全地替代比较时间戳和当前源状态。让重复的转换变得无害。
商业事件必须具有幂等性
在您的接收者提交之后、WooCommerce 看到响应之前,可能会发生超时。然后,重新传递会产生相同的业务操作,除非处理程序是幂等的。存储交付 ID(如果存在)。还强制执行域级别的唯一性,例如每个商店和订单转换一个履行请求。
await db.transaction(async (tx) => {
if (!(await tx.deliveries.claim(deliveryId))) return;
await tx.orders.applyWooCommerceEvent(storeId, topic, payload);
await tx.outbox.enqueueRequiredActions(storeId, topic, payload.id);
});
收件箱加发件箱事务可以防止重复处理和丢失后续工作。看 webhook 重试和幂等性 以获得完整的图案。
使用 WooCommerce 日志调试发送方
WooCommerce 记录 Webhook 交付。打开 WooCommerce → 状态 → 日志 并过滤官方文档中描述的 webhook 传递源。将传送 URL、请求时间、响应状态和响应正文与本地隧道跟踪进行比较。发件人日志回答 WordPress 是否尝试了该请求;接收器日志回答您的应用程序用它做了什么。
请勿将未编辑的订单有效负载复制到公共问题中。它可以包含姓名、帐单和送货地址、电子邮件、电话、产品选择和付款元数据。将固定装置减少到重现错误所需的字段。
排查常见的 WooCommerce Webhook 故障
Webhook 被禁用
在连续五次以上的交付失败后,WooCommerce 会自动禁用 Webhook。根据官方指南,2xx、301 或 302 之外的响应将被视为失败。修复端点、重新激活 webhook 并发送受控测试。无论如何都要避免重定向:它们会使签名调试变得复杂,并且可能会意外地将签名的客户数据发送到非预期的主机。
签名总是不同的
使用 Webhook 配置的密钥对确切的原始正文进行哈希处理,请求二进制 HMAC 输出,然后对其进行 Base64 编码。在节点中,即 .digest('base64')。常见错误是使用十六进制、使用 REST API 密钥、首先解析 JSON 或包含额外的换行字节。
初始 ping 有效,但订单事件无效
确认所选主题与您触发的操作匹配。创建订单和更改现有订单是不同的主题。验证状态为“活动”,检查 WooCommerce 日志,并确保插件或临时缓存不会阻止底层挂钩。
本地请求返回404
检查全路径、路由方式、隧道目标端口。 WordPress 必须发布到 /webhooks/woocommerce,不仅仅是隧道起源。框架中间件不应将 Webhook 重定向到本地化或经过身份验证的页面。
交货超时
保留已验证的事件并立即返回200或202。将远程 API 调用和繁重的转换转移给工作人员。检查本地断点是否暂停请求足够长的时间以将其分类为失败。
重放有效负载会导致 401
捕获的请求必须保留准确的原始字节和签名标头。编辑 JSON 会使原始签名失效。对于业务逻辑测试,在验证边界后使用经过净化的夹具;对于端到端测试,生成具有专用测试密钥的新 HMAC。遵循 安全重播工作流程。
本地商店数据的安全检查表
- 尽可能使用合成客户和产品在临时商店进行测试。
- 使用独特的 webhook 秘密进行本地开发,并在暴露后轮换它。
- 在解析、记录或将正文排队之前验证签名。
- 加密验证后将预期存储源和主题列入白名单。
- 从捕获中编辑地址、联系方式、订单备注和付款元数据。
- 切勿禁用 TLS 验证或向接收者公开 wp-admin 凭据。
本地架构应与生产相匹配:HTTPS 传输、原始主体身份验证、持久接受、幂等处理、快速响应和可审核的故障。对于具有不同 HMAC 标头的另一个商业提供商,请比较 Shopify 本地 webhook 指南。
