Skip to content

有料記事決済 シナリオテスト手順

有料記事の単号購入フローを手動で確認するための手順です。対象は Stripe Checkout、Webhook、購入履歴、閲覧制御、購入 CTA です。

対象範囲

  • 未購入ユーザーにはリードと購入 CTA のみ表示される
  • ログイン済み・メール認証済み・現行規約同意済みユーザーだけ購入開始できる
  • Stripe Checkout で決済成功後、Webhook により db_paid_article_purchase へ購入履歴が作成される
  • 購入履歴が新規作成されたとき、管理者(admin_email)へ決済控えメールが送信される(#2703)
  • 購入済みユーザーは有料記事の全文を閲覧できる
  • キャンセル、未ログイン、未認証、規約未同意、価格未設定などの分岐が想定通りになる

前提

  • Stripe はテストモードのキーを使う。本番カードや live キーでは実施しない。STRIPE_SECRET_KEY は Restricted API key(rk_test_)を推奨し、従来の Secret key(sk_test_)も利用できる。
  • wp-config.php に次の定数が設定されている。
php
define( 'STRIPE_SECRET_KEY', 'rk_test_...' );
define( 'STRIPE_PUBLISHABLE_KEY', 'pk_test_...' );
define( 'STRIPE_WEBHOOK_SECRET', 'whsec_...' );
  • paid_article の公開済み記事が 1 件以上ある。
  • 会員ユーザーでログインできる。
  • テストには専用のテスト会員と専用のテスト有料記事を使う。実ユーザー・実購入済み記事の組み合わせでは実施しない。
  • DB を直接更新する場合は、事前にローカル環境またはステージング環境で行う。本番 DB では事前バックアップを取る。
  • 管理者メール通知(S14 / S15)を確認する場合は、WordPress の「設定 → 一般」の管理者メールアドレス(admin_email)で受信できること。ローカルでは MailHog などのメール補足環境、または wp_mail の送信ログで確認する。

事前準備

1. Stripe Webhook を受けられる状態にする

ローカルで確認する場合は Stripe CLI で転送する。

bash
stripe listen --forward-to https://example.test/wp-json/commerce/v1/stripe-webhook

表示された whsec_...wp-config.phpSTRIPE_WEBHOOK_SECRET に設定する。

ステージングで確認する場合は Stripe Dashboard の Webhook endpoint に次を登録する。

text
https://example.com/wp-json/commerce/v1/stripe-webhook

イベントは最低限 checkout.session.completed を送る。

2. 単号購入価格を設定する

初期 seed の paid_article_single は価格 0 円なので、そのままだと Checkout は開始されない。テスト用価格を入れる。

WP-CLI が使える場合:

bash
wp db prefix
wp db query "UPDATE <prefix>db_member_plan_master SET price = 500, is_active = 1 WHERE plan_key = 'paid_article_single';"
wp db query "SELECT plan_key, price, is_active FROM <prefix>db_member_plan_master WHERE plan_key = 'paid_article_single';"

<prefix>wp db prefix の出力に置き換える。例: wp_db_member_plan_master

3. テスト会員を購入可能状態にする

UI で会員登録、メール認証、規約同意を行うのが本番に近い。準備を短縮する場合は WP-CLI でユーザー meta を更新する。

bash
wp user meta update <user_id> member_email_verified 1
wp user meta update <user_id> member_terms_accepted_version 2026-07-19
wp user meta update <user_id> member_terms_accepted_at "2026-07-16 00:00:00"

現行規約バージョンは管理画面「法務文書」の利用規約現行版(または DB 空時の MemberTermsConsentConstants::CURRENT_VERSION)と一致させる。確認例: wp db query "SELECT version FROM <prefix>db_legal_document_version WHERE document_type='terms' AND is_current=1;"

プライバシーポリシー版、同意元 IP / User-Agent、文書 URL / hash は監査証跡用であり、購入可否判定には使わない。短縮準備では省略できるが、本番相当の確認では UI から登録または再同意して保存させる。

4. 購入履歴を初期化する

同じユーザー・同じ記事で再テストする場合は、購入履歴を消して未購入状態に戻す。専用テスト会員・専用テスト記事の組み合わせであることを確認してから実行する。

bash
wp db query "DELETE FROM <prefix>db_paid_article_purchase WHERE user_id = <user_id> AND post_id = <post_id>;"

手動シナリオ

S1. 未ログインユーザーはリードとログイン CTA が表示される

  1. ブラウザをシークレットウィンドウで開く。
  2. 公開済み有料記事へアクセスする。
  3. intro セクションだけが表示され、全文は表示されないことを確認する。
  4. ログインボタンが表示されることを確認する。
  5. ログインボタン押下後、ログイン画面に遷移することを確認する。
  6. テスト会員でログインする。
  7. ログイン後の自動遷移先を確認する。
    • Stripe Checkout へ進んだ場合: 未ログイン時に生成された購入開始 URL から、ログイン後も決済開始できることを確認する。
    • 対象記事へ戻った場合: 記事上の購入 CTA から Stripe Checkout へ進めることを確認する。
  8. このシナリオでは支払いを完了せず、Checkout 画面から戻る。

期待結果:

  • 有料本文は見えない。
  • URL に購入開始用の admin-post.php?action=paid_article_checkout が直接露出していても、未ログインならログインへ誘導される。
  • 未ログイン入口からログインした後の実際のリダイレクト先で、購入導線が失われない。

S2. ログイン済み未購入ユーザーは購入 CTA が表示される

  1. テスト会員でログインする。
  2. 対象の有料記事へアクセスする。
  3. intro セクションと購入 CTA が表示されることを確認する。
  4. 価格が事前準備で設定した金額になっていることを確認する。

期待結果:

  • 全文は見えない。
  • 購入ボタンが表示される。

S3. メール未認証ユーザーは購入開始できない

  1. テスト会員の member_email_verified0 にする。
  2. 有料記事の購入ボタンを押す。
  3. メール認証案内ページへ遷移することを確認する。
  4. member_email_verified1 に戻す。

期待結果:

  • Stripe Checkout へは遷移しない。
  • 購入履歴は作成されない。

S4. 現行規約未同意ユーザーは購入開始できない

  1. テスト会員の member_terms_accepted_version を空、または古い値にする。
  2. 有料記事の購入ボタンを押す。
  3. 規約再同意ページへ遷移することを確認する。
  4. 現行規約に同意し直す、または meta を現行バージョンに戻す。

期待結果:

  • Stripe Checkout へは遷移しない。
  • 購入履歴は作成されない。

S5. 正常購入できる

  1. テスト会員をメール認証済み、現行規約同意済み、未購入状態にする。
  2. 有料記事の購入ボタンを押す。
  3. Stripe Checkout へ遷移することを確認する。
  4. テストカードで支払う(Stripe 公式の日本発行カード)。
    • 日本 Visa: 4000 0039 2000 0003(自動化 S5 の既定)
    • 日本 JCB: 3530 1113 3330 0000(手動確認の代替)
    • 有効期限: 任意の未来日
    • CVC: 任意の 3 桁
  5. 決済後、有料記事へ戻ることを確認する。
  6. Stripe CLI または Stripe Dashboard で checkout.session.completed が送信成功になっていることを確認する。
  7. Stripe Dashboard または DB の external_transaction_id から Checkout Session ID を控える。
  8. 購入履歴を確認する。
bash
wp db query "SELECT user_id, post_id, price, external_provider, external_transaction_id, purchased_at FROM <prefix>db_paid_article_purchase WHERE user_id = <user_id> AND post_id = <post_id>;"

期待結果:

  • external_providerstripe
  • external_transaction_id は Checkout Session ID。
  • price は Stripe から返った amount_total
  • Webhook 反映後、記事全文が閲覧できる。

補足:

  • 決済直後に「反映待ち」表示になる場合は、Webhook 到着前の可能性がある。数秒待って記事を再読み込みする。

S6. Webhook 再送で購入履歴が二重作成されない

  1. S5 で作成された checkout.session.completed イベントを Stripe Dashboard から再送する。
  2. 同じ user_id / post_id の購入履歴件数を確認する。
bash
wp db query "SELECT COUNT(*) AS purchase_count FROM <prefix>db_paid_article_purchase WHERE user_id = <user_id> AND post_id = <post_id>;"

期待結果:

  • purchase_count1 のまま。
  • 全文閲覧権限は維持される。
  • エラーログに未処理の例外が出ない。

S7. 購入済みユーザーは全文閲覧できる

  1. S5 の購入履歴がある状態で対象記事へアクセスする。
  2. intro 以外の有料本文も表示されることを確認する。
  3. 購入 CTA が表示されないことを確認する。

期待結果:

  • db_paid_article_purchase に該当レコードがあるユーザーは全文閲覧できる。

S8. 購入済みユーザーが購入 URL を再度踏んだ場合

  1. 購入済みユーザーで、購入開始 URL を開く。
  2. Stripe Checkout へ遷移せず、記事へ戻ることを確認する。

期待結果:

  • 二重購入に進まない。

S9. Checkout をキャンセルした場合

  1. 購入履歴を削除し、未購入状態に戻す。
  2. 購入ボタンを押して Stripe Checkout へ遷移する。
  3. Checkout 画面で戻る、またはキャンセル導線を選ぶ。
  4. 有料記事へ戻り、キャンセル通知が表示されることを確認する。
  5. DB に購入履歴が作成されていないことを確認する。

期待結果:

  • 全文は見えない。
  • db_paid_article_purchase にレコードは追加されない。

S10. 決済失敗カードの場合

  1. 購入履歴を削除し、未購入状態に戻す。
  2. 購入ボタンを押して Stripe Checkout へ遷移する。
  3. Stripe のテスト用失敗カードで支払いを試す。
    • 例: 4000 0000 0000 0002(汎用 decline。日本専用 decline カードは公式に無し)
  4. Checkout 画面上で失敗が表示されることを確認する。
  5. DB に購入履歴が作成されていないことを確認する。

期待結果:

  • サイト側に購入済みアクセスは付与されない。

S11. REST API では未購入ユーザーに本文が返らない

  1. 未購入ユーザー、または未ログイン状態で REST API から対象記事を取得する。
bash
curl -s https://example.test/wp-json/wp/v2/paid_article/<post_id>

期待結果:

  • content.renderedexcerpt.rendered が空、または保護状態になっている。

S12. 編集権限者は購入なしで全文閲覧できる

  1. 管理者または対象投稿を編集できるユーザーでログインする。
  2. 対象の有料記事へアクセスする。

期待結果:

  • 購入履歴がなくても全文閲覧できる。

S13. 価格未設定時は購入 CTA が出ない、または決済不可になる

  1. paid_article_single の価格を 0 にする。
  2. 未購入のテスト会員で対象記事へアクセスする。
  3. 購入 CTA の表示状態を確認する。
  4. 購入 URL を直接開いた場合も Checkout へ遷移しないことを確認する。
  5. テスト後、価格を元に戻す。

期待結果:

  • 価格 0 では Checkout Session は作成されない。

S14. 正常購入時に管理者へ決済控えメールが届く

前提: S5(正常購入)を実施できる状態にする。admin_email が受信可能であること。

  1. 購入履歴を削除し、テスト会員を未購入状態に戻す。
  2. S5 と同じ手順で正常購入を完了する。
  3. Webhook(checkout.session.completed)が送信成功し、購入履歴が新規作成されたことを確認する。
  4. admin_email の受信トレイ(またはメール補足環境・送信ログ)を確認する。

期待結果:

  • 管理者宛に件名「【スロット攻略】有料記事の購入がありました」のメールが 1 通届く。
  • 本文に次が含まれる。
    • 購入日時
    • ユーザー ID・表示名・メールアドレス
    • 記事 ID・記事タイトル
    • 金額(円)
    • Stripe Checkout Session ID(cs_...
    • Payment Intent ID(Webhook に含まれる場合のみ)
  • 購入履歴(db_paid_article_purchase)が 1 件だけ作成されている。

補足:

  • メール送信は購入履歴の新規 INSERT が成功したときだけ行われる。送信に失敗しても Webhook 応答は成功(HTTP 200)で、権利付与(全文閲覧)は維持される。失敗時はサーバーの error_logPaidArticlePurchaseAdminNotifier のログが出る。

S15. Webhook 再送では管理者メールが重複送信されない

前提: S14 を実施済みで、購入履歴が 1 件ある状態。

  1. S14 で作成された checkout.session.completed イベントを Stripe Dashboard から再送する。
  2. admin_email の受信トレイ(またはメール補足環境・送信ログ)を確認する。
  3. 購入履歴件数を確認する。
bash
wp db query "SELECT COUNT(*) AS purchase_count FROM <prefix>db_paid_article_purchase WHERE user_id = <user_id> AND post_id = <post_id>;"

期待結果:

  • 再送分の管理者メールは届かない(メールは増えない)。
  • purchase_count1 のまま(uk_external_transaction_id により重複 INSERT は null となり、通知も行われない)。

手動以外の確認方法

1. PHPUnit の関連テストだけ実行する

決済フローの分岐、Webhook、アクセス判定、戻り通知、管理者メール通知はユニットテストで確認できる。

bash
composer test -- --filter 'PaidArticleCheckoutServiceTest|PaidArticleCheckoutControllerTest|StripeWebhookServiceTest|StripeWebhookControllerTest|PaidArticleAccessServiceTest|FrontendAccessHooksTest|PaidArticleCheckoutReturnHooksTest|RestAccessHooksTest|PaidArticlePurchaseAdminNotifierTest'

環境によってテストコマンド名が違う場合は PHPUnit実行ガイド を参照する。

管理者メール通知(#2703)の受け入れ条件と自動テスト対応

S14 / S15 の観点は、次の PHPUnit で自動確認済み。手動シナリオは実メール到達の最終確認に限定できる。

受け入れ条件確認する PHPUnit
新規 INSERT 成功時のみ管理者へ通知するStripeWebhookServiceTest::test_handle_webhook_inserts_purchase_and_notifies_admin_on_checkout_completed
Webhook 再送・重複 INSERT(null)では通知しないStripeWebhookServiceTest::test_handle_webhook_returns_success_without_notify_on_duplicate_insert
メタデータ不正時は通知しないStripeWebhookServiceTest::test_handle_webhook_returns_success_without_insert_when_metadata_invalid
通知が例外を投げても Webhook は成功(権利付与優先)StripeWebhookServiceTest::test_handle_webhook_returns_success_when_admin_notify_throws
本文に日時・ユーザー・記事・金額・Session ID(+ PI)が含まれるPaidArticlePurchaseAdminNotifierTest::test_notify_purchase_sends_mail_with_purchase_details
ユーザー・記事が取得できない場合は不明プレースホルダで送るPaidArticlePurchaseAdminNotifierTest::test_notify_purchase_uses_unknown_placeholders_when_user_and_post_missing
admin_email 空・不正時は送らず常時ログPaidArticlePurchaseAdminNotifierTest::test_notify_purchase_skips_when_admin_email_empty / PaidArticlePurchaseAdminNotifierTest::test_notify_purchase_skips_when_admin_email_invalid
wp_mail 失敗時は常時ログするPaidArticlePurchaseAdminNotifierTest::test_notify_purchase_logs_when_wp_mail_returns_false

実装参照: App\Commerce\Service\stripe_webhook_service\StripeWebhookService / App\Commerce\Service\paid_article_purchase_admin_notifier\PaidArticlePurchaseAdminNotifier

2. Stripe CLI で Webhook だけ確認する

Checkout UI を通らずに Webhook 経路だけ見る場合:

bash
stripe listen --forward-to https://example.test/wp-json/commerce/v1/stripe-webhook
stripe trigger checkout.session.completed

ただし、この方法で送られる fixture の metadata が実装の期待する user_id / post_id と合わない場合、購入履歴は作成されない。その場合は Dashboard から実際の Checkout Session イベントを再送するほうが確実。

3. WP-CLI で購入済み判定だけ確認する

Webhook を待たずに購入履歴を直接入れて、閲覧制御だけ確認できる。

bash
wp db query "INSERT INTO <prefix>db_paid_article_purchase (user_id, post_id, price, external_provider, external_transaction_id, purchased_at) VALUES (<user_id>, <post_id>, 500, 'manual_test', 'manual_test_<post_id>', NOW());"

確認後は該当レコードを削除する。

4. ブラウザ E2E テスト化する

サイト内表示制御(未ログイン / 未購入 CTA / 購入済み全文 / REST マスク)は、DB fixture による 通常 Playwright E2E で自動化済みです。

Stripe Checkout 外部画面まで含む通しの自動化方針は 有料記事 Stripe Checkout / Webhook E2E 方針 を正とします。

  • S5 / S9 / S10(正常購入・キャンセル・失敗カード)— Local 手動実行専用 Playwright(npm run test:e2e:paid-article-stripe)で自動化済み。手順の正本は 有料記事 Stripe Checkout / Webhook E2E 方針 の「実行手順」
  • S6 / S14 / S15(Webhook 再送・管理者メール実到達)— PHPUnit で論理を担保し、実操作は手動の任意確認に残す
  • Staging 通し — 手動シナリオを維持する
  • 通常の npm run test:e2e および CI Quality Gate には Stripe 通しを載せない

片付け

テスト後は必要に応じて以下を戻す。

bash
wp db query "DELETE FROM <prefix>db_paid_article_purchase WHERE external_provider = 'manual_test' AND user_id = <user_id> AND post_id = <post_id>;"
wp db query "DELETE FROM <prefix>db_paid_article_purchase WHERE external_provider = 'stripe' AND external_transaction_id = '<cs_test_session_id>' AND user_id = <user_id> AND post_id = <post_id>;"
wp db query "UPDATE <prefix>db_member_plan_master SET price = 0 WHERE plan_key = 'paid_article_single';"

<cs_test_session_id> は S5 で控えたテストモードの Checkout Session ID に置き換える。実購入履歴を誤って削除しないため、external_provider = 'stripe' だけを条件にした広範な削除は行わない。

価格をステージングで継続利用する場合は price = 0 に戻さず、運用で使うテスト価格を維持する。

参考