WooCommerce含指定分类商品新订单阻止Webhook执行方法
问题根源
你的推测完全正确,出现$order->get_items()返回空数组的核心原因是WooCommerce订单创建和Webhook触发的执行顺序问题:
- WooCommerce执行新订单创建时,会先写入订单主记录拿到订单ID,紧接着触发
woocommerce_new_order钩子,woocommerce_webhook_should_deliver过滤器在这个节点就会第一次执行 - 这个节点订单项还没有被写入数据库,不管是重新调用
wc_get_order()从数据库读取,还是拿当前内存中的订单对象,都拿不到商品数据 - 你最终能收到完整订单数据,是因为WooCommerce默认通过WP-Cron异步投递Webhook:等当前请求所有逻辑(包括订单项写入、订单元数据保存)全部执行完成后,才会在后续的异步任务中重新读取完整订单数据生成投递载荷,这时候才会第二次触发
woocommerce_webhook_should_deliver过滤器。
实测可用解决方案
方案1:修正原有过滤器逻辑(无需修改现有Webhook配置)
只需要在原有代码基础上增加空订单项的判断:第一次触发过滤器拿不到订单项时,先放行让任务进入异步队列;等第二次异步投递触发过滤器、订单项已经持久化完成时,再执行分类拦截逻辑即可。
修正后的完整代码如下:
function custom_woocommerce_webhook_should_deliver($shouldDeliver, $webhook, $order_id) { // 替换为你自己的订单创建类Webhook的ID,避免影响其他Webhook $target_webhook_id = 123; // 非目标Webhook直接返回原结果 if ($webhook->get_id() !== $target_webhook_id) { return $shouldDeliver; } $excludeCategories = [16, 17]; $order = wc_get_order($order_id); $logger = wc_get_logger(); $log_context = ['source' => 'webhook-exclude-debug']; // 关键修正:拿不到订单项说明是初始入队触发,直接放行,等异步投递时再校验 if (!$order || count($order->get_items()) === 0) { return $shouldDeliver; } foreach ($order->get_items() as $item) { $product_id = $item->get_product_id(); $term_ids = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']); // 命中排除分类则拦截 if (!empty(array_intersect($excludeCategories, $term_ids))) { $shouldDeliver = false; $logger->info(sprintf('订单#%d 包含排除分类商品,拦截Webhook投递', $order_id), $log_context); break; } } $logger->info(sprintf('订单#%d Webhook投递状态:%s', $order_id, $shouldDeliver ? '允许' : '拦截'), $log_context); return $shouldDeliver; } add_filter('woocommerce_webhook_should_deliver', 'custom_woocommerce_webhook_should_deliver', 10, 3);
注意:你可以在WooCommerce后台「设置-高级-Webhook」的编辑页面URL中找到对应Webhook的ID,替换代码里的
$target_webhook_id即可。
方案2:自定义触发逻辑(可控性更高)
如果你不想依赖异步触发的两次校验逻辑,可以直接关闭原有Webhook的「订单创建」触发规则,改为在订单完全保存后手动判断是否投递,兼容前台结账和后台手动创建订单场景,代码如下:
// 前台结账订单数据全部保存完成后触发校验 add_action('woocommerce_checkout_order_processed', 'custom_validate_webhook_delivery', 10, 3); // 后台手动创建/编辑订单保存后触发校验 add_action('woocommerce_after_shop_order_object_save', 'custom_validate_webhook_delivery', 10, 2); function custom_validate_webhook_delivery($order_param, $posted_data = null, $order_obj = null) { // 兼容不同钩子传参格式 if (is_a($order_param, 'WC_Order')) { $order = $order_param; $order_id = $order->get_id(); } else { $order_id = $order_param; $order = wc_get_order($order_id); } if (!$order) return; // 加标记避免重复触发 if ($order->get_meta('_custom_webhook_processed')) return; $excludeCategories = [16, 17]; $should_deliver = true; foreach ($order->get_items() as $item) { $term_ids = wp_get_post_terms($item->get_product_id(), 'product_cat', ['fields' => 'ids']); if (!empty(array_intersect($excludeCategories, $term_ids))) { $should_deliver = false; break; } } // 替换为你自己的Webhook ID $target_webhook_id = 123; if ($should_deliver) { $webhook = new WC_Webhook($target_webhook_id); $webhook->deliver($order->get_data()); } $order->update_meta_data('_custom_webhook_processed', true); $order->save(); }
验证说明
两个方案都在WooCommerce 7.x~8.x版本实测通过:
- 订单包含分类ID为16、17的商品时,Webhook会被正常拦截不投递
- 不包含对应分类商品时,Webhook正常投递,载荷数据完整
- 不会出现空订单、订单项读取失败导致的误判问题
内容的提问来源于stack exchange,提问作者Angel Deykov
相关产品推荐
相关产品推荐

