2026年5月31日
2026年5月31日
WordPressでオークションサイトを構築する方法|機能設計・決済・不正対策と法律の確認点
WordPressでオークションサイトを作るには、WooCommerceにオークション用の拡張機能を組み合わせる方法と、カスタム投稿タイプと入札用のテーブルで自作する方法があります。どちらを選ぶ場合も、先に「誰が出品するのか」を決め、入札の同時処理、終了時刻の扱い、落札後の決済、不正入札への対策、関係する法律の確認までを設計に含めることが大切です。この記事では、構築方法の選び方と、運用で問題になりやすい部分の作り方を順に解説します。
まずサイトの形を決める
オークションサイトは、出品者が誰かによって大きく2つに分かれます。
- 自社出品型: 運営者が自社の商品だけを出品し、利用者は入札するだけ。仕組みが単純で、出品物の管理も自社で完結する
- マーケットプレイス型: 一般の利用者や事業者が出品し、運営者は取引の場を提供する。出品審査、出品者への売上の支払い、トラブル対応など、運営側の仕組みが大きく増える
マーケットプレイス型は、機能面だけでなく法律上の扱いも変わることがあります。最初は自社出品型で始め、運用が安定してから出品者を広げる進め方のほうが、リスクを抑えやすくなります。
オークションに必要な機能
通常のネットショップと違い、オークションには次のような機能が必要です。どこまで実装するかを最初に決めておくと、拡張機能の比較や自作の範囲が明確になります。
- 開始価格と入札単位(例: 100円刻み)
- 最低落札価格(この金額に届かなければ落札とならない価格)
- 即決価格(この金額で入札するとその場で落札となる価格)
- 自動入札(上限額を預けておき、他の人に上回られたら自動で金額を上げる)
- 終了時刻の自動延長(終了直前に入札があったら時間を延ばし、駆け込み入札を防ぐ)
- 入札履歴の表示と、上回られたとき・落札したときのメール通知
構築方法1: WooCommerceとオークション拡張を使う
すでにWooCommerceでネットショップを運営している、または商品管理・注文管理・決済をまとめて任せたい場合は、WooCommerce向けのオークション拡張機能を使うのが現実的です。商品の種類として「オークション商品」を追加し、落札後はWooCommerceの注文と決済の流れに乗せる仕組みのものが一般的です。
オークション拡張は無料・有料の両方がありますが、機能の範囲、対応するWooCommerceのバージョン、サポートの状況は製品ごとに大きく異なり、変わることもあります(2026年10月現在)。選ぶときは次の点を確認してください。
- 上で挙げた機能(特に自動入札と終了時刻の延長)に対応しているか
- オークションの終了処理がWP-Cronだけに頼っていないか、頼る場合の推奨設定が説明されているか
- WooCommerceのHPOS(高性能な注文保存の仕組み)に対応しているか
- WordPress.orgやベンダーのページで、最終更新日が新しく、対応バージョンが現行のものか
導入はテスト環境で行い、テスト用のアカウントを複数作って、入札の競り合い、終了、落札者への請求、支払いまでの流れを一通り試してから本番に入れます。
構築方法2: カスタム投稿タイプと入札テーブルで自作する
独自のルールで運営したい場合は自作も選択肢です。出品物はカスタム投稿タイプ(例: auction)で管理し、開始価格や終了日時はカスタムフィールドに保存します。入札は件数が多くなり、金額での集計や並べ替えが必要になるため、投稿メタではなく専用のテーブル(例: wp_auction_bids)に保存するのが向いています。テーブルはプラグインの有効化時に dbDelta() で作成し、トランザクションが使えるInnoDBにしておきます。
同時の入札で金額が狂わないようにする
自作で最もつまずきやすいのが、同じ瞬間に複数の入札が届いたときの処理です。「現在価格を読む → 比べる → 保存する」を単純に行うと、2人が同じ現在価格を読んでどちらの入札も通り、同じ金額の入札が2件残るといった不整合が起きます。
次の例では、トランザクションの中で対象のオークションの行をロックし、同じオークションへの入札を1件ずつ処理しています。nonceの確認とログイン確認は、この関数を呼ぶ側(フォームの受け取り処理)で済ませておく前提です。
/**
* 入札を1件登録する。
* 呼び出し側で nonce の確認(check_admin_referer など)とログイン確認を済ませておくこと。
*
* @return true|WP_Error
*/
function myauction_place_bid( $auction_id, $user_id, $amount ) {
global $wpdb;
$auction_id = absint( $auction_id );
$user_id = absint( $user_id );
$amount = absint( $amount );
$table = $wpdb->prefix . 'auction_bids';
$step = 100; // 入札単位(円)
$wpdb->query( 'START TRANSACTION' );
// 同じオークションへの入札を1件ずつ処理するため、対象の投稿の行をロックする
$locked = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE ID = %d AND post_type = 'auction' AND post_status = 'publish' FOR UPDATE",
$auction_id
) );
if ( ! $locked ) {
$wpdb->query( 'ROLLBACK' );
return new WP_Error( 'not_found', 'オークションが見つかりません。' );
}
// 終了日時(UNIXタイムスタンプで保存しておく)
$end_at = (int) get_post_meta( $auction_id, '_auction_end_at', true );
if ( time() >= $end_at ) {
$wpdb->query( 'ROLLBACK' );
return new WP_Error( 'closed', 'このオークションは終了しています。' );
}
$current = (int) $wpdb->get_var( $wpdb->prepare(
"SELECT MAX(amount) FROM {$table} WHERE auction_id = %d",
$auction_id
) );
$start = max( 1, (int) get_post_meta( $auction_id, '_auction_start_price', true ) );
$minimum = $current > 0 ? $current + $step : $start;
if ( $amount < $minimum ) {
$wpdb->query( 'ROLLBACK' );
return new WP_Error( 'too_low', sprintf( '%d円以上で入札してください。', $minimum ) );
}
$inserted = $wpdb->insert(
$table,
array(
'auction_id' => $auction_id,
'user_id' => $user_id,
'amount' => $amount,
'created_at' => current_time( 'mysql', true ),
),
array( '%d', '%d', '%d', '%s' )
);
if ( false === $inserted ) {
$wpdb->query( 'ROLLBACK' );
return new WP_Error( 'db_error', '入札を登録できませんでした。' );
}
$wpdb->query( 'COMMIT' );
return true;
}
実際の運用では、ここに「出品者本人の入札を拒否する」「すでに最高額の入札者が自分の金額を上げる場合の扱い」「終了直前の入札で終了日時を延長する」といったルールを加えます。延長の処理も、このロックの内側で行うと、延長と入札の順序が入れ替わりません。確認方法は、テスト環境で2つのブラウザから同じ金額をほぼ同時に送信し、入札テーブルに1件だけ登録されるかを見ることです。
終了時刻とキャッシュの扱い
WP-Cronはサイトへのアクセスをきっかけに動くため、アクセスが少ない時間帯はオークションの終了処理(落札者の決定や通知)が遅れます。wp-config.php に define( 'DISABLE_WP_CRON', true ); を書き、サーバーのcronから1分ごとなどの間隔で wp-cron.php を実行する設定にすると、終了処理のタイミングが安定します。設定を変える前には wp-config.php のバックアップを取ってください。
また、ページキャッシュを使っていると、現在価格や残り時間が古いまま表示されます。オークションのページをキャッシュの対象外にするか、価格と残り時間だけをJavaScriptでREST APIから取得して表示する構成にします。時刻の判定はサーバー側の時刻で行い、利用者のパソコンの時計に頼らないようにします。
落札後の決済を設計する
オークションでは、落札が決まってから支払いが発生します。代表的な方法は2つです。
- 落札後に請求する: 落札者に支払い用のリンクを送り、期限内に支払ってもらう。仕組みは単純だが、支払わない落札者が出る
- 事前にカードを登録してもらう: 入札の条件として決済サービスでカード情報を登録してもらい、落札後に請求する。未払いは減るが、利用者の心理的なハードルは上がる
どちらの場合も、カード情報は自社のサーバーに保存せず、Stripeなどの決済サービスの仕組みで扱います。支払期限を過ぎた落札者への対応(次点の入札者への繰り上げ、アカウントの利用制限など)は、利用規約に明記しておきます。マーケットプレイス型で運営者が代金を受け取って出品者に支払う形にする場合は、決済サービスが用意する事業者向けの仕組みを使えるかも確認してください。
不正入札を防ぐ
オークションでは、価格をつり上げるための自作自演の入札(いわゆるサクラ入札)や、落札する気のない入札が問題になります。次の対策を組み合わせます。
- 本人確認: メールアドレスの確認に加え、入札前に電話番号や住所の登録を求める。高額商品は審査を挟む
- 新規アカウントの制限: 登録直後のアカウントは入札できる金額や件数に上限を設ける
- 複数アカウントの監視: 同じ出品物に対して、同じ端末や同じ配送先のアカウントが繰り返し入札していないかを確認する
- 入札の取り消しルール: 入札の取り消しを原則認めない、または理由と回数を記録する
- ログと連続送信の制限: 入札のIPアドレスと日時を記録し、短時間の大量送信は制限する。ログイン画面には二段階認証を入れる
入札ログには個人情報が含まれるため、保存期間と閲覧できる担当者を決め、プライバシーポリシーに利用目的を書いておきます。
法律の確認点
オークションサイトの運営には、扱う商品と運営の形によって複数の法律が関わります。代表的なものは次のとおりです。
- 古物営業法: 中古品を仕入れて販売する場合は、古物商の許可が関係します。また、インターネット上で利用者同士の売買の場を提供する事業は「古物競りあっせん業」にあたり、公安委員会への届出などが必要になる場合があります
- 特定商取引法: インターネットで販売する場合は通信販売にあたり、事業者名、連絡先、販売価格、支払方法、返品の条件などを表示する義務があります
- 出品できない物の扱い: 法律で売買が制限されている物や、権利を侵害する物が出品されないよう、禁止事項と審査の流れを決めておきます
このほか、運営者が代金を一時的に預かる仕組みや、入札そのものに手数料を課す仕組みなどは、別の規制や表示のルールが関わることがあります。ここで挙げたのは一般的な確認点にすぎず、自社の運営形態で何が必要かは事前に弁護士・行政書士などの専門家や、所轄の警察署などの窓口に相談してください。
まとめ
- 最初に自社出品型かマーケットプレイス型かを決める。後者は機能も法律上の扱いも重くなる
- WooCommerceを使っているならオークション拡張を検討し、自動入札・終了延長・WP-Cronの扱い・最終更新日を確認する
- 自作する場合は入札を専用テーブルに保存し、トランザクションとロックで同時入札を処理する。終了処理はサーバーのcronで動かし、価格表示はキャッシュの影響を避ける
- 決済は落札後請求か事前のカード登録かを選び、未払い時の扱いを利用規約に書く
- 本人確認や新規アカウントの制限で不正入札を防ぎ、古物営業法・特定商取引法などは専門家に確認する