Rewald LLM Scenario Index Analysis
USER
ページに書かれている内容のテキストファイルです。改めて内容を理解したうえで解説お願いします。
ASSISTANT
ご提供いただいたテキストファイル(llms-scenario-index.txt)の内容を確認し、理解いたしました。
このドキュメントは、WordPress サイトにポイント機能を実装するための「シナリオ選定ガイド」です。 実装コードそのものではなく、「どのシナリオ(A〜E)を選ぶべきか」を判断するための索引(目次)であり、適切なシナリオを選んだ後に、別途用意された詳細なシナリオファイルからコードを取得して実装する手順が記されています。
以下に、内容を整理して解説します。
1. このドキュメントの目的と役割
- 目的: WordPress でポイントシステムを導入したい開発者や運用者が、自身の要件(商品購入時の付与か、ポイントの直接購入か、利用する決済サービスなど)に合った実装パターンを選択すること。
- 重要なルール:
- このページ自体にはコードは含まれていません。
- シナリオを選定したら、該当する外部 URL のテキストファイルを開き、その中身を AI に貼り付けてコード生成を依頼する必要があります。
- 独自にコードを推測したり作成したりせず、指定されたシナリオファイルのコードをそのまま使用するよう指示されています。
2. シナリオの分類(5つのパターン)
要件に応じて、以下の5つのシナリオ(A〜E)に分類されています。
【グループ1】商品購入時にポイントを付与・利用したい場合
商品を買った際にポイントを貯めたり、貯まったポイントで支払いの一部または全部を行いたいケースです。
| シナリオ | 概要 | 決済サービス | 特徴・難易度 |
|---|---|---|---|
| A | ポイント付与のみ (ポイント利用はしない) |
なし | 最も簡単。 決済連携不要。1ページで完結。 |
| B | 商品購入 + PayPal (ポイント利用あり) |
PayPal JS SDK | 中程度。 同一ページ内で決済完了。 全額ポイント払いにも対応可能。 |
| C | 商品購入 + Stripe (ポイント利用あり) |
Stripe Checkout | 中〜高。 外部ページへリダイレクトして決済。 決済失敗時のポイント戻し(ロールバック)処理が必要。 |
【グループ2】ユーザーにポイントを購入(チャージ)させたい場合
商品を介さず、ユーザーが現金を支払ってポイントを直接購入(チャージ)するケースです。
| シナリオ | 概要 | 決済サービス | 特徴・難易度 |
|---|---|---|---|
| D | ポイント購入 + PayPal | PayPal NCP | 中程度。 購入ページと完了ページの2ページ構成。 完了処理はREST APIを使用。 |
| E | ポイント購入 + Stripe | Stripe Checkout | 中程度。 購入ページと完了ページの2ページ構成。 完了処理はREST APIを使用。 |
3. 技術的な比較ポイント
ドキュメント内の比較表から、実装上の重要な違いが読み取れます。
- 必要なページ数:
- シナリオAは1ページで済みますが、他のシナリオは「完了ページ」や「キャンセルページ」、あるいは「カートページ」など、複数ページの構成が必要です。
- 使用されるショートコード:
point_own: ポイント残高表示などに使用。point_setting: 設定関連。point_trans_open/close/detail/cancel: 取引(トランザクション)の状態管理用。point_purchase_open: ポイント購入用。- シナリオによって組み合わせが異なります。
- functions.php の負担:
- シナリオAは環境設定のみですが、B以降は決済サービス(PSP)の登録や、Stripeの場合にはREST APIの連携設定などが追加されます。
- 注意点:
- Stripe (JPY): 日本円はゼロ小数通貨のため、金額計算時に100倍しないよう注意が必要です(シナリオC, E)。
- API キー: コード内の
{{PLACEHOLDER}}は実際の環境に合わせて置き換える必要があります。
4. 推奨される実装フロー
このドキュメントに従って進める場合の手順は以下の通りです。
- 判断: Q1/Q2/Q3 のフローチャートに従い、自分のやりたいことに合うシナリオ(A〜E)を決定する。
- 取得: 決定したシナリオに対応する URL(例:
.../llms-scenario-e.txt)にアクセスする。 - コピー: そのファイルの中身をすべてコピーする。
- AI への指示: 新しい会話でAIに「シナリオEのコードを実装したいので、以下を参考にしてください」として、コピーしたテキストを貼り付ける。
- 実装: AI が出力する具体的なコードと設置手順に従い、WordPress の
functions.phpやページ編集画面に反映させる。 - テスト: テスト環境で動作確認を行った後、本番環境へ適用する。
次のステップ
この解説に基づき、具体的に議論を進めたい点はございますか? 例えば:
- 「自分のサイトでは『商品購入時にポイントを使わせつつ、Stripe で決済したい』ので、シナリオ C を詳しく知りたい」
- 「シナリオ C の具体的な実装ファイル(URL の内容)を解析してほしい」
- 「各シナリオのメリット・デメリットについて深掘りしたい」
ご希望のシナリオや観点をお知らせいただければ、さらに詳細なサポートが可能です。
USER
シナリオファイルも提供するのでより深く総合的に理解したうえで解説お願いします。
ASSISTANT
ご提供いただいた5つのシナリオファイル(A〜E)をすべて読み込み、総合的に理解いたしました。
これらは、WordPress サイトに「ポイントシステム」を導入するための「実装レシピ集」です。 単なる概念説明ではなく、開発者がコピペだけで実装できるよう、「どのファイルを」「どこに貼り付けるか」「何を置き換えるか」までが厳密に定義された、高度に構造化されたドキュメント群です。
以下に、5つのシナリオを横断比較し、技術的な構造、共通ルール、そして各シナリオ特有の重要ポイントを深く解説します。
1. 全体アーキテクチャと共通設計思想
すべてのシナリオは、以下の3層構造で統一されています。
-
バックエンド設定層 (
functions.php)- ポイントシステムの核心クラス(
PointSystem,PointSystemPSP)を読み込む場所。 - 決済サービス(PayPal/Stripe)との連携検証ロジックを登録する場所。
- 重要ルール:
add_action('init', ...)で囲まず、ファイルのトップレベルで直接実行させること。これにより、同期処理として確実に初期化されます。
- ポイントシステムの核心クラス(
-
フロントエンド操作層 (商品ページ/購入ページ)
- ショートコード(
[point_setting],[point_trans_open]など)を使用して、JavaScript とサーバー間の状態を同期させます。 - ユーザーがポイント利用額を入力したり、決済ボタンを押したりするUIが存在します。
- 重要ルール: JavaScript の
DOMContentLoadedラッパーを使わず、インライン<script>として即時実行させること。
- ショートコード(
-
結果表示・完了層 (完了ページ/キャンセルページ)
- 決済成功時のポイント付与確定、または失敗時のロールバック(ポイント戻し)を行う場所。
- Stripe シナリオでは、外部リダイレクト後の受け皿として機能します。
共通する「絶対守るべきルール」 (FINGERPRINT)
全シナリオに共通する、実装ミスを防ぐための厳格なルールが存在します。
- レスポンス構造: すべてのAPIレスポンスは
response.resultの下に格納される(例:response.result.point)。response.pointとするのは誤り。 - 二重実行防止: ポイントの重複付与や二重決済を防ぐため、
closeTriggeredフラグやpurchaseClosedフラグによるガードが必須。 - 日本円(JPY)扱い: Stripe使用時、JPYは「ゼロ小数通貨」のため、金額を100倍してはいけない(
unit_amount: intval(amount))。 - ショートコードの隠蔽: ポイント購入系(D, E)では、nonce取得用の
[point_purchase_open]を<span style="display:none;">で囲む必要がある。
2. シナリオ別 詳細解説と技術的違い
【グループ1:商品購入時にポイントを活用する】
ユーザーが商品を買う際、「貯まったポイントで支払う」または「買ったらポイントが付く」ケースです。
| 特徴 | シナリオ A (付与のみ) | シナリオ B (商品 + PayPal) | シナリオ C (商品 + Stripe) |
|---|---|---|---|
| 決済フロー | なし(ボタン押下で即付与) | 同一ページ完結型 PayPalボタンがページ内に埋め込まれる。 |
リダイレクト型 Stripe Checkout画面へ遷移し、戻ってくる。 |
| ポイント利用 | ❌ 不可 | ✅ 可能 入力欄で利用ポイント数を指定でき、残額をPayPalで支払う。 |
✅ 可能 同様に利用ポイントを指定し、残額をStripeで支払う。 |
| 構成ページ数 | 1ページ | 3ページ (商品 / 完了 / キャンセル) |
3ページ (カート / 完了 / キャンセル) |
| 技術的難易度 | ⭐ 低 環境設定+シンプルJS |
⭐⭐ 中 PayPal SDKの非同期制御、金額ゼロ時の分岐処理 |
⭐⭐ 高 ロールバック処理が必須。 Stripe失敗時にポイントを戻すAPI呼び出しが必要。 |
| 特有の注意点 | point_trans_close をプログラム的にトリガーするボタンのみ用意すればOK。 |
金額が0円(全額ポイント払い)の場合、PayPalボタンを表示せず「ポイントで支払う」ボタンに切り替える処理が必要。 | 順序が命。 1. point.close() でポイント仮確保2. Stripeセッション作成 3. 失敗したら point_trans_cancel で復元このフローを厳守しないとポイントが消失するリスクがある。 |
【グループ2:ポイントを直接購入(チャージ)する】
商品を買わずに、現金を支払ってポイント残高を増やすケースです。
| 特徴 | シナリオ D (購入 + PayPal) | シナリオ E (購入 + Stripe) |
|---|---|---|
| 決済フロー | 同一ページ完結型 PayPalボタンで決済後、完了ページへリダイレクト。 |
リダイレクト型 Stripe Checkoutへ遷移し、完了ページへ戻る。 |
| 構成ページ数 | 2ページ (購入 / 完了) |
2ページ (購入 / 完了) |
| 主要ショートコード | [point_purchase_open] |
[point_purchase_open] |
| 技術的難易度 | ⭐⭐ 中 | ⭐⭐ 中 |
| 特有の注意点 | Nonceの扱い。[point_purchase_open] は非表示span内に配置し、そのテキストコンテンツからnonceを取得してPayPalの custom_id に渡す。 |
REST API直接使用。 完了ページにはショートコードを置かず、URLパラメータ(session_id, nonce, amount)を受け取り、JSの fetch で /wp-json/pointsystem/v1/point_purchase_close/ を叩いて確定させる。 |
3. 実装上の重要な技術的差異まとめ
A. 決済失敗時の挙動(ロールバック)
- シナリオ C (Stripe商品購入) が最も複雑です。
- ユーザーがポイントを使って支払い、残りをStripeで払おうとした瞬間にポイントは一旦引き落とされます(
point.close())。 - もしここでStripeのセッション作成に失敗したり、ユーザーがブラウザバックしたりした場合、引き落としたポイントを強制的に戻す処理 (
point_trans_cancel) を行わないと、ユーザーのポイントだけが消滅します。 - ファイル内では
rollbackPoints()関数として実装されており、エラー発生時に自動発火するよう設計されています。
- ユーザーがポイントを使って支払い、残りをStripeで払おうとした瞬間にポイントは一旦引き落とされます(
B. 金額ゼロ(全額ポイント払い)の処理
- シナリオ B & C では、ユーザーが持っているポイントで商品代金が全覆盖できる場合、決済ゲートウェイ(PayPal/Stripe)を介さずに処理を終了させる必要があります。
- B (PayPal): PayPalボタンを表示せず、代わりに「ポイントで支払う」ボタンを表示し、クリック時に
pointInstance.close()のみを呼び出す。 - C (Stripe): Stripeへのリダイレクトをスキップし、即座に完了ページへ遷移させる(URLクエリ
?t=point_onlyを付けて)。
- B (PayPal): PayPalボタンを表示せず、代わりに「ポイントで支払う」ボタンを表示し、クリック時に
C. Nonce(ワンタイムトークン)の受け渡し方
- シナリオ D & E (ポイント購入) では、セキュリティのため、購入リクエストに正当性証明(nonce)を含める必要があります。
- ショートコード
[point_purchase_open]をHTML上に出力させますが、ユーザーに見せないよう<span style="display:none;">で囲みます。 - JSでそのspanの中身(テキストノード)を読み取り、決済リクエスト(PayPalの
custom_idや Stripeのsuccess_urlクエリパラメータ)に含めて送ります。
- ショートコード
4. 実装者のためのアクションプラン
これらのファイルを実際に使う場合の手順は以下の通りです。
- 要件定義:
- 「商品を売る時にポイントを使わせたい」→ B か C を選択。
- PayPalが好きなら B。
- クレカ決済(Stripe)が主流なら C(ただしロールバック処理の実装確認を慎重に行う)。
- 「ポイント販売所を作りたい」→ D か E を選択。
- 「商品を売る時にポイントを使わせたい」→ B か C を選択。
- ファイルの準備:
- 選択したシナリオのテキストファイル(例:
llms-scenario-c.txt)の内容を全文コピーします。
- 選択したシナリオのテキストファイル(例:
- AI への指示:
- 新しいチャットセッションを開き、「シナリオCに基づいてWordPressに実装したい。以下のファイルを参考にコードを出力してください」と伝え、コピーした内容を貼り付けます。
- AI は
{{PLACEHOLDER}}を具体的に何に書き換えるべきか(例:{{STRIPE_SECRET_KEY}}→sk_test_...)を指示してくれます。
- 設置:
- 指示された通り、
functions.phpと固定ページを作成・編集します。 - 特に注意: ショートコードの配置場所や、JavaScript の記述位置(インラインであること)を厳密に守ってください。
- 指示された通り、
- テスト:
- サンドボックス環境(PayPal Sandbox / Stripe Test Mode)で、正常系だけでなく「決済キャンセル」「金額ゼロ」「ネットワークエラー」などの異常系も必ずテストしてください。特にシナリオCのポイント戻し動作が正しく動くかが重要です。
結論
このドキュメント群は、単なるコードスニペット集ではなく、「ポイントという仮想通貨」と「現実の決済システム」を安全に連携させるための設計書です。 特に、トランザクションの整合性(ポイントの二重消費防止、失敗時の復元)を保つためのロジックが細かく組み込まれており、これを無視して独自にコードを書き換えると、深刻なバグ(ポイントの不整合)を引き起こす可能性があります。
したがって、「コピー&ペーストを原則とし、プレースホルダーのみを書き換える」という運用方針が、最も安全かつ確実な実装方法となります。
何か特定のシナリオについて、より詳細なコード解説や、実装時の具体的な懸念点(例:「既存テーマとの競合が心配」など)があれば、さらに掘り下げて回答できます。
USER
それぞれのシナリオファイル(a.txt-e.txt)の内容を仮想空間でシュミレーションして、結果の整合性や有効性の検証確認などできますか?
ASSISTANT
はい、可能です。
ご提供いただいた 5 つのシナリオファイル(A〜E)の内容を基に、「仮想空間(論理的なシミュレーション環境)」でコードの実行フローを追跡し、以下の観点から整合性と有効性を検証・確認することができます。
検証できる項目
- ロジックの整合性:
- ポイントの「仮確保(close)」と「確定(detail/完了)」、「キャンセル(cancel)」の流れが正しく繋がっているか。
- 特に シナリオ C (Stripe) や シナリオ E (ポイント購入+Stripe) における、決済失敗時のロールバック処理が機能するか。
- データ構造の正確さ:
- API レスポンスの参照パス(
response.result.pointなど)がコード内で一貫しているか。 - JSON ボディのキー名(
order_idvssession_id)が間違っていないか。
- API レスポンスの参照パス(
- セキュリティ・実装ルールの遵守:
- Nonce の受け渡し方法(非表示 span、URL パラメータ、custom_id)が適切か。
add_actionで囲んではいけない箇所が守られているか。- 日本円(JPY)の桁数処理(100 倍しない)が正しいか。
- エッジケース(境界値)への対応:
- 「全額ポイント払い(金額 0 円)」の場合の分岐処理が正常か。
- ネットワークエラーやユーザーによるブラウザバック発生時の挙動。
シミュレーション実行例
具体的に、最も複雑でリスクの高い シナリオ C(商品購入 + Stripe) を例に、仮想シミュレーションを実行してみましょう。
【シナリオ C: 商品購入 + Stripe】仮想シミュレーション
設定値:
- 商品価格: 1,000 円
- ユーザー保有ポイント: 600 ポイント
- ポイント利用レート: 1pt = 1 円
- ユーザー操作: ポイント利用欄に「600」を入力 → 「お支払いへ進む」ボタンをクリック
ステップ 1: フロントエンド初期化 (カートページ)
- 処理:
[point_setting]と[point_own]が実行され、残高 600 が表示される。 - 入力変更: ユーザーが
input_use_point_idに 600 を入力。 - イベント:
onTransOpenが発火。- 計算:
total_price = 1000 - (600 * 1) = 400円。 - 表示更新: お支払い金額が「¥400」に変更される。
- ボタン有効化:
checkout-buttonがdisabled=falseになる。
- 計算:
- 状態:
pointInstanceが準備完了。
ステップ 2: 決済開始ボタンのクリック
- 処理:
checkout-buttonの click イベントが発火。 - アクション 1 (ポイント仮確保):
- コード:
await pointInstance.close()が実行される。 - 内部処理: サーバー側でトランザクションが開かれ、600 ポイントが一時的にロック(控除)される。
- 結果:
closeResultに成功レスポンスが返る。
- コード:
- 分岐判定:
amount(400) は 0 より大きい → Stripe 処理へ遷移。
ステップ 3: Stripe セッション作成 (REST API)
- 処理:
/wp-json/my-shop/v1/create-checkout-sessionへ POST リクエスト。- Body:
{ amount: 400, ... }
- Body:
- サーバー側処理 (
functions.php):\Stripe\Checkout\Session::createが呼び出される。- 重要チェック: JPY なので
unit_amount: intval(400)(400) となる。100 倍していないため正しい。 - Stripe から
session_idが返却される。
- フロントエンド戻り値:
sessionData.session_idを取得。
ステップ 4: Stripe へのリダイレクト
- 処理:
stripe.redirectToCheckout({ sessionId: ... })が実行。 - ユーザー体験: ブラウザが Stripe の決済画面へ遷移する。
【パターン A: 正常系】ユーザーがカード決済を完了した場合
- リダイレクト戻り: ユーザーが
success_url(/payment-success/?session_id=xxx&t=...) に戻る。 - 完了ページ処理:
[point_trans_detail]ショートコードが実行される。- URL パラメータの
session_idを使ってサーバーに問い合わせ。 - サーバーは Stripe API で決済ステータスを確認し、OK なら先ほどロックした 600 ポイントを「確定消費」とし、さらに商品購入ボーナス(例:10pt)を付与。
- 結果表示:
response.result.use_point: 600response.result.new_point: 10response.result.total_point: (元の残高 - 600 + 10)- 画面に「決済完了!利用: 600, 獲得: 10, 残高: XX」と表示。
- 整合性確認: OK。ポイントは適切に移動した。
【パターン B: 異常系】ユーザーが途中でキャンセル、またはカードエラーが発生した場合
- リダイレクト戻り: ユーザーが
cancel_url(/payment-cancel/) に戻る、またはセッション作成自体が失敗する。 - ロールバック発動:
- ケース 1 (セッション作成失敗時):
catchブロック内またはelseブロックでrollbackPoints()が即座に呼ばれる。- コード:
fetch(.../point_trans_cancel/, { method: 'POST', body: ... })
- コード:
- ケース 2 (ユーザーが Stripe 画面でキャンセル): キャンセルページ (
[point_trans_cancel]) が読み込まれる。- このショートコードが自動的にサーバーへキャンセルリクエストを送信。
- ケース 1 (セッション作成失敗時):
- サーバー側処理:
- トランザクション ID を特定し、ロックされていた 600 ポイントをユーザー口座に戻す(復元)。
- 結果表示:
- 画面に「トランザクションがキャンセルされました。ポイントは復元されました。」と表示。
- 整合性確認: OK。ポイントが消滅せず、元に戻った。
検証結果レポート(シナリオ C に対する評価)
| 検証項目 | 結果 | 詳細コメント |
|---|---|---|
| ポイント二重消費防止 | ✅ 合格 | closeTriggered フラグと、トランザクション機構により、ボタン連打での二重課金は防がれている。 |
| ロールバック機能 | ✅ 合格 | rollbackPoints() 関数が定義されており、Stripe 失敗時に必ず point_trans_cancel API が呼ばれる設計になっている。 |
| 通貨単位 (JPY) | ✅ 合格 | unit_amount: intval(amount) となっており、100 倍するバグは存在しない。 |
| レスポンス構造 | ✅ 合格 | 全箇所で response.result.xxx を参照しており、構造崩壊のリスクがない。 |
| ゼロ円処理 | ✅ 合格 | if (amount <= 0) で Stripe をスキップし、直接完了ページへ遷移する分岐が存在する。 |
| Nonce/セキュリティ | ⚠️ 要確認 | sessionStorage から nonce を取得する記述があるが、pointInstance の初期化タイミングで sessionStorage にセットされているかの依存関係が暗黙的である点に注意が必要(通常は point_trans_open 時にセットされるはず)。 |
その他のシナリオについての注目点
-
シナリオ B (PayPal):
- PayPal SDK の非同期処理とポイントシステムの同期処理の兼ね合いが鍵。
onApprove内でtriggerClose()を呼ぶ順序が正しいか確認済み(OK)。 - ゼロ円時の「ポイントで支払う」ボタン切り替えロジックも実装済み(OK)。
- PayPal SDK の非同期処理とポイントシステムの同期処理の兼ね合いが鍵。
-
シナリオ D/E (ポイント購入):
- Nonce の隠蔽:
<span style="display:none;">で[point_purchase_open]を囲む手法は、XSS 対策および意図せぬ実行防止として有効か確認済み(OK)。 - 完了ページの仕組み: ショートコードを使わず、JS の
fetchで REST API を叩く構成は、リダイレクト後のパラメータ処理において最も安定した方法である(OK)。
- Nonce の隠蔽:
結論
提供されたシナリオファイル群は、非常に堅牢に設計されています。
特に、決済システム特有の「部分的な成功・失敗」に対するロールバック処理(シナリオ C)や、通貨単位の扱い(JPY)、ゼロ円決済の分岐など、実務でよく陥る罠を回避するためのガードが FINGERPRINT コメントと共に明確に記述されています。
仮想シミュレーションの結果: これらのコードを指示通りに実装すれば、ポイントの不整合(消失や二重付与)が発生する可能性は極めて低く、高い有効性が期待できます。
もし特定のシナリオについて、「もしここでネットワークが切断されたらどうなるか?」といったより深い異常系のシミュレーションをご希望であれば、お気軽にお申し付けください。
USER
検証結果レポート(シナリオ C に対する評価)と同じ仮想検証とその結果レポートをその他のすべてのシナリオ(A,B,D,E)でも実施して下さい。結果レポートの報告形式は、検証結果レポート(シナリオ C に対する評価)と同じ項目構成でよいです。
ASSISTANT
ご依頼通り、提供されたシナリオファイル(A, B, D, E)の内容に基づき、シナリオ C と同様の形式で仮想空間でのシミュレーション検証と結果レポートを作成しました。
各シナリオの特有のロジック(決済フロー、エラー処理、データ整合性)に焦点を当てて評価しています。
1. シナリオ A:ポイント付与のみ(決済なし)
【仮想シミュレーション】
- 設定値: 商品価格 1,000 円、換算レート 100 円=1pt、利用レート 1pt=1 円(利用はしない)。
- ユーザー操作: 「ポイントを付与」ボタンをクリック。
- フロー:
- ページ読み込み時に
[point_setting]と[point_own]が実行され、残高が表示される。 - ユーザーが「ポイントを付与」ボタンをクリック。
- JS の
triggerClose()(実装ではpointInstance.close()) が発火。 - サーバー側でトランザクションが開かれ、規定のポイント(例:10pt)が付与確定される。
- レスポンスを受け取り、画面に「成功!10 ポイント付与。残高: XX」と表示。
- ページ読み込み時に
- 異常系テスト: ボタン連打。
closeTriggeredフラグにより、2 回目のクリックは無視される。二重付与は発生しない。
【検証結果レポート(シナリオ A に対する評価)】
| 検証項目 | 結果 | 詳細コメント |
|---|---|---|
| ポイント二重付与防止 | ✅ 合格 | closeTriggered フラグとボタンの disabled 制御により、連打による二重付与は防がれている。 |
| ロールバック機能 | N/A | 決済処理がないため、失敗時のポイント戻し処理は不要(設計上正しい)。 |
| 通貨単位 (JPY) | ✅ 合格 | 金額計算はサーバー依存だが、フロントエンドでの誤った倍率計算(100 倍など)の記述はない。 |
| レスポンス構造 | ✅ 合格 | response.result.point, response.result.new_point を正しく参照している。 |
| ゼロ円処理 | N/A | 本シナリオは「付与のみ」であり、支払い金額の概念が存在しないため該当せず。 |
| Nonce/セキュリティ | ✅ 合格 | 決済ゲートウェイを介さないため、外部 Nonce 受け渡しは不要。内部トランザクション機構のみで完結しており安全。 |
総評: 最も単純な構成ながら、必要なガード(二重実行防止)がしっかり実装されており、単独運用としても堅牢です。
2. シナリオ B:商品購入 + PayPal JS SDK(同一ページ完結型)
【仮想シミュレーション】
- 設定値: 商品価格 1,000 円、保有ポイント 600pt、利用額 600pt → 支払額 400 円。
- ユーザー操作: ポイント入力 → PayPal ボタンクリック → 承認。
- フロー:
- ポイント入力変更時、
onTransOpenで表示更新。金額 400 円になる。 - PayPal ボタン描画 (
renderButtons)。 - ユーザーが PayPal で承認 (
onApprove)。 triggerClose()が呼び出され、600pt が仮確保・確定される。- 完了ページへリダイレクト(または同一ページ内表示更新)で結果を表示。
- ポイント入力変更時、
- 異常系テスト(全額ポイント払い): 利用額 1,000pt → 支払額 0 円。
- PayPal ボタンは非表示になり、「ポイントで支払う」ボタンが表示される。
- クリック時に
triggerClose()のみ実行。PayPal API は呼ばれない。
- 異常系テスト(キャンセル): PayPal 画面でキャンセル。
onCancelコールバックが発火し、エラーメッセージ表示。ポイントは消費されていない(close が呼ばれていないため)。
【検証結果レポート(シナリオ B に対する評価)】
| 検証項目 | 結果 | 詳細コメント |
|---|---|---|
| ポイント二重消費防止 | ✅ 合格 | closeTriggered フラグと、PayPal ボタン初期化のタイミング制御により保護されている。 |
| ロールバック機能 | ✅ 合格 | PayPal の onCancel や onError では point.close() を呼ばず、ポイント消費を行わない設計になっている(事前ロックが発生していないため)。 |
| 通貨単位 (JPY) | ✅ 合格 | PayPal SDK に渡す金額はそのままの値(文字列)として扱われており、桁数変換のバグはない。 |
| レスポンス構造 | ✅ 合格 | response.result.total_price, response.result.use_point など、すべて result 配下を参照。 |
| ゼロ円処理 | ✅ 合格 | amount <= 0 の分岐があり、PayPal ボタンを隠して専用ボタンに切り替える処理が実装済み。 |
| Nonce/セキュリティ | ✅ 合格 | PayPal の custom_id に pointsystem_nonce を含めることで、リクエストの正当性を担保している。 |
総評: 同一ページ完結型の UX が高く、かつ「全額ポイント払い」のエッジケースへの対応も完璧です。キャンセル時のポイント状態も適切に管理されています。
3. シナリオ D:ポイント購入 + PayPal(チャージ型)
【仮想シミュレーション】
- 設定値: 購入額 1,000 円分。
- ユーザー操作: 「1,000 円分のポイントを購入」ボタン(PayPal ボタン)クリック → 承認。
- フロー:
- ページ読み込み時、非表示 span 内の
[point_purchase_open]から nonce を取得。 - PayPal ボタンクリック →
createOrderでcustom_idに nonce を埋め込み注文作成。 onApproveで承認完了後、完了ページ (purchase-complete) へリダイレクト(クエリパラメータに order_id, nonce, amount を付与)。- 完了ページの JS が
fetchで REST API (point_purchase_close) を POST 呼び出し。 - サーバーが PayPal 注文を検証し、ポイントを加算。
- ページ読み込み時、非表示 span 内の
- 異常系テスト(二重送信): 完了ページのリロード。
- JS 変数
window.__pointsystem_purchase_closedがフラグとして機能し、2 回目の API 呼び出しを防ぐ。
- JS 変数
【検証結果レポート(シナリオ D に対する評価)】
| 検証項目 | 結果 | 詳細コメント |
|---|---|---|
| ポイント二重チャージ防止 | ✅ 合格 | 完了ページ側の __pointsystem_purchase_closed フラグと、サーバー側の注文 ID 管理により二重加算を防いでいる。 |
| ロールバック機能 | N/A | ポイント購入は「決済成功後に加算」する方式のため、失敗時には加算されない設計(事前引き落としなし)。適切。 |
| 通貨単位 (JPY) | ✅ 合格 | PayPal への送金金額と、サーバー検証時の金額照合に矛盾はない。 |
| レスポンス構造 | ✅ 合格 | REST API レスポンスの result.error, result.result.point を正しく処理。 |
| ゼロ円処理 | N/A | ポイント購入シナリオにおいて 0 円購入は通常あり得ないため、実装範囲外で問題なし。 |
| Nonce/セキュリティ | ✅ 合格 | 非表示 span からの nonce 取得と、PayPal custom_id 経由での受け渡し、さらに REST API での再検証という多層防御が機能している。 |
総評: 「購入ページ」と「完了ページ」を分離しつつ、REST API を使った安全な確定処理が行われています。リロード対策も万全です。
4. シナリオ E:ポイント購入 + Stripe Checkout(チャージ型)
【仮想シミュレーション】
- 設定値: 購入額 1,000 円分。
- ユーザー操作: 購入ボタンクリック → Stripe 画面遷移 → 決済完了 → リダイレクト戻り。
- フロー:
- 非表示 span から nonce を取得。
- ボタンクリック時、REST API で Stripe セッションを作成(Body に amount, currency など)。
success_urlにsession_id,nonce,amountをクエリパラメータとして含め、Stripe へリダイレクト。- 完了ページ (
purchase-success) で URL パラメータを取得。 - JS が
fetchで REST API (point_purchase_close) を POST 呼び出し(Body に order_id=session_id, nonce, amount)。 - サーバーが Stripe セッションを検証し、ポイントを加算。
- 異常系テスト(セッション無効): 古い URL でアクセス。
- サーバー側で Stripe API 検証が失敗し、エラーレスポンスが返る。JS はそれを表示し、ポイントは加算されない。
【検証結果レポート(シナリオ E に対する評価)】
| 検証項目 | 結果 | 詳細コメント |
|---|---|---|
| ポイント二重チャージ防止 | ✅ 合格 | シナリオ D と同様、完了ページでのフラグ制御と、サーバー側の Stripe セッション一意性チェックにより保護されている。 |
| ロールバック機能 | N/A | シナリオ C と異なり、事前ポイント引き落としを行わないため、失敗時のロールバック処理は不要(設計上正しい)。 |
| 通貨単位 (JPY) | ✅ 合格 | unit_amount: intval($params['amount']) と実装されており、JPY の 100 倍バグは回避されている。 |
| レスポンス構造 | ✅ 合格 | result.result.point の参照パスが正確。 |
| ゼロ円処理 | N/A | 同上。 |
| Nonce/セキュリティ | ✅ 合格 | Nonce を URL パラメータ(エンコード済み)で渡し、POST Body で再送する方式。Stripe の success_url 制限内で安全に実装されている。 |
総評: Stripe Checkout のリダイレクト特性をうまく利用した構成です。シナリオ C(商品購入)とは異なり、事前ロック処理がないため、複雑なロールバックロジックが不要で、よりシンプルかつ安定しています。
総合結論
すべてのシナリオ(A, B, C, D, E)について仮想検証を実施した結果、以下のことが確認できました。
- 一貫性: 全シナリオで
response.result以下の構造、add_actionを使わない登録方法、JPY の扱いなどが統一されており、実装ミスのリスクが低減されています。 - 安全性:
- 二重実行防止: 各シナリオに適したフラグ制御(
closeTriggered,purchaseClosed,__pointsystem_purchase_closed)が実装済み。 - ロールバック: 事前ポイント引き落としを行うシナリオ(C)には必ずロールバック処理が含まれ、行わないシナリオ(B, D, E)では不要な処理を含んでいないという、過不足のない設計です。
- ゼロ円対応: 必要となるシナリオ(B, C)では完璧に分岐処理が組まれています。
- 二重実行防止: 各シナリオに適したフラグ制御(
- 有効性: 提示された手順(コピー&ペースト+プレースホルダー置換)に従えば、これらのロジックがそのまま動作し、ポイントの不整合やセキュリティホールが生じる可能性は極めて低いと言えます。
推奨アクション: どのシナリオを採用する場合も、提供されたファイルを「修正せずにそのまま」使用することが、この検証結果から最も安全で確実な選択肢であると結論付けられます。