2026年6月1日
2026年6月1日
WooCommerceのカスタム決済ゲートウェイを実装する方法
はじめに
WooCommerceには標準でStripe・PayPalなどの決済手段が用意されていますが、日本国内の決済サービス(GMOペイメント・SBペイメント・PAY.JP等)や独自の決済フローを実装するには、カスタム決済ゲートウェイを作成する必要があります。本記事では WC_Payment_Gateway クラスを継承して完全な決済ゲートウェイを実装する方法を解説します。
症状・原因
- 国内の決済サービスに対応した WooCommerce ゲートウェイが存在しない
- 既存のプラグインでは社内システムとの連携ができない
- 決済フロー(リダイレクト型・API型・後払い型)をカスタマイズしたい
- Webhook / IPN コールバックの処理方法がわからない
- テスト環境と本番環境で異なる認証情報を管理したい
解決手順
ステップ1:WC_Payment_Gateway を継承したクラスの基本構造を作成する
<?php
// my-payment-gateway/class-wc-my-payment-gateway.php
if ( ! defined( 'ABSPATH' ) ) {
exit; // WordPress 外からの直接アクセスを防ぐ
}
/**
* カスタム決済ゲートウェイクラス
* WC_Payment_Gateway を継承して実装する
*/
class WC_My_Payment_Gateway extends WC_Payment_Gateway {
/**
* コンストラクタ:基本プロパティを設定する
*/
public function __construct() {
// ゲートウェイの識別子(一意の値を設定すること)
$this->id = 'my_payment_gateway';
// チェックアウトページに表示するアイコン
$this->icon = plugin_dir_url( __FILE__ ) . 'assets/logo.png';
// 決済フォームを直接表示するか(false = 外部にリダイレクト)
$this->has_fields = true;
// 管理画面に表示する名前と説明
$this->method_title = __( 'カスタム決済', 'my-payment-gateway' );
$this->method_description = __( '独自の決済ゲートウェイを使用します。', 'my-payment-gateway' );
// 対応する機能(refunds を追加すると返金処理が可能)
$this->supports = [
'products',
'refunds',
];
// 管理画面の設定フィールドを初期化する
$this->init_form_fields();
// 保存された設定を読み込む
$this->init_settings();
// 設定値をプロパティにセットする
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
$this->test_mode = 'yes' === $this->get_option( 'test_mode' );
$this->api_key = $this->test_mode
? $this->get_option( 'test_api_key' )
: $this->get_option( 'live_api_key' );
// 設定保存のフックを登録する
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
[ $this, 'process_admin_options' ]
);
}
}
ステップ2:init_form_fields() でゲートウェイ設定フィールドを定義する
<?php
// WC_My_Payment_Gateway クラスのメソッドとして追加する
/**
* 管理画面の設定フィールドを定義する
* WooCommerce → 設定 → 支払い → カスタム決済 で表示される
*/
public function init_form_fields(): void {
$this->form_fields = [
// 決済手段の有効/無効
'enabled' => [
'title' => __( '有効化', 'my-payment-gateway' ),
'type' => 'checkbox',
'label' => __( 'この決済手段を有効化する', 'my-payment-gateway' ),
'default' => 'no',
],
// チェックアウトページに表示するタイトル
'title' => [
'title' => __( '表示名', 'my-payment-gateway' ),
'type' => 'text',
'description' => __( 'チェックアウトページに表示される決済方法の名前。', 'my-payment-gateway' ),
'default' => __( 'クレジットカード決済', 'my-payment-gateway' ),
'desc_tip' => true,
],
// 顧客向けの説明文
'description' => [
'title' => __( '説明', 'my-payment-gateway' ),
'type' => 'textarea',
'default' => __( 'クレジットカードで安全にお支払いいただけます。', 'my-payment-gateway' ),
],
// テストモード切り替え
'test_mode' => [
'title' => __( 'テストモード', 'my-payment-gateway' ),
'type' => 'checkbox',
'label' => __( 'テスト環境を使用する', 'my-payment-gateway' ),
'default' => 'yes',
],
// 本番用 API キー
'live_api_key' => [
'title' => __( '本番 API キー', 'my-payment-gateway' ),
'type' => 'password',
'description' => __( '決済サービスの本番用APIキーを入力してください。', 'my-payment-gateway' ),
'default' => '',
],
// テスト用 API キー
'test_api_key' => [
'title' => __( 'テスト API キー', 'my-payment-gateway' ),
'type' => 'password',
'description' => __( '決済サービスのテスト用APIキーを入力してください。', 'my-payment-gateway' ),
'default' => '',
],
// Webhook シークレット
'webhook_secret' => [
'title' => __( 'Webhook シークレット', 'my-payment-gateway' ),
'type' => 'password',
'description' => __( 'IPN/Webhook の署名検証に使用するシークレット。', 'my-payment-gateway' ),
'default' => '',
],
];
}
ステップ3:process_payment() で支払い処理を実装する
<?php
// WC_My_Payment_Gateway クラスのメソッドとして追加する
/**
* 支払い処理
* チェックアウトの「注文する」ボタン押下時に呼び出される
*
* @param int $order_id 注文ID
* @return array 処理結果(result と redirect を含む)
*/
public function process_payment( $order_id ): array {
$order = wc_get_order( $order_id );
if ( ! $order ) {
wc_add_notice( __( '注文が見つかりません。', 'my-payment-gateway' ), 'error' );
return [ 'result' => 'fail' ];
}
try {
// 決済 API を呼び出す
$response = $this->call_payment_api( [
'amount' => (int) ( $order->get_total() * 100 ), // 金額(銭単位)
'currency' => strtolower( get_woocommerce_currency() ),
'order_id' => $order_id,
'customer' => [
'name' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
'email' => $order->get_billing_email(),
],
'return_url' => $this->get_return_url( $order ),
'cancel_url' => wc_get_checkout_url(),
'webhook_url' => WC()->api_request_url( 'my_payment_gateway' ),
] );
if ( isset( $response['redirect_url'] ) ) {
// リダイレクト型:決済ページへ転送する
return [
'result' => 'success',
'redirect' => $response['redirect_url'],
];
}
if ( isset( $response['transaction_id'] ) ) {
// API 型:即時決済が完了した場合
$order->payment_complete( $response['transaction_id'] );
$order->add_order_note(
sprintf(
__( '決済完了。トランザクションID: %s', 'my-payment-gateway' ),
$response['transaction_id']
)
);
// カートをクリアする
WC()->cart->empty_cart();
return [
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
];
}
throw new \Exception( __( '決済サービスからの応答が不正です。', 'my-payment-gateway' ) );
} catch ( \Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
$order->update_status(
'failed',
sprintf( __( '決済エラー: %s', 'my-payment-gateway' ), $e->getMessage() )
);
return [ 'result' => 'fail' ];
}
}
/**
* 決済 API を呼び出す内部メソッド
*/
private function call_payment_api( array $params ): array {
$api_endpoint = $this->test_mode
? 'https://api-sandbox.payment-service.example.com/v1/charges'
: 'https://api.payment-service.example.com/v1/charges';
$response = wp_remote_post( $api_endpoint, [
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . $this->api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode( $params ),
] );
if ( is_wp_error( $response ) ) {
throw new \Exception( $response->get_error_message() );
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( wp_remote_retrieve_response_code( $response ) >= 400 ) {
throw new \Exception( $body['error']['message'] ?? __( 'APIエラー', 'my-payment-gateway' ) );
}
return $body;
}
ステップ4:payment_complete() と注文ステータス更新を処理する
<?php
// WC_My_Payment_Gateway クラスのメソッドとして追加する
/**
* 決済完了後の処理
* リダイレクト型の場合、決済サービスからのリターンURLで呼び出される
*
* @param WC_Order $order 注文オブジェクト
* @param string $transaction_id トランザクションID
*/
public function complete_order_payment( WC_Order $order, string $transaction_id ): void {
// 二重処理を防ぐ(既に処理済みの場合はスキップ)
if ( $order->is_paid() ) {
return;
}
// 決済完了として注文を処理する
// → 注文ステータスを「processing」に変更
// → 在庫の減算
// → 顧客への確認メール送信
$order->payment_complete( $transaction_id );
// トランザクションIDをメモに記録する
$order->add_order_note(
sprintf(
__( '決済が完了しました。トランザクションID: %s', 'my-payment-gateway' ),
esc_html( $transaction_id )
)
);
// カスタムメタデータを保存する
$order->update_meta_data( '_my_gateway_transaction_id', $transaction_id );
$order->update_meta_data( '_my_gateway_test_mode', $this->test_mode ? 'yes' : 'no' );
$order->save();
}
/**
* 返金処理の実装
*
* @param int $order_id 注文ID
* @param float $amount 返金金額(null の場合は全額)
* @param string $reason 返金理由
* @return bool|WP_Error
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) {
$order = wc_get_order( $order_id );
$transaction_id = $order->get_meta( '_my_gateway_transaction_id' );
if ( ! $transaction_id ) {
return new WP_Error( 'refund_error', __( 'トランザクションIDが見つかりません。', 'my-payment-gateway' ) );
}
try {
$response = $this->call_payment_api( [
'action' => 'refund',
'transaction_id' => $transaction_id,
'amount' => (int) ( $amount * 100 ),
'reason' => $reason,
] );
$order->add_order_note(
sprintf(
__( '返金完了: ¥%s(理由: %s)', 'my-payment-gateway' ),
number_format( $amount ),
$reason
)
);
return true;
} catch ( \Exception $e ) {
return new WP_Error( 'refund_error', $e->getMessage() );
}
}
ステップ5:woocommerce_payment_gateways フィルターで登録し Webhook を処理する
<?php
// プラグインのメインファイル(my-payment-gateway.php)に追記する
/**
* WooCommerce にゲートウェイを登録する
*/
add_filter( 'woocommerce_payment_gateways', 'register_my_payment_gateway' );
function register_my_payment_gateway( array $gateways ): array {
$gateways[] = 'WC_My_Payment_Gateway';
return $gateways;
}
/**
* プラグイン有効化時にクラスを読み込む
*/
add_action( 'plugins_loaded', 'init_my_payment_gateway_class' );
function init_my_payment_gateway_class(): void {
if ( ! class_exists( 'WC_Payment_Gateway' ) ) {
return; // WooCommerce が有効でない場合はスキップ
}
require_once plugin_dir_path( __FILE__ ) . 'class-wc-my-payment-gateway.php';
}
/**
* Webhook / IPN コールバックを処理する
* URL: https://example.com/?wc-api=my_payment_gateway
*/
add_action( 'woocommerce_api_my_payment_gateway', 'handle_my_payment_webhook' );
function handle_my_payment_webhook(): void {
$gateway = new WC_My_Payment_Gateway();
// リクエストボディを取得する
$payload = file_get_contents( 'php://input' );
$data = json_decode( $payload, true );
// 署名を検証する(改ざん防止)
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected_sig = hash_hmac( 'sha256', $payload, $gateway->get_option( 'webhook_secret' ) );
if ( ! hash_equals( $expected_sig, $signature ) ) {
http_response_code( 401 );
exit( 'Invalid signature' );
}
// イベントタイプに応じて処理する
switch ( $data['event'] ?? '' ) {
case 'payment.completed':
$order_id = (int) ( $data['metadata']['order_id'] ?? 0 );
$transaction_id = $data['transaction_id'] ?? '';
$order = wc_get_order( $order_id );
if ( $order ) {
$gateway->complete_order_payment( $order, $transaction_id );
}
break;
case 'payment.failed':
$order_id = (int) ( $data['metadata']['order_id'] ?? 0 );
$order = wc_get_order( $order_id );
if ( $order ) {
$order->update_status(
'failed',
__( 'Webhook: 決済が失敗しました。', 'my-payment-gateway' )
);
}
break;
case 'refund.completed':
// 返金完了の処理
break;
}
http_response_code( 200 );
echo 'OK';
exit;
}
注意事項
- PCI DSS 準拠: カード番号を自サーバーに送信・保存してはいけません。トークナイゼーションを使用し、決済サービス側でカード情報を処理してください。
- Webhook の署名検証: 署名検証なしで Webhook を処理すると、偽の決済完了通知を送り込む攻撃が可能になります。必ず
hash_equalsを使って検証してください。 - 冪等性: Webhook が複数回送信された場合でも、注文の二重処理が起きないよう
$order->is_paid()などで処理済みチェックを入れてください。
まとめ
WooCommerceのカスタム決済ゲートウェイは「クラス定義→設定フィールド→支払い処理→注文ステータス更新→Webhook処理」の5ステップで実装できます。セキュリティ(署名検証・冪等性)と WooCommerce のライフサイクルに従った実装が成功の鍵です。関連記事:WooCommerceにカスタム商品タイプを作成する方法