2026年6月21日

2026年6月21日

WooCommerceサブスクリプション定期購入を実装する方法

はじめに

WooCommerceでSaaS的なビジネスモデルを実現するには、定期購入(サブスクリプション)機能が不可欠です。WooCommerce Subscriptionsプラグインを活用すれば、月額・年額課金、無料トライアル、按分請求など複雑な課金ロジックを実装できます。本記事では、プラグインの基本設定からカスタム拡張まで、実装に必要なコードを詳しく解説します。

症状・原因

  • WooCommerceで月額課金・年額課金を実装したい
  • サブスクリプションのステータス(active/cancelled/expired)を管理したい
  • 更新時にWebhookを送信して外部システムと連携したい
  • 無料トライアル期間を設定して新規顧客を獲得したい
  • 途中加入時の按分請求(プロレーション)を正しく計算したい

解決手順

ステップ1:カスタムサブスクリプション商品タイプを登録する

WooCommerce Subscriptionsのクラスを継承してカスタム商品タイプを作成します。

<?php
/**
 * カスタムサブスクリプション商品タイプの登録
 * plugins/my-subscription/my-subscription.php
 */

add_filter( 'product_type_selector', 'add_custom_subscription_type' );

function add_custom_subscription_type( array $types ): array {
    $types['my_subscription'] = 'マイサブスクリプション';
    return $types;
}

add_action( 'init', 'register_custom_subscription_product_type' );

function register_custom_subscription_product_type() {
    class WC_Product_My_Subscription extends WC_Product_Subscription {
        public function __construct( $product ) {
            $this->product_type = 'my_subscription';
            parent::__construct( $product );
        }

        // 無料トライアル期間を設定
        public function get_trial_length( string $context = 'view' ): string {
            return $this->get_prop( 'subscription_trial_length', $context );
        }

        public function set_trial_length( string $value ): void {
            $this->set_prop( 'subscription_trial_length', absint( $value ) );
        }
    }
}

// WooCommerceに商品クラスを認識させる
add_filter( 'woocommerce_product_class', 'set_custom_subscription_class', 10, 2 );

function set_custom_subscription_class( string $classname, string $product_type ): string {
    if ( 'my_subscription' === $product_type ) {
        return 'WC_Product_My_Subscription';
    }
    return $classname;
}

ステップ2:サブスクリプションステータスを管理する

サブスクリプションのライフサイクルを管理するフックを設定します。

<?php
/**
 * サブスクリプションステータス変更の処理
 */

// アクティブ化時の処理
add_action( 'woocommerce_subscription_status_active', 'on_subscription_activated', 10, 1 );

function on_subscription_activated( WC_Subscription $subscription ): void {
    $user_id    = $subscription->get_user_id();
    $product_id = $subscription->get_items() ? array_values( $subscription->get_items() )[0]->get_product_id() : 0;

    // ユーザーにプレミアム権限を付与
    $user = new WP_User( $user_id );
    $user->add_role( 'premium_member' );

    // カスタムメタを更新
    update_user_meta( $user_id, 'subscription_status', 'active' );
    update_user_meta( $user_id, 'subscription_end_date', $subscription->get_date( 'next_payment' ) );

    // アクティブ化メールを送信
    wp_mail(
        $subscription->get_billing_email(),
        'サブスクリプションが有効になりました',
        "ご購読ありがとうございます。サービスをご利用いただけます。\n\n次回更新日: " . $subscription->get_date( 'next_payment' )
    );
}

// キャンセル時の処理
add_action( 'woocommerce_subscription_status_cancelled', 'on_subscription_cancelled', 10, 1 );

function on_subscription_cancelled( WC_Subscription $subscription ): void {
    $user_id = $subscription->get_user_id();

    $user = new WP_User( $user_id );
    $user->remove_role( 'premium_member' );

    update_user_meta( $user_id, 'subscription_status', 'cancelled' );
}

// 期限切れ時の処理
add_action( 'woocommerce_subscription_status_expired', 'on_subscription_expired', 10, 1 );

function on_subscription_expired( WC_Subscription $subscription ): void {
    $user_id = $subscription->get_user_id();
    update_user_meta( $user_id, 'subscription_status', 'expired' );
    delete_user_meta( $user_id, 'subscription_end_date' );
}

ステップ3:更新イベントのWebhookを実装する

サブスクリプション更新時に外部システムへWebhookを送信します。

<?php
/**
 * サブスクリプション更新Webhookの送信
 */
add_action( 'woocommerce_subscription_renewal_payment_complete', 'send_renewal_webhook', 10, 2 );

function send_renewal_webhook( WC_Subscription $subscription, WC_Order $last_order ): void {
    $webhook_url = get_option( 'my_renewal_webhook_url', '' );

    if ( empty( $webhook_url ) ) {
        return;
    }

    $payload = [
        'event'           => 'subscription.renewed',
        'subscription_id' => $subscription->get_id(),
        'user_id'         => $subscription->get_user_id(),
        'order_id'        => $last_order->get_id(),
        'amount'          => $last_order->get_total(),
        'currency'        => $last_order->get_currency(),
        'next_payment'    => $subscription->get_date( 'next_payment' ),
        'timestamp'       => time(),
    ];

    $secret    = get_option( 'my_webhook_secret', '' );
    $body_json = wp_json_encode( $payload );
    $signature = hash_hmac( 'sha256', $body_json, $secret );

    wp_remote_post( $webhook_url, [
        'method'  => 'POST',
        'timeout' => 15,
        'headers' => [
            'Content-Type'       => 'application/json',
            'X-Webhook-Signature' => $signature,
        ],
        'body' => $body_json,
    ]);
}

ステップ4:無料トライアルを実装する

商品に無料トライアル期間を設定し、トライアル終了後に自動課金します。

<?php
/**
 * 無料トライアルの設定と管理
 */

// 商品保存時にトライアル設定を保存
add_action( 'woocommerce_process_product_meta', 'save_trial_settings' );

function save_trial_settings( int $post_id ): void {
    $trial_length = isset( $_POST['_subscription_trial_length'] )
        ? absint( $_POST['_subscription_trial_length'] )
        : 0;
    $trial_period = isset( $_POST['_subscription_trial_period'] )
        ? sanitize_key( $_POST['_subscription_trial_period'] )
        : 'day';

    update_post_meta( $post_id, '_subscription_trial_length', $trial_length );
    update_post_meta( $post_id, '_subscription_trial_period', $trial_period );
}

// トライアル終了前の通知
add_action( 'woocommerce_scheduled_subscription_trial_end', 'notify_trial_ending', 10, 1 );

function notify_trial_ending( int $subscription_id ): void {
    $subscription = wcs_get_subscription( $subscription_id );
    if ( ! $subscription ) {
        return;
    }

    wp_mail(
        $subscription->get_billing_email(),
        '無料トライアルが間もなく終了します',
        "無料トライアル期間が3日後に終了します。\n引き続きサービスをご利用の場合、自動的に課金が開始されます。"
    );
}

ステップ5:按分請求(プロレーション)を計算する

プランアップグレード時に残り期間を按分計算してクレジットを付与します。

<?php
/**
 * 按分請求(プロレーション)の計算
 */
function calculate_proration( WC_Subscription $subscription, float $new_price ): float {
    $current_price  = (float) $subscription->get_total();
    $next_payment   = strtotime( $subscription->get_date( 'next_payment' ) );
    $last_payment   = strtotime( $subscription->get_date( 'last_order_date_created' ) );

    if ( ! $next_payment || ! $last_payment ) {
        return $new_price;
    }

    $total_seconds   = $next_payment - $last_payment;
    $elapsed_seconds = time() - $last_payment;
    $remaining_ratio = max( 0, ( $total_seconds - $elapsed_seconds ) / $total_seconds );

    // 既払い分のクレジット
    $credit       = $current_price * $remaining_ratio;
    $prorated_fee = max( 0, $new_price - $credit );

    return round( $prorated_fee, 2 );
}

注意事項

  • WooCommerce Subscriptions プラグインは有料です。テスト環境では WooCommerce Subscriptions のダミーデータを使って動作確認してください
  • 決済ゲートウェイ(Stripe等)が定期決済に対応しているか事前に確認が必要です
  • Webhook の署名検証は必ず実装し、不正なリクエストを排除してください
  • サブスクリプションのステータス変更フックは複数回発火する場合があるため、冪等性を保つ実装にしてください
  • 按分計算のロジックは課金周期(月・年)によって変わるため、十分にテストしてください

まとめ

WooCommerceサブスクリプションは、カスタム商品タイプの登録からステータス管理、Webhook連携、無料トライアル、按分請求まで幅広い機能を提供しています。フックを活用することで、ビジネスロジックに合わせた柔軟な拡張が可能です。本番環境に投入する前に、Stripeのテストモードなどで十分な動作確認を行いましょう。

お気軽にご相談ください

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