fix(#163): 購入レシートのサーバー側検証を追加(Android先行) - #211
Merged
Merged
Conversation
クライアント改竄によるプレミアム不正取得を防ぐため、購入をサーバー側 (Cloud Functions)で検証する仕組みを追加。プレミアム判定の信頼できる 唯一のソースをサーバー検証済みフラグに一本化する(対応案#1/#3)。 サーバー: - functions/purchase/android_verifier.js: Play Developer API での検証 ロジック(APIクライアントを注入可能にしてユニットテスト済み・8件) - functions/index.js: verifyPurchase(onCall) を追加。認証+レート制限+ Android検証→検証成功時のみ premium_entitlement(サーバー専用doc)へ書込。 iOSは未対応(unimplemented)としTODO化 - firestore.rules: premium_entitlement をクライアント書込禁止(サーバー専用) クライアント: - purchase_verifier.dart: verifyPurchase を呼ぶ注入可能ラッパー (Firebase初期化前の構築でも落ちないよう instance アクセスを遅延) - one_time_purchase_service.dart: 購入成功時にAndroidはサーバー検証を必須化。 読込元を premium_entitlement に一本化。レガシーフラグのみの既存ユーザーは 起動時に自動再検証(restorePurchases)でシームレス移行。iOSは既存挙動を維持。 InAppPurchase.instance アクセスを遅延化(構築時の課金接続を回避) テスト: - server_verification_test / premium_migration_test を追加(実機なしで 「サーバー未検証ならプレミアムを付与しない」「移行判定」を検証) - firestore_rules/purchases.test.js を追加(JDK21+環境/CIで実行) 残作業(IK手動・docs/purchase-server-verification-setup.md 参照): サービスアカウント発行・Play Console権限付与・Secret Manager登録・deploy Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #163
背景
issue #163 の核心「購入検証がクライアント側のみ → 改竄でプレミアム不正取得が可能」に対応する。復元タイムアウト時の Completer 放置(対応案#2)と一次検証(PurchaseValidator)は PR #203 で対応済みのため、本PRは**サーバー側レシート検証(対応案#1)とプレミアム判定の一本化(対応案#3)**を実装する。
変更点
サーバー
functions/purchase/android_verifier.js— Google Play Developer API での検証ロジック(APIクライアント注入式で純粋・テスト可能)functions/purchase/android_publisher_client.js— googleapis への薄いグルーfunctions/index.js—verifyPurchase(onCall) を追加。認証+レート制限+Android検証→検証成功時のみusers/{uid}/purchases/premium_entitlement(サーバー専用doc)へ書込。iOSはunimplemented(TODO)firestore.rules—premium_entitlementをクライアント書込禁止(サーバー専用)に変更クライアント
lib/services/purchase/purchase_verifier.dart—verifyPurchaseを呼ぶ注入可能ラッパー(Firebase初期化前の構築でも落ちないよう instance アクセスを遅延)lib/services/one_time_purchase_service.dart— Android購入はサーバー検証を必須化、プレミアム読込元をpremium_entitlementに一本化、レガシーフラグのみの既存ユーザーは起動時の自動再検証(restorePurchases)でシームレス移行。iOSは既存挙動を維持。InAppPurchase.instanceアクセスも遅延化lib/services/purchase/purchase_persistence.dart—loadServerEntitlementを追加テスト
functions/test/purchase/android_verifier.test.js(8件)— 正規/不正token/productID不一致/キャンセル/API失敗 等test/services/purchase/server_verification_test.dart— 「サーバー未検証ならプレミアム付与しない」を実機なしで検証test/services/purchase/premium_migration_test.dart— 読込元一本化・移行判定(Android/iOS)test/firestore_rules/purchases.test.js—premium_entitlementのクライアント書込拒否(JDK21+ 環境/CIで実行)検証結果
flutter analyze:エラー0flutter test --exclude-tags=integration:477件パス(既存含む)flutter build web:成功(Web影響なし)レビュー時の注意 / 残作業
docs/purchase-server-verification-setup.md参照): サービスアカウント発行・Play Console 権限付与・Secret Manager にGOOGLE_PLAY_SERVICE_ACCOUNT_JSON登録・firebase deploy(functions + firestore:rules)verifyPurchaseは iOS にunimplementedを返し、当面クライアント一次検証のみで付与🤖 Generated with Claude Code