2026年5月23日

2026年5月23日

WordPressのWooCommerceにカスタム配送方法を追加する方法

はじめに

「WooCommerceに独自の配送料計算ロジックを追加したい」「重量・個数・地域によって送料を動的に変えたい」「配送APIと連携してリアルタイムで送料を取得したい」——WC_Shipping_Methodクラスを継承することでカスタム配送方法を実装できます。

症状・原因

WooCommerceの標準配送方法(定額・無料・集荷)では対応できない複雑な送料計算や、ヤマト運輸・佐川急便・日本郵便などのAPIと連携してリアルタイムで送料を取得したい場合にカスタム配送方法を実装します。

解決手順

ステップ1:WC_Shipping_Methodを継承したクラスを作成する

// includes/class-my-shipping-method.php
if ( ! defined( 'ABSPATH' ) ) exit;

class My_Shipping_Method extends WC_Shipping_Method {

    public function __construct( int $instance_id = 0 ) {
        $this->id                 = 'my_shipping';
        $this->instance_id        = absint( $instance_id );
        $this->method_title       = '独自配送';
        $this->method_description = '重量と地域に基づくカスタム配送料';
        $this->supports           = [
            'shipping-zones',        // 配送ゾーン対応
            'instance-settings',     // ゾーンごとの設定
            'instance-settings-modal',
        ];

        $this->init();

        // 設定変更時の保存フック
        add_action(
            'woocommerce_update_options_shipping_' . $this->id,
            [ $this, 'process_admin_options' ]
        );
    }

    public function init(): void {
        $this->init_form_fields();
        $this->init_settings();

        $this->title        = $this->get_option( 'title', '独自配送' );
        $this->base_cost    = (float) $this->get_option( 'base_cost', 500 );
        $this->weight_rate  = (float) $this->get_option( 'weight_rate', 100 );
        $this->free_over    = (float) $this->get_option( 'free_over', 0 );
    }

    public function init_form_fields(): void {
        $this->instance_form_fields = [
            'title' => [
                'title'       => '配送方法名',
                'type'        => 'text',
                'default'     => '独自配送',
                'description' => 'チェックアウトに表示される名前',
            ],
            'base_cost' => [
                'title'       => '基本料金(円)',
                'type'        => 'number',
                'default'     => '500',
                'custom_attributes' => [ 'min' => 0, 'step' => 1 ],
            ],
            'weight_rate' => [
                'title'       => '重量追加料金(円/kg)',
                'type'        => 'number',
                'default'     => '100',
                'description' => '1kgあたりの追加料金',
            ],
            'free_over' => [
                'title'       => '送料無料の注文金額(0=無効)',
                'type'        => 'number',
                'default'     => '0',
                'custom_attributes' => [ 'min' => 0, 'step' => 1 ],
            ],
        ];
    }
}

ステップ2:calculate_shipping()で送料を計算する

// calculate_shipping() メソッドを追加
public function calculate_shipping( array $package = [] ): void {
    $total_weight = 0;
    $cart_total   = WC()->cart->get_subtotal();

    // カートの総重量を計算
    foreach ( $package['contents'] as $item ) {
        $product      = $item['data'];
        $weight       = (float) $product->get_weight();      // kg
        $quantity     = (int)   $item['quantity'];
        $total_weight += $weight * $quantity;
    }

    // 送料無料チェック
    if ( $this->free_over > 0 && $cart_total >= $this->free_over ) {
        $this->add_rate( [
            'id'       => $this->get_rate_id(),
            'label'    => $this->title . '(送料無料)',
            'cost'     => 0,
            'calc_tax' => 'per_order',
        ] );
        return;
    }

    // 基本料金 + 重量追加料金
    $cost = $this->base_cost + ( $total_weight * $this->weight_rate );

    // 配送ゾーンの都道府県で追加料金
    $destination = $package['destination'];
    $state       = $destination['state'] ?? '';

    // 沖縄・北海道は追加料金
    $remote_states = [ 'JP01', 'JP47' ]; // 北海道=JP01, 沖縄=JP47
    if ( in_array( $state, $remote_states, true ) ) {
        $cost += 1000;
    }

    $this->add_rate( [
        'id'       => $this->get_rate_id(),
        'label'    => $this->title,
        'cost'     => round( $cost ),
        'calc_tax' => 'per_order',
        'meta_data' => [
            'weight'      => $total_weight,
            'destination' => $state,
        ],
    ] );
}

ステップ3:外部APIでリアルタイム送料を取得する

// 外部APIから送料を取得するパターン
public function calculate_shipping( array $package = [] ): void {
    $destination = $package['destination'];
    $total_weight = 0;

    foreach ( $package['contents'] as $item ) {
        $total_weight += (float) $item['data']->get_weight() * $item['quantity'];
    }

    // キャッシュキー(宛先+重量でユニーク)
    $cache_key = 'my_shipping_rate_' . md5( serialize( [
        'zip'    => $destination['postcode'],
        'weight' => $total_weight,
    ] ) );

    $rates = get_transient( $cache_key );

    if ( false === $rates ) {
        $response = wp_remote_post( 'https://api.myshipping.example.com/rates', [
            'headers' => [ 'Content-Type' => 'application/json' ],
            'body'    => wp_json_encode( [
                'origin_zip'   => get_option( 'woocommerce_store_postcode' ),
                'dest_zip'     => $destination['postcode'],
                'weight_kg'    => $total_weight,
                'currency'     => get_woocommerce_currency(),
            ] ),
            'timeout' => 10,
        ] );

        if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
            // API失敗時はフォールバック料金を使用
            $this->add_rate( [
                'id'    => $this->get_rate_id() . '_fallback',
                'label' => $this->title . '(概算)',
                'cost'  => $this->base_cost,
            ] );
            return;
        }

        $rates = json_decode( wp_remote_retrieve_body( $response ), true );
        set_transient( $cache_key, $rates, 30 * MINUTE_IN_SECONDS );
    }

    // APIから返ってきた複数の配送オプションを登録
    foreach ( $rates as $rate ) {
        $this->add_rate( [
            'id'    => $this->get_rate_id() . '_' . $rate['service_code'],
            'label' => $rate['service_name'],
            'cost'  => $rate['price'],
        ] );
    }
}

ステップ4:配送方法をWooCommerceに登録する

// my-shipping-plugin.php: プラグインのメインファイル
add_action( 'woocommerce_shipping_init', function(): void {
    require_once plugin_dir_path( __FILE__ ) . 'includes/class-my-shipping-method.php';
} );

add_filter( 'woocommerce_shipping_methods', function( array $methods ): array {
    $methods['my_shipping'] = 'My_Shipping_Method';
    return $methods;
} );

ステップ5:配送ゾーンとデバッグ設定

// functions.php: 配送のデバッグ(開発時のみ)
add_action( 'woocommerce_after_shipping_rate', function( WC_Shipping_Rate $rate ): void {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    $meta = $rate->get_meta_data();
    if ( ! empty( $meta ) ) {
        echo '<small style="color:#999"> [重量: ' . esc_html( $meta['weight'] ?? '?' ) . 'kg]</small>';
    }
}, 10 );

// 配送ゾーンを設定する手順:
// 1. WooCommerce → 設定 → 配送 → 配送ゾーン
// 2. ゾーンを追加(例: 「国内」→ リージョン: Japan)
// 3. ゾーンに「独自配送」を追加
// 4. 設定画面で base_cost / weight_rate / free_over を設定

// 配送料のキャッシュをクリアする
add_action( 'woocommerce_checkout_update_order_review', function(): void {
    // 配送料のセッションキャッシュをクリア(テスト時に有効)
    // WC()->session->set( 'shipping_for_package_0', null );
} );

注意事項

  • calculate_shipping()内で外部APIを呼び出す場合は必ずタイムアウトを設定し(timeout: 10程度)、API障害時のフォールバック処理を実装してください。チェックアウトページのロードが遅くなる可能性があります。
  • 配送料はトランジェントでキャッシュすることを推奨します。ただし郵便番号・重量・目的地が変わったら必ず異なるキャッシュキーを使用してください。
  • supports'shipping-zones'を含めないと、配送ゾーン設定画面に表示されません。

まとめ

WooCommerceカスタム配送方法の実装は「WC_Shipping_Methodを継承→init_form_fields()でゾーンごとの設定項目を定義→calculate_shipping()でカートの重量・宛先・金額から送料を計算してadd_rate()で追加→外部API連携時はトランジェントでキャッシュ→woocommerce_shipping_methodsフィルターでクラスを登録→配送ゾーン設定画面で有効化」の流れで整備します。関連記事:WordPressでWooCommerceカスタム決済ゲートウェイを実装する方法WordPressのWooCommerceチェックアウトをカスタマイズする方法

お気軽にご相談ください

お見積りへ お問い合わせへ