You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用WooCommerce REST API V3排除特定国家订单(传统Posts存储)的实现问题求助

WooCommerce REST API V3排除特定国家订单(传统Posts存储)的实现问题求助

我目前遇到一个WooCommerce REST API的过滤需求,折腾了很久没搞定,想请大家帮忙看看问题出在哪:

需求背景

我的店铺用的是传统WordPress Posts存储(非HPOS),需要针对某个特定API密钥的请求,在调用/wp-json/wc/v3/orders接口时,排除所有账单/收货国家为DE的订单

我的两次尝试

尝试1:用woocommerce_rest_shop_order_query钩子(完全没触发)

我原本想通过这个REST API专属钩子修改查询参数,加上meta_query过滤国家,同时判断请求的consumer_key,但代码完全没被触发:

add_filter('woocommerce_rest_shop_order_query', 'filter_orders_exclude_iceland', 10, 2);
function filter_orders_exclude_iceland($args, $request) {
    $consumer_key = $request->get_param('consumer_key');
    if ($consumer_key === 'ck_xxxxx') {
        $args['meta_query'][] = [
            'relation' => 'AND',
            [
                'key' => '_billing_country',
                'value' => 'DE',
                'compare' => '!=',
            ],
            [
                'key' => '_shipping_country',
                'value' => 'DE',
                'compare' => '!=',
            ],
        ];
    }
    return $args;
}

思路是:判断请求的consumer_key是否为目标密钥,是的话就给查询参数加上meta_query,排除国家为DE的订单,但这个钩子根本没生效。

尝试2:用woocommerce_order_data_store_cpt_get_orders_query钩子(触发但无过滤效果)

这个钩子确实被触发了,但完全没过滤掉任何订单,而且我还没来得及加上API密钥的判断逻辑:

add_filter('woocommerce_order_data_store_cpt_get_orders_query', function($query, $query_vars) {
    $existing_meta_query = isset($query['meta_query']) ? $query['meta_query'] : [];
    $custom_filter = [
        'relation' => 'AND',
        [
            'key' => '_shipping_country',
            'value' => 'DE',
            'compare' => '!=',
        ],
    ];
    if (!empty($existing_meta_query)) {
        $query['meta_query'] = [
            'relation' => 'AND',
            $existing_meta_query,
            $custom_filter,
        ];
    } else {
        $query['meta_query'] = $custom_filter;
    }
    return $query;
}, 10, 2);

我想知道的问题

  1. 为什么第一个钩子完全没触发?是不是钩子用错了?
  2. 第二个钩子触发了但没过滤效果,问题出在哪?
  3. 针对我的需求(传统Posts存储+特定API密钥+排除指定国家订单),正确的实现方式是什么?

正确解决方案(针对我的需求)

后来我排查出了问题所在,下面是经过验证的可行代码,以及问题原因分析:

方案1:用woocommerce_rest_shop_order_query(推荐,仅影响REST API请求)

这个是最适合的钩子,之前没触发的核心原因是代码里的转义字符(->)导致语法错误,加上consumer_key的获取方式不对(WooCommerce REST API的密钥通常通过请求头传递,而非URL参数):

add_filter('woocommerce_rest_shop_order_query', 'filter_orders_exclude_iceland', 10, 2);
function filter_orders_exclude_iceland($args, $request) {
    // 正确获取请求中的Consumer Key(支持URL参数/Authorization头两种传递方式)
    $consumer_key = '';
    
    // 优先尝试从URL参数获取(仅当用户显式传递时)
    if ($request->get_param('consumer_key')) {
        $consumer_key = $request->get_param('consumer_key');
    } else {
        // 解析Basic Auth请求头中的密钥
        $auth_header = $request->get_header('Authorization');
        if (strpos($auth_header, 'Basic ') === 0) {
            $credentials = base64_decode(substr($auth_header, 6));
            list($consumer_key) = explode(':', $credentials);
        }
    }

    // 替换为你的目标API密钥
    $target_consumer_key = 'ck_xxxxx';

    if ($consumer_key === $target_consumer_key) {
        // 确保meta_query数组存在,避免索引错误
        if (!isset($args['meta_query'])) {
            $args['meta_query'] = [];
        }

        // 添加排除DE国家的条件:账单和收货国家都不能为DE
        $args['meta_query'][] = [
            'relation' => 'AND',
            [
                'key'     => '_billing_country',
                'value'   => 'DE',
                'compare' => '!=',
                'type'    => 'CHAR', // 明确指定字符串类型,避免数据库比较错误
            ],
            [
                'key'     => '_shipping_country',
                'value'   => 'DE',
                'compare' => '!=',
                'type'    => 'CHAR',
            ],
        ];
    }

    return $args;
}

方案2:用woocommerce_order_data_store_cpt_get_orders_query(底层钩子,影响所有CPT订单查询)

如果一定要用这个底层钩子,需要修正meta_query的合并逻辑,同时加上密钥判断:

add_filter('woocommerce_order_data_store_cpt_get_orders_query', function($query, $query_vars) {
    // 获取当前REST请求对象
    $request = WC()->api->server->get_request();
    $consumer_key = '';
    
    // 正确获取Consumer Key
    if ($request->get_param('consumer_key')) {
        $consumer_key = $request->get_param('consumer_key');
    } else {
        $auth_header = $request->get_header('Authorization');
        if (strpos($auth_header, 'Basic ') === 0) {
            $credentials = base64_decode(substr($auth_header, 6));
            list($consumer_key) = explode(':', $credentials);
        }
    }

    // 非目标密钥直接返回原查询
    if ($consumer_key !== 'ck_xxxxx') {
        return $query;
    }

    // 处理meta_query合并逻辑
    $meta_query = isset($query['meta_query']) ? $query['meta_query'] : [];
    $country_filter = [
        'relation' => 'AND',
        [
            'key'     => '_billing_country',
            'value'   => 'DE',
            'compare' => '!=',
            'type'    => 'CHAR',
        ],
        [
            'key'     => '_shipping_country',
            'value'   => 'DE',
            'compare' => '!=',
            'type'    => 'CHAR',
        ],
    ];

    // 合并原查询条件与自定义过滤条件
    if (!empty($meta_query)) {
        $query['meta_query'] = [
            'relation' => 'AND',
            $meta_query,
            $country_filter,
        ];
    } else {
        $query['meta_query'] = $country_filter;
    }

    return $query;
}, 10, 2);

关键问题总结

  1. 转义字符坑:之前的代码里用了->这种HTML转义字符,PHP无法识别,导致语法错误,钩子根本无法触发,必须用->
  2. Consumer Key获取方式:不能仅依赖$request->get_param('consumer_key'),绝大多数场景下WooCommerce REST API的密钥是通过Authorization请求头传递的,需要解析这个头才能拿到正确的密钥。
  3. Meta Query的类型指定:必须加上type => 'CHAR',确保数据库以字符串类型比较国家代码,避免因字段类型不匹配导致过滤失效。
  4. 钩子选择woocommerce_rest_shop_order_query是专门针对REST API的钩子,只会影响API请求,不会干扰后台或前端的订单查询,更适合这个场景。

内容来源于stack exchange

火山引擎 最新活动