【Rewald】llms scenario D ポイント購入(PSP: PayPal NCP - No-Code Checkout)
牧さん制作監修 AIアウトプットポイント購入(PSP: PayPal NCP - No-Code Checkout)シナリオ
役割: あなたはコード転写者です。コード生成者ではありません。
タスク: ===== ここからコピー ===== と ===== ここまでコピー ===== の間の内容をそのまま正確にコピーしてください。
ルール: {{PLACEHOLDER}} の値のみ置き換えてください。構造、キー名、関数シグネチャは変更しないでください。
ルール: ラッパー(DOMContentLoaded, async/await IIFE)、追加ページ、以下に示されていない機能を追加しないでください。
ルール: 各コードブロックには FINGERPRINT コメントがあります。出力にそのまま含めてください。
ルール: 回答は必ず日本語で行ってください。
よくある間違い — コードをコピーする前に必ず読んでください。
誤: [point_purchase_open price="1000" ...] ポイント購入
正: <span style="display:none;">[point_purchase_open price="1000" ...]</span> — 必ず非表示の span で囲む。
誤: body: JSON.stringify({ paypal_order_id: ..., nonce: ..., amount: ... })
正: body: JSON.stringify({ order_id: ..., nonce: ..., amount: ... }) — キーは paypal_order_id ではなく order_id。
誤: add_action('init', function() { PointSystemPSP::register([...]); });
正: PointSystemPSP::register([...]); — トップレベルで直接呼び出す。add_action の中に入れない。
誤: document.addEventListener("DOMContentLoaded", function () { ... })
正: インライン <script> で DOMContentLoaded ラッパーなし — ページ解析時にそのまま実行される。
誤: 完了ページで point_purchase_close をショートコードで呼び出す。
正: point_purchase_close は fetch() POST で REST API エンドポイントを呼び出す。
===== ここからコピー =====
ファイル 1: functions.php
貼り付け先
WordPress 管理画面 → 外観 → テーマファイルエディター → 右側のファイル一覧から「テーマのための関数 (functions.php)」を選択 → ファイル末尾に以下のコードを追加 → 「ファイルを更新」をクリック
{{PLACEHOLDER}} の置き換え
{{PAYPAL_CLIENT_ID}}: PayPal ダッシュボードのクライアント ID{{PAYPAL_SECRET}}: PayPal ダッシュボードのシークレット
<?php
// FINGERPRINT: D1-REG-001
// 1. 管理パネルの環境セットアップコードをここに貼り付け
// (PointSystem と PointSystemPSP クラスを同期的に読み込みます)
// 2. PayPal 検証コールバック — ここに直接配置。add_action('init', ...) で囲まないこと
PointSystemPSP::register([
'verify' => function ($pointsystem_request) {
return PointSystemPSP::verifyPaypalOrder(
$pointsystem_request['order_id'],
$pointsystem_request['amount'],
'{{PAYPAL_CLIENT_ID}}',
'{{PAYPAL_SECRET}}',
false // true: サンドボックス, false: 本番
);
},
]);
===== ここまでコピー =====
===== ここからコピー =====
ファイル 2: ポイント購入ページ(WordPress 固定ページ)
貼り付け先
WordPress 管理画面 → 固定ページ → 新規追加(またはポイント購入用の既存ページを編集)→ エディタ右上の「テキスト」タブに切り替え → 以下のコードを貼り付け → 「公開」(または「更新」)をクリック
前提条件
- PayPal JS SDK のスクリプトタグがページ内で読み込まれていること(例:
<script src="https://www.paypal.com/sdk/js?client-id={{PAYPAL_CLIENT_ID}}¤cy=JPY"></script>)
{{PLACEHOLDER}} の置き換え
{{PRICE}}: 購入金額(例: 1000){{COMPLETION_PAGE_URL}}: 購入完了ページの URL(例:/purchase-complete/)
<!-- FINGERPRINT: D2-BALANCE-001 — 現在のポイント残高を表示 -->
[point_own callback_name="displayBalance"]
<p>保有ポイント: <span id="point_id">読み込み中...</span></p>
<!-- FINGERPRINT: D2-NONCE-001 — nonce 取得: ショートコードは必ず非表示の span で囲む -->
<span id="purchase_nonce" style="display:none;">
[point_purchase_open price="{{PRICE}}" currency_type="JPY" conv_rate="1"]
</span>
<div id="paypal-button-container"></div>
<div id="purchase-status" style="display:none; padding:10px; margin:10px 0; border-radius:5px;"></div>
<script>
// FINGERPRINT: D2-DISPLAY-001
function displayBalance(response) {
if (response.error) {
document.getElementById('point_id').textContent = 'エラー';
return;
}
document.getElementById('point_id').textContent = response.result.point;
}
var nonce = document.getElementById('purchase_nonce').textContent.trim();
var amount = '{{PRICE}}';
// FINGERPRINT: D2-GUARD-001 — 二重 close 防止
var purchaseClosed = false;
// FINGERPRINT: D2-PAYPAL-001 — PayPal JS SDK Buttons
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: { value: amount },
custom_id: nonce
}]
});
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
if (purchaseClosed) return;
purchaseClosed = true;
var orderId = details.id;
window.location.href = '{{COMPLETION_PAGE_URL}}'
+ '?order_id=' + encodeURIComponent(orderId)
+ '&nonce=' + encodeURIComponent(nonce)
+ '&amount=' + encodeURIComponent(amount);
});
},
onCancel: function() {
showStatus('支払いがキャンセルされました。', 'error');
},
onError: function(err) {
console.error('PayPal エラー:', err);
showStatus('決済エラーが発生しました。', 'error');
}
}).render('#paypal-button-container');
function showStatus(msg, type) {
var el = document.getElementById('purchase-status');
el.style.display = 'block';
el.textContent = msg;
el.style.background = type === 'error' ? '#f8d7da' : '#d4edda';
el.style.border = '1px solid ' + (type === 'error' ? '#f5c6cb' : '#c3e6cb');
}
</script>
===== ここまでコピー =====
===== ここからコピー =====
ファイル 3: 購入完了ページ — {{COMPLETION_PAGE_URL}}(WordPress 固定ページ、ショートコードなし)
貼り付け先
WordPress 管理画面 → 固定ページ → 新規追加 → タイトル:「購入完了」(任意)→ パーマリンクのスラッグをファイル 2 の {{COMPLETION_PAGE_URL}} と一致させる(例: purchase-complete)→ エディタ右上の「テキスト」タブに切り替え → 以下のコードを貼り付け → 「公開」をクリック
注意
- このページにはショートコードを置かないでください。JavaScript のみで動作します。
<!-- FINGERPRINT: D3-RESULT-001 — このページにはショートコードなし、JavaScript のみ -->
<div id="purchase-result">決済処理中...</div>
<script>
// FINGERPRINT: D3-CLOSE-001 — point_purchase_close REST API を JSON ボディで POST 呼び出し
var params = new URLSearchParams(location.search);
if (window.__pointsystem_purchase_closed) {
document.getElementById('purchase-result').textContent = '処理済みです。';
} else {
fetch('/wp-json/pointsystem/v1/point_purchase_close/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_id: params.get('order_id'),
nonce: params.get('nonce'),
amount: params.get('amount')
})
})
.then(function(res) { return res.json(); })
.then(function(result) {
window.__pointsystem_purchase_closed = true;
// FINGERPRINT: D3-RESP-001 — レスポンスは result.error と result.result.point を使用
var el = document.getElementById('purchase-result');
if (result.error) {
el.style.background = '#f8d7da';
el.style.padding = '10px';
el.textContent = 'エラー: ' + result.error;
} else {
el.style.background = '#d4edda';
el.style.padding = '10px';
el.textContent = 'ポイント購入完了! 新しい残高: ' + result.result.point;
}
})
.catch(function(err) {
document.getElementById('purchase-result').textContent = 'ネットワークエラー: ' + err.message;
});
}
</script>
===== ここまでコピー =====
確認チェックリスト — 回答する前に、出力内容が以下の 8 項目をすべて満たしていることを確認してください。
- PointSystemPSP::register() は add_action で囲まない — FINGERPRINT D1-REG-001 参照
- register() のキーは 'verify'('paypal_verify' やネストされた配列ではない)— FINGERPRINT D1-REG-001 参照
- [point_purchase_open] は非表示の span で囲まれている — FINGERPRINT D2-NONCE-001 参照
- displayBalance は response.result.point を使用 — FINGERPRINT D2-DISPLAY-001 参照
- 二重 close 防止ガード(purchaseClosed フラグ)が存在する — FINGERPRINT D2-GUARD-001 参照
- PayPal の custom_id に nonce がセットされている — FINGERPRINT D2-PAYPAL-001 参照
- point_purchase_close は POST + JSON ボディで呼び出し、キーは order_id — FINGERPRINT D3-CLOSE-001 参照
- レスポンスのプロパティは result.error と result.result.point — FINGERPRINT D3-RESP-001 参照