You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:09:16