PAY.JP の Checkout V2 を試すためのサンプル集です。クライアント(モバイル)とローカル開発用のバックエンドを同梱しています。セットアップと動作確認の手順はこの README にまとめています。
| ディレクトリ | 内容 |
|---|---|
android/ |
Android アプリ(サンプル API と連携し、Checkout を開く) |
server/ |
Node.js 製のサンプル API(商品一覧・Checkout Session 作成・Webhook 受信) |
ios/ |
iOS アプリ(SwiftUI・サンプル API と連携し Safari で Checkout を開く) |
flutter/ |
Flutter アプリ(Android / iOS 共通・外部ブラウザで Checkout を開く) |
react-native/ |
React Native アプリ(Android / iOS 共通・Linking で外部ブラウザ起動) |
- Node.js 22 以上 (CI 検証バージョン)
- PAY.JP アカウント
- PAY.JP ダッシュボードで Price オブジェクトを作成済み
- PayPay を使うテスト環境
このサンプルサーバーは payment_method_types を ['card', 'paypay', 'apple_pay'] に固定しています (server/index.js)。Checkout 画面に表示される支払い方法はこの配列を編集するだけで増減できます — サーバー再起動のみで、クライアント (iOS/Android/Flutter/RN) のコード変更は不要です。
// 例: Apple Pay を外す
payment_method_types: ['card', 'paypay']
// 例: Apple Pay 専用にする
payment_method_types: ['apple_pay']PayPay / Apple Pay が有効でないアカウントでは Checkout Session 作成が失敗する可能性があります。Apple Pay は test mode から追加申請なしに利用できます。
| サンプル | Checkout 起動方法 | Card / PayPay | Apple Pay |
|---|---|---|---|
| iOS native | SFSafariViewController |
✅ | ✅ 対応端末で表示 |
| Android native | Chrome Custom Tabs | ✅ | — (Android 端末のため非表示) |
| Flutter | url_launcher (外部ブラウザ) |
✅ | ✅ iOS 対応端末で表示 / Android は非表示 |
| React Native | expo-web-browser.openBrowserAsync (iOS: SFSafariViewController / Android: Chrome Custom Tabs) + Linking でリダイレクト検知 |
✅ | ✅ iOS 対応端末で表示 (Simulator でボタン描画まで確認済み) / Android は非表示 |
Apple Pay ボタンは PAY.JP がホストする Checkout ページが端末側 (window.ApplePaySession の有無) で判定して描画するため、非対応端末・ブラウザではボタンは表示されません。
Checkout 画面に Apple Pay の選択肢が描画されるところまでは iOS Simulator で確認できますが、Apple Pay sheet を実際に開いて認証 → token 生成までを通す検証は Simulator では完結しません。Wallet の card provisioning が Simulator では成立しないため、以下が必要です (Apple Pay Sandbox Testing):
- 有料 Apple Developer Program メンバーシップ
- App Store Connect Sandbox tester アカウント (Users and Access > Sandbox > Testers で作成)
- Apple Pay 対応の実機 iPhone(iPad でも可)
- 実機で iCloud からサインアウトし、Sandbox tester アカウントでサインインし直す
- Region を Japan に設定
- Wallet にテストカードを手動追加 (例: JCB
3540 5019 9000 932412/30 CVV111)
Sandbox トランザクションは pre-fulfillment で decline します (本番キーと一致しないため)。本番環境での最終検証は実カード + 本番キーで別途必要です。
cd server
npm installcp .env.example .env最低限、次の値を .env に設定してください。
PAYJP_SECRET_KEY=sk_test_xxxxx
PAYJP_SAMPLE_PRICE_ID=price_xxxxx
PAYJP_WEBHOOK_SECRET=your_webhook_secret任意で次の値も設定できます。
PAYJP_SAMPLE_PRODUCT_NAME:/productsで返すサンプル商品名。未設定時はテスト商品PAYJP_SAMPLE_PRODUCT_AMOUNT:/productsで返すサンプル金額。未設定時は100ALLOWED_ORIGINS: CORS 許可オリジン。カンマ区切り。未設定時は全オリジン許可(本番環境では必ず設定してください)PORT: サーバーポート。既定値は3000
npm startサーバーが http://localhost:3000 で起動します。
payjp-cli listen --forward-to http://localhost:3000/webhooksuccess_url へのリダイレクトだけでは決済完了は確定しません。注文確定や発送などの後続処理は、必ず checkout.session.completed の Webhook を受けてから行ってください。
サンプル商品一覧を返します。
レスポンス例:
{
"products": [
{ "id": "price_xxx", "name": "テスト商品A", "amount": 100 }
]
}Checkout Session を作成します。
リクエスト例:
{
"price_id": "price_xxx",
"quantity": 1,
"success_url": "payjpcheckoutexample://checkout/success",
"cancel_url": "payjpcheckoutexample://checkout/cancel"
}レスポンス例:
{
"id": "cs_xxx",
"url": "https://checkout.pay.jp/...",
"status": "open"
}success_url / cancel_url には次のスキームだけを許可しています。
https://http://localhostpayjpcheckoutexample://
PAY.JP からの Webhook を受信します。x-payjp-webhook-token が .env の PAYJP_WEBHOOK_SECRET と一致しない場合は拒否します。
処理対象イベント:
checkout.session.completedcheckout.session.expired
- Android Studio で
android/をプロジェクトとして開きます。 - アプリのバックエンド URL に、エミュレータなら
http://10.0.2.2:3000、実機なら開発マシンの LAN IP を入力します。 - 商品を選び、Checkout を開始します。
- Xcode で
ios/PayJPCheckoutExample.xcodeprojを開きます。 - シミュレータでは既定の
http://localhost:3000のまま動かせます。 - 実機 (Apple Pay 検証を含む) は同一 Wi-Fi 上の Mac の LAN IP を入力します。
- Mac の IP は
ipconfig getifaddr en0(Wi-Fi) またはipconfig getifaddr en1で確認 (例:http://192.168.1.10:3000)。 - Mac の システム設定 → ネットワーク → ファイアウォール が ON なら Node.js を許可するか一時的に OFF。
- 初回起動時に 「ローカルネットワーク上のデバイスの検索を許可しますか?」 のダイアログが出るので「許可」。拒否した場合は 設定アプリ → PayJPCheckoutExample → ローカルネットワーク を ON。
- Info.plist で
NSAllowsArbitraryLoads = trueを有効化済み (開発用サンプル想定。LAN のhttp://経路を許可するための ATS 緩和)。
- Mac の IP は
- 商品を選び、Safari で Checkout を開始します。Apple Pay の実機検証手順は Apple Pay の動作確認 を参照。
cd flutter && flutter pub get- iOS は初回のみ
flutter config --enable-swift-package-manager(CocoaPods は不要・Podfile もコミットしていません) flutter runでエミュレータ / シミュレータに展開。バックエンド URL 既定値は Android エミュレータがhttp://10.0.2.2:3000、iOS シミュレータがhttp://localhost:3000。- 詳細は
flutter/README.md。
cd react-native && npm install- iOS は
cd ios && bundle install && bundle exec pod install && cd .. npm startで Metro を起動、別シェルでnpm run androidまたはnpm run ios- 詳細は
react-native/README.md。
iOS アプリは success_url に戻った時点では「決済完了」ではなく、Webhook 確認待ちとして扱います。success_url は受付済みのシグナルであって、決済確定そのものではありません。
コマンドラインでビルドする例:
xcodebuild -project ios/PayJPCheckoutExample.xcodeproj -target PayJPCheckoutExample -sdk iphonesimulator -configuration Debug build成果物は ios/build/Debug-iphonesimulator/PayJPCheckoutExample.app に出力されます。プロジェクトの iOS デプロイメントターゲットは 26.1 です。
テストカード、PayPay のテスト手順、テスト用アカウントについては PAY.JP ドキュメント を参照してください。
- 秘密鍵(
sk_xxx)はクライアントサイドに含めないでください success_urlだけで決済完了扱いしないでください- Webhook を使わない場合、このサンプルは注文確定の正判定を提供しません