WooCommerceでカテゴリ単位で『特定国からの注文を制限』する

日本郵便がアメリカ宛ての荷物の引受を一時停止したため、WooCommerceで、「特定カテゴリの商品を特定の国から注文できないようにする」ように設定しました。

ここでは、その方法を備忘録としてまとめています。

今回の経緯として、日本郵便がアメリカ宛ての荷物の引受を停止し、関税の影響で発送ができなくなってしまいました。(2020年のコロナ禍を思い出しました。)

これまでは、商品ページの上部に発送できない国は表示していましたが、それでも注文を受けることがあり、返金処理が必要になったり、お客様にとっても私にとってもよくない状況でした。

そのため、ショップ側でも特定の商品カテゴリを特定の国からは注文できないようにできないかなと思っていました。

ですが、WooCommerceには特定のカテゴリ商品に対して、特定国からの注文を制限する機能はありません。

アメリカからのオーダーは多いので、今回【WPCodeプラグイン】を使って、発送できない国を指定することで、誤って注文が入り返金処理が必要になるような状況を防ぎたいと思いました。

さらに、「カートに追加」したときや「チェックアウト画面」でも配送先の国を判定し、該当する国であればエラーメッセージを表示して注文をブロックするようにしました。

使用したコード(ここをクリックで表示)

<?PHP
// US宛てへの「cat-toys」カテゴリをブロック
// - 商品ページ:Add to Cart 時にエラー(優先ロジック)
// - カート/チェックアウト:保険として再検証
// カテゴリ: cat-toys / 国コード: US

if ( ! function_exists('nyagomi_us_msg') ) {
    function nyagomi_us_msg(){
        return "We're temporarily unable to ship cat toys to the U.S. due to Japan Post's suspension and customs regulations.";
    }
}

if ( ! function_exists('nyagomi_get_country_from_context') ) {
    function nyagomi_get_country_from_context( $checkout_data = [] ){
        if ( is_array($checkout_data) && ! empty($checkout_data) ) {
            if ( ! empty( $checkout_data['shipping_country'] ) ) {
                return strtoupper( wc_clean( $checkout_data['shipping_country'] ) );
            }
            if ( ! empty( $checkout_data['billing_country'] ) ) {
                return strtoupper( wc_clean( $checkout_data['billing_country'] ) );
            }
        }
        if ( function_exists('WC') && WC()->customer ) {
            $country = WC()->customer->get_shipping_country();
            if ( ! $country ) $country = WC()->customer->get_billing_country();
            return strtoupper( (string) $country );
        }
        return '';
    }
}

if ( ! function_exists('nyagomi_cart_has_category') ) {
    function nyagomi_cart_has_category( $category_slug ){
        if ( ! function_exists('WC') || ! WC()->cart || WC()->cart->is_empty() ) return false;
        foreach ( WC()->cart->get_cart() as $item ) {
            $ids = array_filter([
                isset($item['product_id'])   ? (int) $item['product_id']   : 0,
                isset($item['variation_id']) ? (int) $item['variation_id'] : 0,
            ]);
            foreach ( $ids as $id ) {
                $parent_id = wp_get_post_parent_id( $id );
                if ( has_term( $category_slug, 'product_cat', $id )
                  || ( $parent_id && has_term( $category_slug, 'product_cat', $parent_id ) ) ) {
                    return true;
                }
            }
        }
        return false;
    }
}

if ( ! function_exists('nyagomi_product_is_in_category') ) {
    function nyagomi_product_is_in_category( $product_id, $variation_id, $category_slug ){
        $ids = array_filter([ (int) $product_id, (int) $variation_id ]);
        foreach ( $ids as $id ){
            if ( $id && has_term( $category_slug, 'product_cat', $id ) ) return true;
            $parent_id = wp_get_post_parent_id( $id );
            if ( $parent_id && has_term( $category_slug, 'product_cat', $parent_id ) ) return true;
        }
        return false;
    }
}

// 1) 商品ページ:Add to Cart 段階でブロック
add_filter('woocommerce_add_to_cart_validation','nyagomi_block_add_to_cart_us_cat_toys',10,5);
function nyagomi_block_add_to_cart_us_cat_toys($passed, $product_id, $quantity, $variation_id = 0, $cart_item_data = []){
    $blocked_country = 'US';
    $category_slug   = 'cat-toys';
    $country         = nyagomi_get_country_from_context();

    if ( $country !== $blocked_country ) return $passed;

    if ( nyagomi_product_is_in_category( $product_id, $variation_id, $category_slug ) ) {
        wc_add_notice( nyagomi_us_msg(), 'error' );
        return false;
    }
    return $passed;
}

// 2) カート画面:保険として再チェック
add_action('woocommerce_check_cart_items','nyagomi_cart_notice_us_for_cat_toys');
function nyagomi_cart_notice_us_for_cat_toys(){
    $blocked_country = 'US';
    $category_slug   = 'cat-toys';
    $country         = nyagomi_get_country_from_context();

    if ( $country === $blocked_country && nyagomi_cart_has_category( $category_slug ) ) {
        wc_add_notice( nyagomi_us_msg(), 'error' );
    }
}

// 3) チェックアウト:最終ブロック
add_action('woocommerce_after_checkout_validation','nyagomi_block_us_for_cat_toys',10,2);
function nyagomi_block_us_for_cat_toys( $data, $errors ){
    $blocked_country = 'US';
    $category_slug   = 'cat-toys';
    $country         = nyagomi_get_country_from_context( $data );

    if ( $country === $blocked_country && nyagomi_cart_has_category( $category_slug ) ) {
        $errors->add( 'nyagomi_us_cat_toys_block', nyagomi_us_msg() );
    }
}

エラーメッセージは、少し長いのですがお客様にきちんと理由が伝わるように記載しました。今回使用した英文はこちらです。

We’re temporarily unable to ship cat toys to the U.S. due to Japan Post’s suspension and customs regulations.

(日本語訳:日本郵便の引受停止および通関規制により、現在アメリカ合衆国への猫のおもちゃの発送は一時的にできません。)

今回はアメリカのみを対象に制限しましたが、複数国も指定することができます。

複数の国を制限したい場合には、単一の $blocked_country = 'US'; ではなく、以下のように、国をカンマで区切って複数指定することができます。

$blocked_countries = ['US','CA','AU'];
if ( in_array( $country, $blocked_countries, true ) ) {
   // ブロック処理
}

今回のように、郵便事情や関税規制などによって一時的に販売を制限しなければならない場合は、コードを使えば注文を受けられない国からのオーダーにも対応することができるので安心です。

AD