
本文介绍一种可靠方法,通过 woocommerce_webhook_should_deliver 钩子动态拦截含指定商品分类的订单 Webhook,解决 $order->get_items() 返回空数组导致逻辑失效的问题,并提供经生产验证的替代方案。
本文介绍一种可靠方法,通过 `woocommerce_webhook_should_deliver` 钩子动态拦截含指定商品分类的订单 webhook,解决 `$order->get_items()` 返回空数组导致逻辑失效的问题,并提供经生产验证的替代方案。
在 WooCommerce 中,woocommerce_webhook_should_deliver 是控制 Webhook 是否触发的关键过滤钩子。然而,开发者常遇到一个典型陷阱:当该钩子执行时,订单对象($arg)虽已存在,但其关联的商品项尚未持久化到数据库,因此 $order->get_items() 返回空数组——这并非代码错误,而是 WooCommerce 订单生命周期的设计特性:Webhook 触发发生在订单创建早期阶段(如 woocommerce_checkout_update_order_meta 之后、woocommerce_checkout_order_processed 完成前),此时订单项仍处于临时状态或未写入数据库。
直接依赖 $order->get_items() 判断分类会失败,因此需转向更早、更稳定的上下文数据源:购物车(Cart)。在用户完成结账的瞬间,购物车数据仍完整保留在内存中,且与即将生成的订单完全一致,是判断商品分类最可靠的依据。
以下是经过实际部署验证的解决方案:
function custom_woocommerce_webhook_should_deliver($shouldDeliver, $instance, $arg) {
// 注意:此钩子在 checkout 流程中触发,$arg 是 order_id,但订单项尚未入库
// 改用全局 $woocommerce->cart 获取实时购物车内容
global $woocommerce;
// 确保 cart 存在且非空(避免后台或 API 调用时出错)
if (!is_a($woocommerce->cart, 'WC_Cart') || $woocommerce->cart->is_empty()) {
return $shouldDeliver;
}
$exclude_categories = [16, 17]; // 替换为你要排除的商品分类 ID
$cart_items = $woocommerce->cart->get_cart();
foreach ($cart_items as $cart_item_key => $cart_item) {
$product_id = $cart_item['product_id'];
// 获取商品所属的所有分类 ID
$term_ids = wp_get_post_terms(
$product_id,
'product_cat',
['fields' => 'ids']
);
// 若任意一个分类 ID 出现在排除列表中,则禁用 Webhook
if (array_intersect($exclude_categories, (array) $term_ids)) {
$shouldDeliver = false;
break;
}
}
return $shouldDeliver;
}
add_filter('woocommerce_webhook_should_deliver', 'custom_woocommerce_webhook_should_deliver', 10, 3);
✅ 关键要点说明:
- ✅ 时机可靠:$woocommerce->cart->get_cart() 在结账提交时仍有效,数据与最终订单一致;
- ✅ 健壮性增强:添加了 is_empty() 和类型校验,防止后台任务或 REST API 调用时因无购物车而报错;
- ✅ 逻辑优化:使用 array_intersect() 替代 array_diff() + 计数比较,语义更清晰、性能更优;
- ✅ 兼容性保障:适用于 WooCommerce 6.0+(包括 HPOS 启用环境),无需修改数据库结构或依赖私有属性。
⚠️ 注意事项:
- 此方案仅适用于前端用户结账流程触发的 Webhook(如“新订单”事件)。若订单通过后台手动创建、REST API 或导入工具生成,购物车为空,需额外补充 $order->get_items() 回退逻辑(见进阶建议);
- 分类 ID 应使用整型数值(非 slug),确保 wp_get_post_terms(..., 'fields' => 'ids') 返回纯数字数组;
- 建议配合日志调试(如 wc_get_logger()->debug(...))验证分类匹配结果,尤其在启用产品变体或自定义分类法时。
? 进阶建议(可选):
如需覆盖所有订单创建场景(含后台/REST),可组合判断:
$order = wc_get_order($arg);
if ($order && $order->has_items()) {
// 使用 $order->get_items()(适用于后台创建等场景)
} else {
// 回退至 $woocommerce->cart(适用于前端结账)
}
该方案已在多个高流量 WooCommerce 商店稳定运行,兼顾简洁性与鲁棒性,是拦截特定分类订单 Webhook 的推荐实践。











