Skip to content

feat(runtime): add fire os and vega support#162

Open
hyochan wants to merge 51 commits into
mainfrom
release/fireos-prerelease
Open

feat(runtime): add fire os and vega support#162
hyochan wants to merge 51 commits into
mainfrom
release/fireos-prerelease

Conversation

@hyochan

@hyochan hyochan commented May 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Add Amazon Fire OS Android flavor support and setup documentation.
  • Add Vega runtime adapters and generated type/package metadata updates across framework libraries.
  • Add Kit-side Amazon receipt handling and related route/schema coverage.

Validation

  • git diff --cached --check
  • scripts/audit-non-godot-parity.mjs
  • packages/docs: bun run typecheck, bun run audit:docs, Prettier check from pre-commit
  • packages/google: ./gradlew :openiap:compilePlayDebugKotlin, ./gradlew :openiap:compileHorizonDebugKotlin
  • packages/gql: bun test with Bun 1.3.13
  • packages/kit: lint, Prettier check, tests, smoke server from pre-commit
  • libraries/kmp-iap: ./gradlew :library:compileDebugKotlinAndroid from pre-commit
  • Vega VVD: installed App Tester and the Vega smoke app via vda push + vpm install, injected amazon.sdktester.json and app sandbox config, verified getProductData reached the Amazon App Tester purchase sheet, and confirmed Get Item completed with Thank You! Your order is complete.
  • Vega VVD follow-up: after the App Tester completion dialog, verify the app returns to the smoke screen and records purchase result / getPurchaseUpdates; the current macOS VVD session disconnected immediately after purchase completion.

Notes

  • Flutter analyze was skipped by the pre-commit hook because flutter is not on PATH in this environment.

Summary by CodeRabbit

  • New Features

    • Fire OS (Amazon Appstore) support across SDKs, toolchains, and server verification; Vega OS runtime adapter for React Native/Expo; Amazon in-app adapters and verification flows.
  • Bug Fixes / Privacy

    • Improved sensitive-key redaction and reduced logging of verification parameters; stricter verification payload validation and platform-consistency checks.
  • Documentation

    • New Fire OS and Vega OS setup/runtime guides; updated examples (finishTransaction object form) and release notes.
  • Tests

    • Extensive Amazon/Vega unit and integration test coverage.
  • Chores

    • CI/release workflow and publishing adjustments; version bumps.

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 255 files, which is 105 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c93daff2-fec9-4bf1-aba1-56df54ed21bc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ca6bdb and b60a706.

⛔ Files ignored due to path filters (28)
  • libraries/expo-iap/bun.lock is excluded by !**/*.lock
  • libraries/expo-iap/example/bun.lock is excluded by !**/*.lock
  • libraries/react-native-iap/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • packages/docs/public/examples/amazon/videos/fireos-available-purchases.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/amazon/videos/fireos-inapp.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/amazon/videos/fireos-overview.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/amazon/videos/fireos-subscription.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/amazon/videos/fireos-verification.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/apple/videos/apple-available-purchases.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/apple/videos/apple-inapp.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/apple/videos/apple-subscription.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/apple/videos/apple-verification.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/google/videos/google-available-purchases.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/google/videos/google-inapp.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/google/videos/google-subscription.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/google/videos/google-verification.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/horizon/videos/horizon-available-purchases.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/horizon/videos/horizon-inapp.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/horizon/videos/horizon-overview.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/horizon/videos/horizon-subscription.mp4 is excluded by !**/*.mp4
  • packages/docs/public/examples/horizon/videos/horizon-verification.mp4 is excluded by !**/*.mp4
  • packages/docs/src/generated/version-metadata.json is excluded by !**/generated/**
  • packages/gql/src/generated/Types.cs is excluded by !**/generated/**
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.gd is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (255)
  • .claude/commands/commit.md
  • .claude/commands/e2e-tests.md
  • .claude/commands/verify-all.md
  • .codex/skills/openiap-workflows/SKILL.md
  • .github/workflows/ci.yml
  • .github/workflows/release-google.yml
  • AGENTS.md
  • AGENTS.md
  • CLAUDE.md
  • CLAUDE.md
  • GEMINI.md
  • knowledge/_claude-context/context.md
  • knowledge/internal/03-coding-style.md
  • knowledge/internal/04-platform-packages.md
  • knowledge/internal/06-git-deployment.md
  • libraries/expo-iap/.npmignore
  • libraries/expo-iap/android/build.gradle
  • libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapLog.kt
  • libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapModule.kt
  • libraries/expo-iap/example/.env.example
  • libraries/expo-iap/example/.gitignore
  • libraries/expo-iap/example/App.kepler.tsx
  • libraries/expo-iap/example/__tests__/alternative-billing.test.tsx
  • libraries/expo-iap/example/__tests__/available-purchases.test.tsx
  • libraries/expo-iap/example/__tests__/index.test.tsx
  • libraries/expo-iap/example/__tests__/offer-code.test.tsx
  • libraries/expo-iap/example/__tests__/purchase-flow.test.tsx
  • libraries/expo-iap/example/__tests__/subscription-flow.test.tsx
  • libraries/expo-iap/example/__tests__/vega-runtime.test.ts
  • libraries/expo-iap/example/__tests__/webhook-stream.test.tsx
  • libraries/expo-iap/example/amazon.config.json
  • libraries/expo-iap/example/amazon.sdktester.json
  • libraries/expo-iap/example/app.config.ts
  • libraries/expo-iap/example/app/all-products.tsx
  • libraries/expo-iap/example/app/alternative-billing.tsx
  • libraries/expo-iap/example/app/available-purchases.tsx
  • libraries/expo-iap/example/app/index.tsx
  • libraries/expo-iap/example/app/offer-code.tsx
  • libraries/expo-iap/example/app/purchase-flow.tsx
  • libraries/expo-iap/example/app/subscription-flow.tsx
  • libraries/expo-iap/example/app/webhook-stream.tsx
  • libraries/expo-iap/example/babel.config.js
  • libraries/expo-iap/example/jest.setup.js
  • libraries/expo-iap/example/metro.config.js
  • libraries/expo-iap/example/package.json
  • libraries/expo-iap/example/scripts/build-vega-example.mjs
  • libraries/expo-iap/example/scripts/run-vega-firetv.mjs
  • libraries/expo-iap/example/src/hooks/useVegaTvSelection.ts
  • libraries/expo-iap/example/src/utils/errorUtils.ts
  • libraries/expo-iap/example/src/utils/vegaRuntime.ts
  • libraries/expo-iap/example/vega-shims/expo-router.tsx
  • libraries/expo-iap/ios/ExpoIapLog.swift
  • libraries/expo-iap/ios/ExpoIapModule.swift
  • libraries/expo-iap/ios/onside/OnsideIapModule.swift
  • libraries/expo-iap/package.json
  • libraries/expo-iap/plugin/__tests__/withIAP.test.ts
  • libraries/expo-iap/plugin/src/expoConfig.augmentation.d.ts
  • libraries/expo-iap/plugin/src/withIAP.ts
  • libraries/expo-iap/plugin/src/withLocalOpenIAP.ts
  • libraries/expo-iap/plugin/src/withVega.ts
  • libraries/expo-iap/src/ExpoIapModule.ts
  • libraries/expo-iap/src/__tests__/native-log-redaction.test.js
  • libraries/expo-iap/src/__tests__/useIAP.test.tsx
  • libraries/expo-iap/src/__tests__/vega-adapter.test.ts
  • libraries/expo-iap/src/amazon-devices-kepler.d.ts
  • libraries/expo-iap/src/index.kepler.ts
  • libraries/expo-iap/src/index.ts
  • libraries/expo-iap/src/types.ts
  • libraries/expo-iap/src/useIAP.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/expo-iap/src/vega.kepler.ts
  • libraries/expo-iap/src/vega.ts
  • libraries/flutter_inapp_purchase/android/build.gradle
  • libraries/flutter_inapp_purchase/android/settings.gradle
  • libraries/flutter_inapp_purchase/android/src/main/kotlin/io/github/hyochan/flutter_inapp_purchase/AndroidInappPurchasePlugin.kt
  • libraries/flutter_inapp_purchase/android/src/main/kotlin/io/github/hyochan/flutter_inapp_purchase/FlutterInappPurchasePlugin.kt
  • libraries/flutter_inapp_purchase/example/README.md
  • libraries/flutter_inapp_purchase/example/android/app/build.gradle
  • libraries/flutter_inapp_purchase/example/android/gradle.properties
  • libraries/flutter_inapp_purchase/example/android/settings.gradle
  • libraries/flutter_inapp_purchase/example/env.example
  • libraries/flutter_inapp_purchase/example/lib/main.dart
  • libraries/flutter_inapp_purchase/example/lib/src/constants.dart
  • libraries/flutter_inapp_purchase/example/lib/src/screens/purchase_flow_screen.dart
  • libraries/flutter_inapp_purchase/example/lib/src/screens/subscription_flow_screen.dart
  • libraries/flutter_inapp_purchase/example/pubspec.yaml
  • libraries/flutter_inapp_purchase/ios/Classes/FlutterInappPurchasePlugin.swift
  • libraries/flutter_inapp_purchase/lib/flutter_inapp_purchase.dart
  • libraries/flutter_inapp_purchase/lib/types.dart
  • libraries/flutter_inapp_purchase/macos/Classes/FlutterInappPurchasePlugin.swift
  • libraries/flutter_inapp_purchase/pubspec.yaml
  • libraries/flutter_inapp_purchase/test/flutter_inapp_purchase_channel_test.dart
  • libraries/godot-iap/addons/godot-iap/android/GodotIap.gdap
  • libraries/godot-iap/addons/godot-iap/godot_iap.gd
  • libraries/godot-iap/addons/godot-iap/plugin.cfg
  • libraries/godot-iap/addons/godot-iap/types.gd
  • libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIap.kt
  • libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIapLog.kt
  • libraries/godot-iap/ios-gdextension/Sources/GodotIap/GodotIap.swift
  • libraries/godot-iap/ios-gdextension/Sources/GodotIap/GodotIapLog.swift
  • libraries/kmp-iap/example/composeApp/src/androidMain/kotlin/dev/hyo/martie/screens/PlatformTime.android.kt
  • libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/screens/AvailablePurchasesScreen.kt
  • libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/screens/WebhookTestNotification.kt
  • libraries/kmp-iap/example/composeApp/src/iosMain/kotlin/dev/hyo/martie/screens/WebhookTestNotification.ios.kt
  • libraries/kmp-iap/example/composeApp/src/jvmMain/kotlin/dev/hyo/martie/screens/PlatformTime.jvm.kt
  • libraries/kmp-iap/gradle.properties
  • libraries/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.kt
  • libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/KmpIap.kt
  • libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt
  • libraries/kmp-iap/library/src/iosMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseIOS.kt
  • libraries/maui-iap/android/openiap/src/main/java/dev/hyo/openiap/maui/OpenIapMauiModule.kt
  • libraries/maui-iap/src/OpenIap.Maui.Bindings.iOS/ApiDefinition.cs
  • libraries/maui-iap/src/OpenIap.Maui/OpenIap.Maui.csproj
  • libraries/maui-iap/src/OpenIap.Maui/Platforms/iOS/OpenIapIOS.cs
  • libraries/maui-iap/src/OpenIap.Maui/Types.cs
  • libraries/react-native-iap/README.md
  • libraries/react-native-iap/android/build.gradle
  • libraries/react-native-iap/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • libraries/react-native-iap/example/.env.example
  • libraries/react-native-iap/example/.eslintrc.js
  • libraries/react-native-iap/example/App.kepler.tsx
  • libraries/react-native-iap/example/__tests__/screens/AlternativeBilling.test.tsx
  • libraries/react-native-iap/example/__tests__/screens/AvailablePurchases.test.tsx
  • libraries/react-native-iap/example/__tests__/screens/OfferCode.test.tsx
  • libraries/react-native-iap/example/__tests__/screens/PurchaseFlow.test.tsx
  • libraries/react-native-iap/example/__tests__/screens/SubscriptionFlow.test.tsx
  • libraries/react-native-iap/example/__tests__/screens/WebhookStream.test.tsx
  • libraries/react-native-iap/example/__tests__/utils/vegaRuntime.test.ts
  • libraries/react-native-iap/example/amazon.config.json
  • libraries/react-native-iap/example/amazon.sdktester.json
  • libraries/react-native-iap/example/android/app/build.gradle
  • libraries/react-native-iap/example/android/gradle.properties
  • libraries/react-native-iap/example/android/settings.gradle
  • libraries/react-native-iap/example/jest.setup.js
  • libraries/react-native-iap/example/manifest.toml
  • libraries/react-native-iap/example/package.json
  • libraries/react-native-iap/example/screens/AllProducts.tsx
  • libraries/react-native-iap/example/screens/AlternativeBilling.tsx
  • libraries/react-native-iap/example/screens/AvailablePurchases.tsx
  • libraries/react-native-iap/example/screens/Home.tsx
  • libraries/react-native-iap/example/screens/OfferCode.tsx
  • libraries/react-native-iap/example/screens/PurchaseFlow.tsx
  • libraries/react-native-iap/example/screens/SubscriptionFlow.tsx
  • libraries/react-native-iap/example/screens/WebhookStream.tsx
  • libraries/react-native-iap/example/scripts/build-vega-example.mjs
  • libraries/react-native-iap/example/scripts/run-vega-firetv.mjs
  • libraries/react-native-iap/example/src/hooks/useVerificationMethod.ts
  • libraries/react-native-iap/example/src/types/env.d.ts
  • libraries/react-native-iap/example/src/utils/vegaRuntime.ts
  • libraries/react-native-iap/ios/HybridRnIap.swift
  • libraries/react-native-iap/package.json
  • libraries/react-native-iap/plugin/__tests__/withIAP-android.test.ts
  • libraries/react-native-iap/plugin/__tests__/withIAP-ios.test.ts
  • libraries/react-native-iap/plugin/src/withIAP.ts
  • libraries/react-native-iap/src/__tests__/index.test.ts
  • libraries/react-native-iap/src/__tests__/utils/error.test.ts
  • libraries/react-native-iap/src/__tests__/utils/type-bridge.test.ts
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/index.kepler.ts
  • libraries/react-native-iap/src/index.ts
  • libraries/react-native-iap/src/specs/RnIap.nitro.ts
  • libraries/react-native-iap/src/types.ts
  • libraries/react-native-iap/src/types/amazon-devices-kepler/index.d.ts
  • libraries/react-native-iap/src/utils/error.ts
  • libraries/react-native-iap/src/utils/type-bridge.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • libraries/react-native-iap/src/vega.kepler.ts
  • libraries/react-native-iap/src/vega.ts
  • llms-full.txt
  • llms.txt
  • openiap-versions.json
  • packages/apple/Sources/Models/Types.swift
  • packages/apple/Sources/OpenIapModule+ObjC.swift
  • packages/apple/Sources/OpenIapModule.swift
  • packages/apple/Sources/OpenIapStore.swift
  • packages/apple/Tests/OpenIapTests/SubscriptionGroupMappingTests.swift
  • packages/docs/openiap-versions.json
  • packages/docs/public/announcements/amazon-fireos-vega.webp
  • packages/docs/public/examples/amazon/home.webp
  • packages/docs/public/examples/apple/alternative-billing.webp
  • packages/docs/public/examples/apple/available-purchases.webp
  • packages/docs/public/examples/apple/home.webp
  • packages/docs/public/examples/apple/offer-code.webp
  • packages/docs/public/examples/apple/purchase-flow.webp
  • packages/docs/public/examples/apple/subscription-flow-upgrade.webp
  • packages/docs/public/examples/google/available-purchases.webp
  • packages/docs/public/examples/google/home.webp
  • packages/docs/public/examples/google/purchase-flow.webp
  • packages/docs/public/examples/google/redeem-offer-code.webp
  • packages/docs/public/examples/google/subscription-flow-upgrade.webp
  • packages/docs/public/examples/google/subscription-flow.webp
  • packages/docs/public/examples/horizon/.gitkeep
  • packages/docs/public/examples/horizon/home.webp
  • packages/docs/public/llms-full.txt
  • packages/docs/public/llms.txt
  • packages/docs/src/components/PlatformTabs.tsx
  • packages/docs/src/pages/docs/android-setup.tsx
  • packages/docs/src/pages/docs/apis/get-active-subscriptions.tsx
  • packages/docs/src/pages/docs/ecosystem.tsx
  • packages/docs/src/pages/docs/example.tsx
  • packages/docs/src/pages/docs/examples/StoreExampleTemplate.tsx
  • packages/docs/src/pages/docs/examples/VideoSlot.tsx
  • packages/docs/src/pages/docs/examples/android.tsx
  • packages/docs/src/pages/docs/examples/data.ts
  • packages/docs/src/pages/docs/examples/fireos.tsx
  • packages/docs/src/pages/docs/examples/horizon.tsx
  • packages/docs/src/pages/docs/examples/index.tsx
  • packages/docs/src/pages/docs/examples/ios.tsx
  • packages/docs/src/pages/docs/features/alternative-marketplace/onside.tsx
  • packages/docs/src/pages/docs/features/offer-code-redemption.tsx
  • packages/docs/src/pages/docs/features/purchase.tsx
  • packages/docs/src/pages/docs/features/runtime-integrations.tsx
  • packages/docs/src/pages/docs/features/subscription/active-subscriptions.tsx
  • packages/docs/src/pages/docs/features/subscription/index.tsx
  • packages/docs/src/pages/docs/features/validation.tsx
  • packages/docs/src/pages/docs/features/vega-os.tsx
  • packages/docs/src/pages/docs/fireos-setup.tsx
  • packages/docs/src/pages/docs/foundation/sponsorship.tsx
  • packages/docs/src/pages/docs/index.tsx
  • packages/docs/src/pages/docs/kit-backend.tsx
  • packages/docs/src/pages/docs/setup/expo.tsx
  • packages/docs/src/pages/docs/setup/flutter.tsx
  • packages/docs/src/pages/docs/setup/index.tsx
  • packages/docs/src/pages/docs/setup/react-native.tsx
  • packages/docs/src/pages/docs/types/verify-purchase-with-provider-props.tsx
  • packages/docs/src/pages/docs/updates/announcements.tsx
  • packages/docs/src/pages/docs/updates/releases.tsx
  • packages/docs/src/styles/pages.css
  • packages/docs/vite.config.ts
  • packages/google/CONTRIBUTING.md
  • packages/google/CONVENTION.md
  • packages/google/Example/build.gradle.kts
  • packages/google/Example/src/main/AndroidManifest.xml
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AvailablePurchasesScreen.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/HomeScreen.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt
  • packages/google/openiap/build.gradle.kts
  • packages/google/openiap/src/amazon/AndroidManifest.xml
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapErrorExtensions.kt
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/store/OpenIapStoreExtensions.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/AmazonPriceParserTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/AmazonSubscriptionGroupMappingTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/FetchProductsAmazonTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/SubscriptionBillingIssueAmazonTest.kt
  • packages/google/openiap/src/testHorizon/java/dev/hyo/openiap/SubscriptionGroupMappingHorizonTest.kt
  • packages/google/openiap/src/testPlay/java/dev/hyo/openiap/SubscriptionGroupMappingPlayTest.kt
  • packages/google/package.json
  • packages/gql/codegen/core/schema-linter.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Fire OS (amazon) Android flavor and Vega OS runtime support across SDKs, plugins, native adapters, backend Amazon RVS verification, CI publishing steps, docs, and tests; updates types/schemas, build wiring, and release notes.

Changes

Amazon & Vega platform integration

Layer / File(s) Summary
CI & release publishing
.github/workflows/ci.yml, .github/workflows/release-google.yml
CI Android job builds amazon flavor; release workflow checks/publishes openiap-google-amazon and updates release notes and installation snippets.
Gradle flavor & OpenIAP artifact wiring
packages/google/openiap/..., packages/google/Example/..., libraries/*/android/build.gradle, example/*/android/*
Adds amazon product flavor and OPENIAP_STORE=amazon; computes openiapFlavor from horizonEnabled/fireOsEnabled, selects openiap-google-amazon when fireOsEnabled, and conditions publishing on publish tasks.
Config plugins & local module wiring
libraries/expo-iap/plugin/src/*, libraries/react-native-iap/plugin/src/*, libraries/expo-iap/plugin/src/withLocalOpenIAP.ts
Plugins now read/write horizonEnabled/fireOsEnabled, modify build.gradle to inject/remove correct OpenIAP dependency and missingDimensionStrategy, sync Horizon app-id metadata, and normalize AndroidManifest permissions.
Expo & React Native Vega + Amazon adapters
libraries/expo-iap/src/vega*, libraries/react-native-iap/src/vega*, libraries/*/src/vega-adapter.ts
Adds Vega runtime shims and adapters (kepler) for Expo/RN that map Vega purchasing APIs to OpenIAP shapes and implement IAPKit verification for Amazon.
Native OpenIAP Amazon module
packages/google/openiap/src/amazon/*, packages/google/openiap/src/main/java/dev/hyo/openiap/
New Amazon OpenIapModule implementation, AndroidManifest, price parsing, error mappings, loader/constructor wiring, and OpenIapStore convenience.
Backend Convex Amazon RVS
packages/kit/convex/purchases/amazon.ts, packages/kit/convex/schema.ts, packages/kit/server/api/v1/routes.ts
Adds Convex action verifyAmazonReceiptInternalV1, Amazon receipt parsing/mapping/persistence, errors, amazonSharedSecret project field, route handling, replay guard, and tests.
Types & schema updates
libraries/*/src/types.ts, packages/gql/src/type.graphql, packages/*/Types.*
Extends IapStore across languages to include amazon, adds Amazon IAPKit props/types, updates GraphQL schema and codegen linter exceptions.
Provider verification plumbing
packages/apple/.../OpenIapModule.swift, packages/google/.../PurchaseVerificationValidator.kt, libraries/*/verifyPurchaseWithProvider
Standardizes building/parsing of IAPKit payloads for Apple/Google/Amazon, tightens response validation, error extraction, and platform checks.
Logging & redaction
libraries/*/ExpoIapLog.*, libraries/godot-iap/*/GodotIapLog.*, libraries/expo-iap/src/__tests__/native-log-redaction.test.js
Centralizes sensitive-key detection, extends sanitization for JSON strings and nested payloads, and updates tests to assert redaction coverage.
Docs & site
packages/docs/src/pages/docs/*, llms.txt, llms-full.txt, scripts/agent/compile-context.ts
Adds Fire OS and Vega docs pages, updates install snippets/ToC, regenerates LLM outputs, updates releases page, and improves Vite doc chunking.
Tests
many __tests__, testAmazon, Vitest/JUnit files across packages
Adds/extends tests for Amazon/Vega adapters, IAPKit verification shapes, plugin behavior, type conversions, and server route/schema validations.

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

Suggested labels:
🎯 feature, 🤖 android, cross-platform

"A rabbit hops with joy and cheer,
New flavors land — the path is clear.
Fire OS builds and Vega runs,
Docs penned, tests passed — the work is done.
🐇✨"

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/fireos-prerelease

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including native Android, React Native, Expo, and Flutter. It introduces the amazon Gradle flavor, Vega OS kepler runtime adapters, and Amazon RVS receipt verification in IAPKit. Feedback suggests improving compatibility by replacing replaceAll with regex, using 0 as a date fallback instead of Date.now(), and using direct property access instead of reflection in the native Android module. Additionally, missing mappings for freeTrialPeriodAndroid and subscriptionPeriodAndroid should be added to ensure feature parity.

Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment on lines +371 to +386
if (type === 'subs') {
return {
...base,
type,
subscriptionOfferDetailsAndroid: [createSubscriptionOffer(product)],
subscriptionOffers: [createStandardizedSubscriptionOffer(product)],
};
}

return {
...base,
type,
subscriptionOfferDetailsAndroid: null,
subscriptionOffers: null,
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The mapProduct function for Vega OS is missing the mapping for freeTrialPeriodAndroid and subscriptionPeriodAndroid. These fields are part of the ProductSubscription type and should be populated from the Amazon product data to ensure feature parity with the native Android implementation.

  if (type === 'subs') {
    return {
      ...base,
      type,
      freeTrialPeriodAndroid: product.freeTrialPeriod ?? null,
      subscriptionPeriodAndroid: product.subscriptionPeriod ?? null,
      subscriptionOfferDetailsAndroid: [createSubscriptionOffer(product)],
      subscriptionOffers: [createStandardizedSubscriptionOffer(product)],
    };
  }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. The generated ProductSubscriptionAndroid type in libraries/expo-iap/src/types.ts does not expose freeTrialPeriodAndroid or subscriptionPeriodAndroid; adding them makes expo-iap typecheck fail. The subscription period is already carried through the Android pricing phase billingPeriod.

Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated
Comment on lines +355 to +365
nameAndroid: product.title ?? sku,
subscriptionPeriodAndroid: product.subscriptionPeriod ?? null,
freeTrialPeriodAndroid: product.freeTrialPeriod ?? null,
subscriptionOfferDetailsAndroid: subscriptionOfferDetails
? stringifyJson(subscriptionOfferDetails)
: null,
subscriptionOffers: subscriptionOffers
? stringifyJson(subscriptionOffers)
: null,
productStatusAndroid: 'ok',
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The mapProduct function is missing mappings for freeTrialPeriodAndroid and subscriptionPeriodAndroid. These should be populated from the VegaProduct to maintain consistency with the Android platform expectations.

Suggested change
nameAndroid: product.title ?? sku,
subscriptionPeriodAndroid: product.subscriptionPeriod ?? null,
freeTrialPeriodAndroid: product.freeTrialPeriod ?? null,
subscriptionOfferDetailsAndroid: subscriptionOfferDetails
? stringifyJson(subscriptionOfferDetails)
: null,
subscriptionOffers: subscriptionOffers
? stringifyJson(subscriptionOffers)
: null,
productStatusAndroid: 'ok',
};
nameAndroid: product.title ?? sku,
subscriptionPeriodAndroid: product.subscriptionPeriod ?? null,
freeTrialPeriodAndroid: product.freeTrialPeriod ?? null,
subscriptionOfferDetailsAndroid: subscriptionOfferDetails
? stringifyJson(subscriptionOfferDetails)
: null,
subscriptionOffers: subscriptionOffers
? stringifyJson(subscriptionOffers)
: null,
productStatusAndroid: 'ok',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No code change needed. This mapping is already present in the PR: libraries/react-native-iap/src/vega-adapter.ts maps subscriptionPeriodAndroid and freeTrialPeriodAndroid from VegaProduct. Validated with the react-native-iap TypeScript check.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment on lines +721 to +735
currency = "",
description = description.orEmpty(),
displayName = title,
displayPrice = price.orEmpty(),
id = sku,
nameAndroid = title.orEmpty(),
platform = IapPlatform.Android,
price = null,
productStatusAndroid = ProductStatusAndroid.Ok,
subscriptionOfferDetailsAndroid = listOf(legacyOffer),
subscriptionOffers = listOf(standardizedOffer),
title = title.orEmpty(),
type = ProductType.Subs
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The toSubscriptionProduct mapping is missing freeTrialPeriodAndroid and subscriptionPeriodAndroid fields. These should be populated from the Amazon Product object using freeTrialPeriod and subscriptionPeriod respectively.

        return ProductSubscriptionAndroid(
            currency = "",
            description = description.orEmpty(),
            displayName = title,
            displayPrice = price.orEmpty(),
            id = sku,
            nameAndroid = title.orEmpty(),
            platform = IapPlatform.Android,
            price = null,
            productStatusAndroid = ProductStatusAndroid.Ok,
            subscriptionOfferDetailsAndroid = listOf(legacyOffer),
            subscriptionOffers = listOf(standardizedOffer),
            title = title.orEmpty(),
            type = ProductType.Subs,
            freeTrialPeriodAndroid = freeTrialPeriod,
            subscriptionPeriodAndroid = subscriptionPeriod
        )

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. The generated Kotlin ProductSubscriptionAndroid type does not expose freeTrialPeriodAndroid or subscriptionPeriodAndroid constructor parameters; adding them fails :openiap:compileAmazonDebugKotlin. The supported mapping here is subscriptionPeriod into PricingPhaseAndroid.billingPeriod, which is fixed in fecf2fc.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, adding the amazon Android flavor and a JavaScript runtime adapter for Vega OS. It also enables server-side receipt verification via Amazon RVS in IAPKit. Feedback identifies a critical compilation error in the Amazon native module and suggests several improvements to maintain parity with existing store implementations, such as filtering canceled receipts from purchase history, implementing network timeouts for verification requests, and accurately mapping subscription metadata like billing periods and prices.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
libraries/react-native-iap/src/types.ts (1)

1716-1724: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align Amazon IAPKit docs with userId optionality.

The docs say Amazon verification uses userId + receiptId, but userId is optional in RequestVerifyPurchaseWithIapkitAmazonProps. Please make the docs explicit that receiptId is required and userId is optional/resolvable.

💡 Suggested doc fix
- * - amazon: Verifies via Amazon Appstore RVS (userId + receiptId)
+ * - amazon: Verifies via Amazon Appstore RVS (receiptId required; userId optional)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/react-native-iap/src/types.ts` around lines 1716 - 1724, Update the
Amazon docs in the RequestVerifyPurchaseWithIapkitProps block to clarify that
for the amazon verification option (amazon:
RequestVerifyPurchaseWithIapkitAmazonProps | null) receiptId is required while
userId is optional; specifically edit the descriptive comment near
RequestVerifyPurchaseWithIapkitProps (and if present the comment inside
RequestVerifyPurchaseWithIapkitAmazonProps) to state “Amazon: Verifies via
Amazon Appstore RVS (receiptId required; userId optional/resolvable)” so the
type optionality aligns with the docs.
scripts/agent/compile-context.ts (1)

507-513: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the fetchProducts quick example request shape.

The quick reference still shows fetchProducts({ products: [...] }), which conflicts with the SDK shape used elsewhere (skus + type). This is easy to copy-paste wrong for users.

Suggested fix
 const products = await fetchProducts({
-  products: [
-    { id: 'com.app.premium', type: 'in-app' },
-    { id: 'com.app.monthly', type: 'subs' },
-  ],
+  skus: ['com.app.premium', 'com.app.monthly'],
+  type: 'all',
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/agent/compile-context.ts` around lines 507 - 513, Update the example
call to match the SDK shape used elsewhere: replace the object param that uses
products: [...] with skus: [...] and move type to the top-level param.
Concretely, change the fetchProducts call (fetchProducts) so it passes skus (an
array of product IDs) and a top-level type field (e.g., type: 'in-app' or
'subs') instead of per-item type entries.
libraries/react-native-iap/plugin/src/withIAP.ts (1)

82-90: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when OpenIAP version resolution fails.

Returning the original build.gradle here silently skips the OpenIAP dependency injection, so the real error shows up much later as a confusing Gradle/classpath failure. This should abort prebuild instead of warning and continuing.

Suggested change
   let openiapVersion: string;
   try {
     openiapVersion = loadOpenIapVersion();
   } catch (error) {
-    WarningAggregator.addWarningAndroid(
-      'react-native-iap',
-      `react-native-iap: Failed to resolve OpenIAP version (${error instanceof Error ? error.message : error})`,
-    );
-    return gradle;
+    throw new Error(
+      `react-native-iap: Failed to resolve OpenIAP version (${error instanceof Error ? error.message : error})`,
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/react-native-iap/plugin/src/withIAP.ts` around lines 82 - 90, The
current catch in the loadOpenIapVersion block silently logs a warning via
WarningAggregator.addWarningAndroid and returns gradle, which lets prebuild
continue and delays the real failure; instead fail fast by throwing a new Error
(including the original error message/details) from the catch so prebuild aborts
immediately; update the catch that surrounds loadOpenIapVersion (variable
openiapVersion) to throw a descriptive Error rather than returning the original
gradle.
🧹 Nitpick comments (6)
libraries/expo-iap/src/amazon-devices-kepler.d.ts (1)

2-11: ⚡ Quick win

Use named interfaces and document the exported API surface.

Please replace inline request object types with interfaces and add JSDoc on PurchasingService/methods for clearer public typings and better maintainability.

Proposed typing refactor
 declare module '`@amazon-devices/keplerscript-appstore-iap-lib`' {
+  export interface GetProductDataRequest {
+    skus: string[];
+  }
+
+  export interface GetPurchaseUpdatesRequest {
+    reset: boolean;
+  }
+
+  export interface NotifyFulfillmentRequest {
+    fulfillmentResult: number;
+    receiptId: string;
+  }
+
+  /**
+   * Amazon Kepler purchasing bridge.
+   */
   export const PurchasingService: {
-    getProductData(request: {skus: string[]}): Promise<unknown>;
-    getPurchaseUpdates(request: {reset: boolean}): Promise<unknown>;
+    getProductData(request: GetProductDataRequest): Promise<unknown>;
+    getPurchaseUpdates(request: GetPurchaseUpdatesRequest): Promise<unknown>;
     getUserData(request: Record<string, never>): Promise<unknown>;
-    notifyFulfillment(request: {
-      fulfillmentResult: number;
-      receiptId: string;
-    }): Promise<unknown>;
+    notifyFulfillment(request: NotifyFulfillmentRequest): Promise<unknown>;
     purchase(request: {sku: string}): Promise<unknown>;
   };
 }
As per coding guidelines, `**/*.{ts,tsx}`: "Prefer interface for defining object shapes in TypeScript" and `**/*.{ts,tsx,js,jsx}`: "Add JSDoc comments for public functions and exported APIs".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/expo-iap/src/amazon-devices-kepler.d.ts` around lines 2 - 11,
Replace the inline request object types on the exported PurchasingService
surface with named interfaces (e.g., ProductDataRequest, PurchaseUpdatesRequest,
UserDataRequest, NotifyFulfillmentRequest, PurchaseRequest) and update the
method signatures (getProductData, getPurchaseUpdates, getUserData,
notifyFulfillment, purchase) to reference those interfaces; add concise JSDoc
comments above the PurchasingService export and each method describing
parameters and return values so the public API is documented and follows the
project's "prefer interface" and JSDoc guidelines.
libraries/react-native-iap/src/types/amazon-devices-kepler/index.d.ts (1)

2-11: ⚡ Quick win

Promote inline request shapes to interfaces and document the public declaration.

For this exported TS API, named interfaces + brief JSDoc will make the contract easier to reuse and keep in sync across packages.

As per coding guidelines, **/*.{ts,tsx}: "Prefer interface for defining object shapes in TypeScript" and **/*.{ts,tsx,js,jsx}: "Add JSDoc comments for public functions and exported APIs".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/react-native-iap/src/types/amazon-devices-kepler/index.d.ts` around
lines 2 - 11, Replace the inline request object shapes with exported interfaces
and add JSDoc to the public declarations: create interfaces like
GetProductDataRequest, GetPurchaseUpdatesRequest, GetUserDataRequest,
NotifyFulfillmentRequest, and PurchaseRequest and use them in the exported
PurchasingService type; add JSDoc comments above each interface and above the
PurchasingService export (and its methods: getProductData, getPurchaseUpdates,
getUserData, notifyFulfillment, purchase) describing parameters and return types
so the API contract is documented and reusable across packages.
libraries/expo-iap/src/vega.ts (1)

4-10: ⚡ Quick win

Add JSDoc for exported Vega helpers.

Both exported functions are public API surface and should be documented inline.

Proposed update
 export const isVegaOS = (): boolean => {
   return String(Platform.OS).toLowerCase() === 'kepler';
 };

+/**
+ * Returns true when running on Vega/Kepler runtime.
+ */
 export const isVegaOS = (): boolean => {
   return String(Platform.OS).toLowerCase() === 'kepler';
 };

+/**
+ * Returns the Vega IAP module when available.
+ * Currently returns null until native Vega module wiring is enabled.
+ */
 export const getVegaIapModule = (): ExpoIapVegaModule | null => {
   return null;
 };
As per coding guidelines: "Add JSDoc comments for public functions and exported APIs".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/expo-iap/src/vega.ts` around lines 4 - 10, Add JSDoc comments for
the public exported helpers isVegaOS and getVegaIapModule: describe what each
function does, its return type, and any important behavior (e.g., isVegaOS
returns true when Platform.OS equals 'kepler' and getVegaIapModule returns an
ExpoIapVegaModule or null), include `@returns` tags and any relevant `@example` or
`@public` annotations per project style; place the JSDoc immediately above each
exported function declaration (isVegaOS and getVegaIapModule).
libraries/react-native-iap/src/vega.kepler.ts (1)

9-21: ⚡ Quick win

Add JSDoc to exported Vega helpers.

Both exported functions are public API surface and should be documented to match repository standards.

♻️ Suggested update
 export const isVegaOS = (): boolean => {
   return String(Platform.OS).toLowerCase() === 'kepler';
 };
 
+/**
+ * Returns the Vega/Kepler-backed IAP module when running on Vega OS.
+ * Returns null on non-Vega platforms.
+ */
 export const getVegaIapModule = (): RnIap | null => {
   if (!isVegaOS()) return null;
   if (!cachedVegaModule) {
     cachedVegaModule = createVegaIapModule(
       PurchasingService as unknown as VegaPurchasingService,
     );
   }
   return cachedVegaModule;
 };

As per coding guidelines: **/*.{ts,tsx,js,jsx}: Add JSDoc comments for public functions and exported APIs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/react-native-iap/src/vega.kepler.ts` around lines 9 - 21, Add JSDoc
comments for the two exported Vega helpers: document isVegaOS() (what it checks
and return value) and getVegaIapModule() (its behavior: returns null on
non-Vega/Kepler OS, lazy-initializes cachedVegaModule via createVegaIapModule
with PurchasingService cast to VegaPurchasingService, and the return type RnIap
| null). Place the JSDoc directly above each exported function, include
parameter/return descriptions and any side-effects (lazy initialization of
cachedVegaModule) to satisfy the repository's public API documentation standard.
libraries/expo-iap/plugin/src/withLocalOpenIAP.ts (1)

18-24: 🏗️ Heavy lift

Align LocalPathOption platform field names with repo suffix conventions.

ios/android keys in an exported API diverge from the platform naming convention used in this package (IOS/Android suffixes). Consider renaming to non-breaking transitional fields (e.g., support both old and new keys) before fully switching.

As per coding guidelines libraries/expo-iap/**/*.{ts,tsx,js,jsx,swift,kt}: iOS-related fields should use IOS suffix ... Android-related fields should use Android suffix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/expo-iap/plugin/src/withLocalOpenIAP.ts` around lines 18 - 24, The
exported LocalPathOption type and getAndroidLocalPathInput use lowercase keys
(ios/android) that conflict with the repo's IOS/Android suffix convention;
update LocalPathOption to accept both legacy lowercase keys and new suffix keys
(e.g., {IOS?: string; Android?: string; ios?: string; android?: string}) and
modify getAndroidLocalPathInput to prefer the new Android field but fall back to
raw.android for backward compatibility, referencing the LocalPathOption type and
getAndroidLocalPathInput function so callers are not broken while transitioning
to the new naming convention.
packages/kit/convex/purchases/amazon.ts (1)

70-109: ⚡ Quick win

Add JSDoc to exported APIs in this new module.

buildAmazonRemoteId, mapAmazonReceiptState, parseAmazonReceiptResponse, and verifyAmazonReceiptInternalV1 should carry public API docs.

As per coding guidelines: **/*.{ts,tsx,js,jsx}: Add JSDoc comments for public functions and exported APIs.

Also applies to: 119-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/kit/convex/purchases/amazon.ts` around lines 70 - 109, Add JSDoc
comments to all exported APIs in this module: buildAmazonRemoteId,
mapAmazonReceiptState, parseAmazonReceiptResponse, and
verifyAmazonReceiptInternalV1; for each exported function include a one-line
description, `@param` tags describing each argument (including types/shape where
applicable, e.g., AmazonReceiptData or raw unknown), and an `@returns` tag
describing the return value (e.g., string, HarmonizedPurchaseState,
AmazonReceiptData). Place the JSDoc immediately above each function declaration
so they satisfy the project linting rule for public APIs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release-google.yml:
- Around line 293-304: The current step id check_amazon uses body grep to detect
artifact existence and can misreport; replace that logic to perform an HTTP
status check for the artifact URL built from VERSION
(steps.version.outputs.version) instead. Use curl to fetch only headers/status
(e.g., -I or -o /dev/null -w "%{http_code}") and test for a 200 status to set
exists=true, otherwise set exists=false; keep the same echo outputs and messages
but base the decision strictly on the HTTP status code rather than grepping
response body.

In `@libraries/expo-iap/src/index.ts`:
- Around line 271-273: The platform guard is inconsistent: create/use the
Vega-capable guard (the existing isAndroidStoreRuntime helper) for the remaining
public APIs so Vega runtimes don't hit "Unsupported platform". Replace calls to
isStorePlatform() in getActiveSubscriptions, hasActiveSubscriptions, and
verifyPurchaseWithProvider with the Vega-aware predicate (or refactor
isAndroidStoreRuntime to a more descriptive name like isStoreRuntime and use
that) so those methods permit Vega alongside Android/iOS; ensure the exact
function names (getActiveSubscriptions, hasActiveSubscriptions,
verifyPurchaseWithProvider, and isAndroidStoreRuntime/isStoreRuntime) are
updated accordingly.

In `@libraries/expo-iap/src/vega-adapter.ts`:
- Around line 608-614: The code calls amazon.receiptId.trim() without checking
that amazon.receiptId exists or is a string, which can cause a TypeError; update
the guard around receiptId in the vega-adapter logic to first verify
amazon?.receiptId is a non-empty string (or coerce safely) before calling
.trim(), and if it's missing/empty throw the existing
createVegaError(ErrorCode.DeveloperError, 'Amazon Vega IAPKit verification
requires amazon.receiptId.') as intended; locate the check around the receiptId
variable (the amazon.receiptId usage) and change it to validate existence/type
prior to trimming.
- Around line 697-701: In endConnection(), do not clear the shared listeners map
so subscriptions survive reconnects: remove the call to
listenersByEventName.clear() and keep productTypesBySku.clear() and
cachedUserData = null as-is; ensure the async endConnection() method retains
listenersByEventName so previously registered handlers (e.g., for
"purchase-updated"/"purchase-error") continue to receive events after
reconnecting.

In `@libraries/expo-iap/src/vega.kepler.ts`:
- Around line 12-24: Add concise JSDoc comments for the exported helpers
isVegaOS and getVegaIapModule: document what each function does, its parameters
(none), and its return contract (isVegaOS returns boolean indicating whether
Platform.OS equals 'kepler'; getVegaIapModule returns an ExpoIapVegaModule or
null and note that it lazily initializes cachedVegaModule via
createExpoIapVegaModule(PurchasingService as VegaPurchasingService)). Place the
JSDoc immediately above each exported function so tooling and consumers can pick
up the documentation.

In `@libraries/flutter_inapp_purchase/example/README.md`:
- Around line 72-76: The fenced code block under the list item "3. **Test with
Amazon App Tester**" violates MD031 by starting immediately after text; fix it
by adding a blank line before the opening ```bash fence and a blank line after
the closing ``` so the code block is separated from the list text; update the
README.md section containing that fenced block accordingly.

In `@libraries/react-native-iap/src/vega-adapter.ts`:
- Around line 656-673: The fetch to IAPKIT_VERIFY_URL (the response variable
assignment) currently has no timeout; wrap the fetch calls (both the POST at the
IAPKIT_VERIFY_URL and the other fetch at lines ~681-689) with an
AbortController: create a controller, set a setTimeout to call
controller.abort() after a reasonable timeout (e.g. 5–10s), pass
controller.signal into fetch options, and clearTimeout on success; update the
catch to handle aborts (detect AbortError or error.name === 'AbortError') and
return/throw a clear timeout-specific error for the calling functions (keep
existing error flow otherwise). Ensure you reference and use the
controller.signal in the fetch call and clear the timer after the response is
received.
- Around line 451-463: The pagination loop that calls service.getPurchaseUpdates
(the do/while using hasMore, receipts, reset, cachedUserData) must be bounded to
avoid infinite looping on bad backend responses; add a max-iteration guard
(e.g., MAX_PAGES constant) and a counter incremented each loop, and if the
counter exceeds the max, log/throw a clear error or break the loop to stop
fetching and prevent unbounded memory growth. Ensure the guard is checked inside
the do/while around hasMore so getPurchaseUpdates, receipts.push, and
cachedUserData assignments remain unchanged except for the added counter check
and error handling.

In `@openiap-versions.json`:
- Line 3: Revert the manual edit to the root version manifest: remove the direct
change to the "google" entry in openiap-versions.json and restore the file to
the state from the base branch (undo the commit or reset that file in this
branch); do not include any manual version bumps in this PR—ensure the CI/CD
versioning workflow is responsible for updating the "google" (and other) keys so
future changes come from the automated release pipeline rather than edits to
openiap-versions.json.

In `@packages/docs/src/pages/docs/setup/expo.tsx`:
- Around line 353-361: The docs incorrectly label modules.vega as type "runtime"
while the example config uses a boolean ("vega": false); change the type
label/description for modules.vega to "boolean" (or "boolean — enable/disable")
and update the explanatory text to state that modules.vega is a boolean flag
that enables Vega OS runtime support (it does not select an Android flavor) and
point readers to the Vega OS Runtime guide; ensure references to modules.vega
and the example key "vega" remain consistent.

In `@packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt`:
- Around line 4761-4766: The fromJson factory for
RequestVerifyPurchaseWithIapkitAmazonProps currently only rejects when receiptId
is missing but should also require userId; update the fromJson function in
RequestVerifyPurchaseWithIapkitAmazonProps to check that both receiptId and
userId are non-null and return null if either is missing, and make the same
change for the duplicate parsing implementation referenced around the other
occurrence (the second RequestVerifyPurchaseWithIapkitAmazonProps.fromJson) so
Amazon verification always enforces "userId + receiptId".

In `@packages/gql/src/type.graphql`:
- Around line 316-320: The schema declares input
RequestVerifyPurchaseWithIapkitAmazonProps.userId as nullable but the contract
requires userId + receiptId; change the field declaration on input
RequestVerifyPurchaseWithIapkitAmazonProps from "userId: String" to "userId:
String!" (non-nullable), update the input description text if needed to reflect
the requirement, apply the same non-null change to the other Amazon IAPKit
inputs referenced around lines 336-355, and then regenerate downstream GraphQL
bindings/contracts after the schema change.

In `@packages/kit/convex/projects/mutation.ts`:
- Around line 304-305: The amazonSharedSecret field currently only accepts
strings and cannot be cleared; update the mutation/schema and normalization to
allow clearing by accepting null (and/or a dedicated clear flag) so clients can
unset stored credentials. Specifically, modify the v.optional(v.string()) for
amazonSharedSecret to accept null (or add a boolean clearAmazonSharedSecret in
the same input type) and update normalizeAmazonSharedSecret() to treat null or
the clear flag as an instruction to return null (and accept "" if you prefer)
instead of rejecting it; apply the same change to the other occurrence
referenced around the normalize function (the block at ~395-399) so both write
paths can unset the stored secret.

In `@packages/kit/convex/purchases/amazon.test.ts`:
- Around line 54-64: Wrap the Date.now spy cleanup in a try/finally so
restoration always runs: after you call vi.spyOn(Date, "now") (the spy used for
the mapAmazonReceiptState test asserting HarmonizedPurchaseState.EXPIRED), put
the expect(...) inside a try block and call vi.restoreAllMocks() in the finally
block (or call spy.mockRestore() there) to ensure the Date.now spy is restored
even if the assertion throws.

In `@packages/kit/convex/purchases/amazon.ts`:
- Around line 109-117: The parseAmazonReceiptResponse currently throws on
invalid shape which can bypass saveReceiptInternal and lose audit logs; change
the flow so parsing/mapping errors are caught and routed through the same
persistence path: wrap calls to parseAmazonReceiptResponse (and the similar
mapping at lines ~242-259) in a try/catch that on error creates a structured
"invalid response" record (including the raw payload and the parse error
message/stack) and calls saveReceiptInternal with that record instead of letting
the exception bubble; ensure function names mentioned
(parseAmazonReceiptResponse and saveReceiptInternal) are used so the catch
persists audit data for both successful and failed parses.
- Around line 159-164: The fetch inside the async callback passed to
retryOnTransient (the block that assigns parsedBody) must use an AbortController
with a timeout to avoid hanging; create an AbortController, start a setTimeout
to call controller.abort() after a sensible timeout (e.g., 5–15s), pass
controller.signal to fetch, and ensure you clear the timeout when fetch
completes (both on success and in finally) so the timer is cleaned up; make sure
the abort error is allowed to propagate so retryOnTransient can retry/handle it.

In `@packages/kit/src/pages/auth/organization/project/settings.tsx`:
- Around line 1875-1919: The Amazon secret input currently lives inside the
outer metadata form so pressing Enter triggers handleMetadataSubmit() instead of
handleAmazonSubmit(); fix by isolating the Amazon controls into their own form:
wrap the amazonSharedSecret <input> and the Save Amazon config <button> in a
<form onSubmit={(e)=>{e.preventDefault(); void handleAmazonSubmit();}}> and
change the button to type="submit" (remove the onClick). Alternatively, if you
prefer a smaller change, add an onKeyDown handler to the amazonSharedSecret
input that intercepts Enter (e.key === "Enter") and calls e.preventDefault();
void handleAmazonSubmit();; reference amazonSharedSecret input,
handleAmazonSubmit, handleMetadataSubmit, and disableSaveAmazon when making the
change.

In `@packages/kit/src/pages/blog/iapkit-joins-openiap.tsx`:
- Around line 31-32: Run Prettier on the affected file to fix CI formatting
failures: execute bunx prettier --write src/pages/blog/iapkit-joins-openiap.tsx
from the repo root, then stage and commit the changes; ensure the formatting
fixes the string "IAPKit validates receipts for Apple App Store, Google Play,
and Meta Horizon. Fire OS and Vega OS receipt-validation support is on the
roadmap." and the other nearby blocks flagged by CI so the Prettier check
passes.

In `@packages/kit/src/pages/docs/sections/api.tsx`:
- Around line 109-113: The documented 200 success payload in the API docs
includes a "store" field but the actual verify handler returns only { isValid,
state }; update the JSON example in packages/kit/src/pages/docs/sections/api.tsx
to match the actual response (remove "store" and show only "isValid" and
"state"), or alternatively update the verify handler to return the "store"
property if that field is intended to be part of the API contract—make the docs
and the verify function signature (verify handler) consistent.

In `@packages/kit/src/pages/docs/sections/quickstart.tsx`:
- Around line 159-170: Update the intro sentence in the Quickstart docs to
reflect the added Amazon example (four stores) by changing the phrase that
currently reads “three stores” to “four stores” in the quickstart content;
locate the text inside the Quickstart component in
packages/kit/src/pages/docs/sections/quickstart.tsx (near the CodeBlock
components and the sentence introducing available store examples) and update
that string so the documentation and the new Amazon CodeBlock example remain
consistent.

---

Outside diff comments:
In `@libraries/react-native-iap/plugin/src/withIAP.ts`:
- Around line 82-90: The current catch in the loadOpenIapVersion block silently
logs a warning via WarningAggregator.addWarningAndroid and returns gradle, which
lets prebuild continue and delays the real failure; instead fail fast by
throwing a new Error (including the original error message/details) from the
catch so prebuild aborts immediately; update the catch that surrounds
loadOpenIapVersion (variable openiapVersion) to throw a descriptive Error rather
than returning the original gradle.

In `@libraries/react-native-iap/src/types.ts`:
- Around line 1716-1724: Update the Amazon docs in the
RequestVerifyPurchaseWithIapkitProps block to clarify that for the amazon
verification option (amazon: RequestVerifyPurchaseWithIapkitAmazonProps | null)
receiptId is required while userId is optional; specifically edit the
descriptive comment near RequestVerifyPurchaseWithIapkitProps (and if present
the comment inside RequestVerifyPurchaseWithIapkitAmazonProps) to state “Amazon:
Verifies via Amazon Appstore RVS (receiptId required; userId
optional/resolvable)” so the type optionality aligns with the docs.

In `@scripts/agent/compile-context.ts`:
- Around line 507-513: Update the example call to match the SDK shape used
elsewhere: replace the object param that uses products: [...] with skus: [...]
and move type to the top-level param. Concretely, change the fetchProducts call
(fetchProducts) so it passes skus (an array of product IDs) and a top-level type
field (e.g., type: 'in-app' or 'subs') instead of per-item type entries.

---

Nitpick comments:
In `@libraries/expo-iap/plugin/src/withLocalOpenIAP.ts`:
- Around line 18-24: The exported LocalPathOption type and
getAndroidLocalPathInput use lowercase keys (ios/android) that conflict with the
repo's IOS/Android suffix convention; update LocalPathOption to accept both
legacy lowercase keys and new suffix keys (e.g., {IOS?: string; Android?:
string; ios?: string; android?: string}) and modify getAndroidLocalPathInput to
prefer the new Android field but fall back to raw.android for backward
compatibility, referencing the LocalPathOption type and getAndroidLocalPathInput
function so callers are not broken while transitioning to the new naming
convention.

In `@libraries/expo-iap/src/amazon-devices-kepler.d.ts`:
- Around line 2-11: Replace the inline request object types on the exported
PurchasingService surface with named interfaces (e.g., ProductDataRequest,
PurchaseUpdatesRequest, UserDataRequest, NotifyFulfillmentRequest,
PurchaseRequest) and update the method signatures (getProductData,
getPurchaseUpdates, getUserData, notifyFulfillment, purchase) to reference those
interfaces; add concise JSDoc comments above the PurchasingService export and
each method describing parameters and return values so the public API is
documented and follows the project's "prefer interface" and JSDoc guidelines.

In `@libraries/expo-iap/src/vega.ts`:
- Around line 4-10: Add JSDoc comments for the public exported helpers isVegaOS
and getVegaIapModule: describe what each function does, its return type, and any
important behavior (e.g., isVegaOS returns true when Platform.OS equals 'kepler'
and getVegaIapModule returns an ExpoIapVegaModule or null), include `@returns`
tags and any relevant `@example` or `@public` annotations per project style; place
the JSDoc immediately above each exported function declaration (isVegaOS and
getVegaIapModule).

In `@libraries/react-native-iap/src/types/amazon-devices-kepler/index.d.ts`:
- Around line 2-11: Replace the inline request object shapes with exported
interfaces and add JSDoc to the public declarations: create interfaces like
GetProductDataRequest, GetPurchaseUpdatesRequest, GetUserDataRequest,
NotifyFulfillmentRequest, and PurchaseRequest and use them in the exported
PurchasingService type; add JSDoc comments above each interface and above the
PurchasingService export (and its methods: getProductData, getPurchaseUpdates,
getUserData, notifyFulfillment, purchase) describing parameters and return types
so the API contract is documented and reusable across packages.

In `@libraries/react-native-iap/src/vega.kepler.ts`:
- Around line 9-21: Add JSDoc comments for the two exported Vega helpers:
document isVegaOS() (what it checks and return value) and getVegaIapModule()
(its behavior: returns null on non-Vega/Kepler OS, lazy-initializes
cachedVegaModule via createVegaIapModule with PurchasingService cast to
VegaPurchasingService, and the return type RnIap | null). Place the JSDoc
directly above each exported function, include parameter/return descriptions and
any side-effects (lazy initialization of cachedVegaModule) to satisfy the
repository's public API documentation standard.

In `@packages/kit/convex/purchases/amazon.ts`:
- Around line 70-109: Add JSDoc comments to all exported APIs in this module:
buildAmazonRemoteId, mapAmazonReceiptState, parseAmazonReceiptResponse, and
verifyAmazonReceiptInternalV1; for each exported function include a one-line
description, `@param` tags describing each argument (including types/shape where
applicable, e.g., AmazonReceiptData or raw unknown), and an `@returns` tag
describing the return value (e.g., string, HarmonizedPurchaseState,
AmazonReceiptData). Place the JSDoc immediately above each function declaration
so they satisfy the project linting rule for public APIs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e51a400-2fa4-4d97-a0e5-d50dcde40955

📥 Commits

Reviewing files that changed from the base of the PR and between 1042f0b and a82f3f7.

⛔ Files ignored due to path filters (10)
  • libraries/expo-iap/bun.lock is excluded by !**/*.lock
  • libraries/react-native-iap/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • packages/docs/src/generated/version-metadata.json is excluded by !**/generated/**
  • packages/gql/src/generated/Types.cs is excluded by !**/generated/**
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.gd is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
  • packages/kit/convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (134)
  • .github/workflows/ci.yml
  • .github/workflows/release-google.yml
  • knowledge/_claude-context/context.md
  • knowledge/internal/03-coding-style.md
  • knowledge/internal/06-git-deployment.md
  • libraries/expo-iap/.npmignore
  • libraries/expo-iap/android/build.gradle
  • libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapLog.kt
  • libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapModule.kt
  • libraries/expo-iap/example/app.config.ts
  • libraries/expo-iap/ios/ExpoIapLog.swift
  • libraries/expo-iap/ios/ExpoIapModule.swift
  • libraries/expo-iap/package.json
  • libraries/expo-iap/plugin/__tests__/withIAP.test.ts
  • libraries/expo-iap/plugin/src/expoConfig.augmentation.d.ts
  • libraries/expo-iap/plugin/src/withIAP.ts
  • libraries/expo-iap/plugin/src/withLocalOpenIAP.ts
  • libraries/expo-iap/src/ExpoIapModule.ts
  • libraries/expo-iap/src/__tests__/native-log-redaction.test.js
  • libraries/expo-iap/src/__tests__/vega-adapter.test.ts
  • libraries/expo-iap/src/amazon-devices-kepler.d.ts
  • libraries/expo-iap/src/index.ts
  • libraries/expo-iap/src/types.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/expo-iap/src/vega.kepler.ts
  • libraries/expo-iap/src/vega.ts
  • libraries/flutter_inapp_purchase/android/build.gradle
  • libraries/flutter_inapp_purchase/android/settings.gradle
  • libraries/flutter_inapp_purchase/android/src/main/kotlin/io/github/hyochan/flutter_inapp_purchase/AndroidInappPurchasePlugin.kt
  • libraries/flutter_inapp_purchase/android/src/main/kotlin/io/github/hyochan/flutter_inapp_purchase/FlutterInappPurchasePlugin.kt
  • libraries/flutter_inapp_purchase/example/README.md
  • libraries/flutter_inapp_purchase/example/android/app/build.gradle
  • libraries/flutter_inapp_purchase/example/android/gradle.properties
  • libraries/flutter_inapp_purchase/example/android/settings.gradle
  • libraries/flutter_inapp_purchase/lib/types.dart
  • libraries/flutter_inapp_purchase/pubspec.yaml
  • libraries/godot-iap/addons/godot-iap/plugin.cfg
  • libraries/godot-iap/addons/godot-iap/types.gd
  • libraries/kmp-iap/gradle.properties
  • libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt
  • libraries/maui-iap/src/OpenIap.Maui/OpenIap.Maui.csproj
  • libraries/maui-iap/src/OpenIap.Maui/Types.cs
  • libraries/react-native-iap/README.md
  • libraries/react-native-iap/android/build.gradle
  • libraries/react-native-iap/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • libraries/react-native-iap/example/android/app/build.gradle
  • libraries/react-native-iap/example/android/gradle.properties
  • libraries/react-native-iap/example/android/settings.gradle
  • libraries/react-native-iap/package.json
  • libraries/react-native-iap/plugin/__tests__/withIAP-android.test.ts
  • libraries/react-native-iap/plugin/__tests__/withIAP-ios.test.ts
  • libraries/react-native-iap/plugin/src/withIAP.ts
  • libraries/react-native-iap/src/__tests__/index.test.ts
  • libraries/react-native-iap/src/__tests__/utils/type-bridge.test.ts
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/index.ts
  • libraries/react-native-iap/src/specs/RnIap.nitro.ts
  • libraries/react-native-iap/src/types.ts
  • libraries/react-native-iap/src/types/amazon-devices-kepler/index.d.ts
  • libraries/react-native-iap/src/utils/type-bridge.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • libraries/react-native-iap/src/vega.kepler.ts
  • libraries/react-native-iap/src/vega.ts
  • llms-full.txt
  • llms.txt
  • openiap-versions.json
  • packages/apple/Sources/Models/Types.swift
  • packages/apple/Sources/OpenIapModule.swift
  • packages/docs/openiap-versions.json
  • packages/docs/public/llms-full.txt
  • packages/docs/public/llms.txt
  • packages/docs/src/pages/docs/android-setup.tsx
  • packages/docs/src/pages/docs/ecosystem.tsx
  • packages/docs/src/pages/docs/features/alternative-marketplace/onside.tsx
  • packages/docs/src/pages/docs/features/offer-code-redemption.tsx
  • packages/docs/src/pages/docs/features/runtime-integrations.tsx
  • packages/docs/src/pages/docs/features/validation.tsx
  • packages/docs/src/pages/docs/features/vega-os.tsx
  • packages/docs/src/pages/docs/fireos-setup.tsx
  • packages/docs/src/pages/docs/index.tsx
  • packages/docs/src/pages/docs/setup/expo.tsx
  • packages/docs/src/pages/docs/setup/flutter.tsx
  • packages/docs/src/pages/docs/setup/index.tsx
  • packages/docs/src/pages/docs/setup/react-native.tsx
  • packages/docs/src/pages/docs/updates/releases.tsx
  • packages/docs/vite.config.ts
  • packages/google/CONTRIBUTING.md
  • packages/google/CONVENTION.md
  • packages/google/Example/build.gradle.kts
  • packages/google/openiap/build.gradle.kts
  • packages/google/openiap/src/amazon/AndroidManifest.xml
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapErrorExtensions.kt
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/store/OpenIapStoreExtensions.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/FetchProductsAmazonTest.kt
  • packages/google/openiap/src/testAmazon/java/dev/hyo/openiap/SubscriptionBillingIssueAmazonTest.kt
  • packages/google/package.json
  • packages/gql/codegen/core/schema-linter.ts
  • packages/gql/src/type.graphql
  • packages/kit/convex/analytics/action.ts
  • packages/kit/convex/projects/mutation.ts
  • packages/kit/convex/projects/query.ts
  • packages/kit/convex/projects/setupStatus.ts
  • packages/kit/convex/purchases/action.ts
  • packages/kit/convex/purchases/amazon.test.ts
  • packages/kit/convex/purchases/amazon.ts
  • packages/kit/convex/purchases/errors.ts
  • packages/kit/convex/purchases/extract-product-id.test.ts
  • packages/kit/convex/purchases/shared.ts
  • packages/kit/convex/schema.ts
  • packages/kit/server/api/v1/replay-guard.test.ts
  • packages/kit/server/api/v1/replay-guard.ts
  • packages/kit/server/api/v1/request-logger.test.ts
  • packages/kit/server/api/v1/request-logger.ts
  • packages/kit/server/api/v1/route-input-schemas.test.ts
  • packages/kit/server/api/v1/route-input-schemas.ts
  • packages/kit/server/api/v1/routes.ts
  • packages/kit/src/pages/auth/organization/project/PurchasesTable.tsx
  • packages/kit/src/pages/auth/organization/project/analytics.tsx
  • packages/kit/src/pages/auth/organization/project/products.tsx
  • packages/kit/src/pages/auth/organization/project/purchase-detail.tsx
  • packages/kit/src/pages/auth/organization/project/purchases.tsx
  • packages/kit/src/pages/auth/organization/project/settings.tsx
  • packages/kit/src/pages/auth/organization/project/subscriptions.tsx
  • packages/kit/src/pages/auth/organization/project/webhooks.tsx
  • packages/kit/src/pages/blog/iapkit-joins-openiap.tsx
  • packages/kit/src/pages/docs/sections/api.tsx
  • packages/kit/src/pages/docs/sections/quickstart.tsx
  • scripts/agent/compile-context.ts

Comment thread .github/workflows/release-google.yml
Comment thread libraries/expo-iap/src/index.ts
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts
Comment thread libraries/expo-iap/src/vega.kepler.ts
Comment thread packages/kit/convex/purchases/amazon.ts Outdated
Comment thread packages/kit/src/pages/auth/organization/project/settings.tsx
Comment thread packages/kit/src/pages/blog/iapkit-joins-openiap.tsx Outdated
Comment thread packages/kit/src/pages/docs/sections/api.tsx
Comment thread packages/kit/src/pages/docs/sections/quickstart.tsx

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including native Android, React Native, Expo, Flutter, and other framework libraries. Key implementations include a new amazon Gradle flavor for Fire OS distribution, server-side receipt verification via Amazon RVS in IAPKit, and runtime JavaScript adapters for Vega OS. The changes also encompass updated documentation, dashboard UI enhancements, and improved sensitive data redaction in logs. Feedback from the review identifies a potential SyntaxError when parsing empty responses in the Amazon verification logic and a race condition in the Amazon module's asynchronous response handling that could lead to missed callbacks.

Comment thread packages/kit/convex/purchases/amazon.ts Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including native Android, React Native, Expo, and Flutter. It introduces the amazon Gradle flavor, a JavaScript runtime adapter for Vega OS, and server-side receipt verification via Amazon RVS. Reviewers identified a critical logic error in subscription expiration for Amazon RVS, brittle price parsing in the Android module, and a potential stack overflow vulnerability in the Vega adapter's error extraction. Further improvements are needed to map the Bi-Monthly subscription period and to adhere to naming conventions in the native Android package by removing redundant Android suffixes from implementation functions.

Comment thread packages/kit/convex/purchases/amazon.ts
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts
Comment thread libraries/react-native-iap/src/vega-adapter.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/kit/convex/purchases/amazon.ts (1)

218-263: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t swallow res.text() read failures—rethrow so retryOnTransient can retry.

packages/kit/convex/purchases/amazon.ts line 230 turns res.text() errors into bodyText = ""; parseAmazonJsonBody then throws “empty body”, and retryOnTransient doesn’t retry because shouldRetry only matches abort/HTTP 429/transient 5xx or network errors (the parse/verification error doesn’t satisfy those predicates). This converts recoverable stream/transport failures into immediate verification failures.

Suggested fix
-          const bodyText = await res.text().catch(() => "");
+          const bodyText = await res.text();
🧹 Nitpick comments (1)
packages/kit/server/api/v1/route-response-schemas.test.ts (1)

11-38: ⚡ Quick win

Increase positive-path coverage for all supported stores.

Only "amazon" is covered as a valid success case. Add a table-driven test for all allowed stores to prevent silent schema drift per platform.

Suggested test extension
 describe("verifyPurchaseSuccessResponseSchema", () => {
+  test.each(["apple", "google", "horizon", "amazon"] as const)(
+    "accepts successful responses for %s",
+    (store) => {
+      const result = parse({
+        store,
+        isValid: true,
+        state: "ENTITLED",
+      });
+      expect(result.success).toBe(true);
+    },
+  );
+
   test("requires the verified store in successful responses", () => {

As per coding guidelines: **/*.test.{ts,tsx,js,jsx}: Write unit tests for all utility functions and business logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/kit/server/api/v1/route-response-schemas.test.ts` around lines 11 -
38, Add a table-driven test that asserts parse(...) returns success true for
every allowed store instead of only "amazon": create a new test (e.g., "accepts
all supported stores") which iterates the list of permitted store identifiers
used by the schema, calls parse({ store, isValid: true, state: "ENTITLED" }) for
each, and expects result.success toBe(true); keep the existing negative tests
unchanged. Ensure you reference the same parse function used in the file and
include every store identifier defined by the route-response schema so future
schema drift is caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 754-767: toPriceAmount() currently treats comma as decimal only
when dot is absent, which fails for formats like "€1.234,56"; update
String?.toPriceAmount to detect the decimal separator by position: if both '.'
and ',' exist, treat the rightmost occurrence as the decimal separator and
remove all other grouping separators before replacing the decimal with '.'; if
only one of them exists, treat ',' as decimal if no '.', otherwise treat '.' as
decimal and strip grouping; then parse with toDoubleOrNull() as before. Ensure
this logic is implemented inside the existing toPriceAmount function.

---

Nitpick comments:
In `@packages/kit/server/api/v1/route-response-schemas.test.ts`:
- Around line 11-38: Add a table-driven test that asserts parse(...) returns
success true for every allowed store instead of only "amazon": create a new test
(e.g., "accepts all supported stores") which iterates the list of permitted
store identifiers used by the schema, calls parse({ store, isValid: true, state:
"ENTITLED" }) for each, and expects result.success toBe(true); keep the existing
negative tests unchanged. Ensure you reference the same parse function used in
the file and include every store identifier defined by the route-response schema
so future schema drift is caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1c4a6cd-cf28-43eb-a21d-e6a45aeeb6ea

📥 Commits

Reviewing files that changed from the base of the PR and between fecf2fc and bb84885.

📒 Files selected for processing (17)
  • .github/workflows/release-google.yml
  • libraries/expo-iap/src/__tests__/vega-adapter.test.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/godot-iap/addons/godot-iap/android/GodotIap.gdap
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/kit/convex/purchases/amazon.test.ts
  • packages/kit/convex/purchases/amazon.ts
  • packages/kit/convex/purchases/errors.ts
  • packages/kit/server/api/v1/replay-guard.ts
  • packages/kit/server/api/v1/request-logger.test.ts
  • packages/kit/server/api/v1/route-input-schemas.ts
  • packages/kit/server/api/v1/route-response-schemas.test.ts
  • packages/kit/server/api/v1/route-response-schemas.ts
  • packages/kit/server/api/v1/routes.ts
  • packages/kit/src/pages/auth/organization/project/settings.tsx
  • packages/kit/src/pages/blog/iapkit-joins-openiap.tsx
  • packages/kit/src/pages/docs/sections/quickstart.tsx
✅ Files skipped from review due to trivial changes (2)
  • libraries/godot-iap/addons/godot-iap/android/GodotIap.gdap
  • packages/kit/src/pages/blog/iapkit-joins-openiap.tsx

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including the amazon Android flavor and a JavaScript runtime adapter for Kepler. The changes span all framework libraries, adding Amazon-specific types, build configurations, and IAPKit receipt verification paths. Documentation has been expanded with new setup guides and runtime integration details. Review feedback identifies a copy-paste error regarding pending purchases in the Amazon SDK, requires adherence to naming conventions in the native Android package by removing redundant suffixes, and suggests adding support for bi-weekly subscription periods.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment on lines +313 to +324
override val acknowledgePurchaseAndroid: MutationAcknowledgePurchaseAndroidHandler = { purchaseToken ->
withContext(Dispatchers.IO) {
runCatching {
ensureRegistered()
PurchasingService.notifyFulfillment(purchaseToken, FulfillmentResult.FULFILLED)
true
}.getOrElse {
OpenIapLog.w("Amazon acknowledge failed: ${it.message}", TAG)
false
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To adhere to the repository style guide, hand-written native implementations in packages/google should not have an Android suffix. The implementation logic should be moved to a suffix-free function, and the handler property should delegate to it.

    private fun acknowledgePurchase(purchaseToken: String): Boolean {
        return try {
            ensureRegistered()
            PurchasingService.notifyFulfillment(purchaseToken, FulfillmentResult.FULFILLED)
            true
        } catch (e: Exception) {
            OpenIapLog.w("Amazon acknowledge failed: ${e.message}", TAG)
            false
        }
    }

    override val acknowledgePurchaseAndroid: MutationAcknowledgePurchaseAndroidHandler = { purchaseToken ->
        withContext(Dispatchers.IO) {
            acknowledgePurchase(purchaseToken)
        }
    }
References
  1. Hand-written native functions in packages/google should not add a redundant Android suffix. (link)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. acknowledgePurchaseAndroid is a generated GraphQL handler field and must keep the schema name under the packages/google convention exception. The hand-written implementation now delegates to the suffix-free acknowledgePurchase helper.

Comment on lines +326 to +337
override val consumePurchaseAndroid: MutationConsumePurchaseAndroidHandler = { purchaseToken ->
withContext(Dispatchers.IO) {
runCatching {
ensureRegistered()
PurchasingService.notifyFulfillment(purchaseToken, FulfillmentResult.FULFILLED)
true
}.getOrElse {
OpenIapLog.w("Amazon consume failed: ${it.message}", TAG)
false
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To adhere to the repository style guide, hand-written native implementations in packages/google should not have an Android suffix. The implementation logic should be moved to a suffix-free function, and the handler property should delegate to it.

    private fun consumePurchase(purchaseToken: String): Boolean {
        return try {
            ensureRegistered()
            PurchasingService.notifyFulfillment(purchaseToken, FulfillmentResult.FULFILLED)
            true
        } catch (e: Exception) {
            OpenIapLog.w("Amazon consume failed: ${e.message}", TAG)
            false
        }
    }

    override val consumePurchaseAndroid: MutationConsumePurchaseAndroidHandler = { purchaseToken ->
        withContext(Dispatchers.IO) {
            consumePurchase(purchaseToken)
        }
    }
References
  1. Hand-written native functions in packages/google should not add a redundant Android suffix. (link)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. consumePurchaseAndroid is a generated GraphQL handler field and must keep the schema name under the packages/google convention exception. The hand-written implementation now delegates to the suffix-free consumePurchase helper.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Amazon Fire OS and Vega OS across the entire OpenIAP project, including client SDKs, documentation, and the IAPKit verification server. It introduces a new amazon Gradle flavor for Android and a runtime adapter for Vega OS. Technical feedback points out critical bugs in the native Amazon module, such as invalid property access on SDK classes that will cause compilation failures, insufficient error handling in purchase requests, and logic errors in subscription status mapping and price string parsing.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment on lines +94 to +112
export function mapAmazonReceiptState(
receipt: AmazonReceiptData,
): HarmonizedPurchaseState {
if (receipt.cancelDate !== undefined && receipt.cancelDate !== null) {
return HarmonizedPurchaseState.CANCELED;
}

const productType = receipt.productType?.toUpperCase();
switch (productType) {
case "CONSUMABLE":
return HarmonizedPurchaseState.READY_TO_CONSUME;
case "ENTITLED":
return HarmonizedPurchaseState.ENTITLED;
case "SUBSCRIPTION":
return HarmonizedPurchaseState.ENTITLED;
default:
return HarmonizedPurchaseState.UNKNOWN;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

For subscriptions, the Amazon RVS response includes a renewalDate. If the subscription has expired naturally (without an explicit cancellation that would set cancelDate), renewalDate will be in the past. The current mapping only checks cancelDate, which might lead to expired subscriptions being reported as ENTITLED. Consider checking renewalDate < Date.now() for the SUBSCRIPTION product type.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. Amazon RVS docs define cancelDate as the date a subscription expired or was canceled, and renewalDate as the next renewal date for autorenew subscriptions, null when not autorenewing. The entitlement state should therefore key off cancelDate, not renewalDate. See https://developer.amazon.com/docs/in-app-purchasing/iap-rvs-for-android-apps.html and https://developer.amazon.com/docs/in-app-purchasing/billing-compatibility-rvs-subs.html.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Appstore distribution across the OpenIAP ecosystem, including Fire OS (Android flavor) and Vega OS (JavaScript runtime). Key changes include the implementation of the amazon flavor in the native Android package using the Amazon Appstore SDK, the addition of a Vega OS runtime adapter for React Native and Expo, and the integration of Amazon Receipt Verification Service (RVS) into IAPKit. The PR also updates documentation, examples, and client libraries to support the new amazon store type and IAPKit verification path. Feedback focuses on improving the robustness of the Amazon implementation in the native Android module, specifically regarding error handling in requestPurchase, adding defensive pagination limits in requestPurchaseUpdates, and ensuring the numeric price field is correctly populated in product data.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including native Android, React Native, Expo, and Flutter libraries. Key changes include the addition of an amazon build flavor for the native Android package using the Amazon Appstore SDK, a new JavaScript runtime adapter for Vega OS (Kepler), and server-side receipt verification for Amazon RVS in IAPKit. Documentation and example apps have been updated to reflect these new store targets. Feedback on the native Amazon implementation suggests optimizing product data fetching through parallel requests and ensuring that getAvailablePurchases and receipt state mapping correctly respect provided options and deferred transaction statuses.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Amazon distribution targets, including Fire OS and Vega OS, across the OpenIAP ecosystem. Key additions include a new amazon Gradle flavor for the native Android package, a runtime-selected JavaScript adapter for Vega OS in React Native and Expo, and server-side receipt verification for Amazon RVS in IAPKit. The update also provides comprehensive documentation, configuration plugins, and type definitions to align Amazon store support with existing platforms, alongside improved sensitive key detection for log sanitization. Feedback identifies potential issues with localized string mapping for subscription periods and fragile currency parsing heuristics in the new Amazon module.

Comment on lines +782 to +795
private fun String?.toIsoBillingPeriod(): String {
val value = this?.trim().orEmpty()
if (value.isEmpty() || value.startsWith("P")) return value

return when (value.lowercase(Locale.ROOT)) {
"weekly", "week", "1 week" -> "P1W"
"biweekly", "bi-weekly", "bi weekly", "2 week", "2 weeks" -> "P2W"
"monthly", "month", "1 month" -> "P1M"
"bi-monthly", "bimonthly", "2 month", "2 months" -> "P2M"
"quarterly", "quarter", "3 months" -> "P3M"
"semiannual", "semiannually", "semi-annual", "semi-annually", "6 months" -> "P6M"
"annual", "annually", "yearly", "year", "1 year" -> "P1Y"
else -> value
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The toIsoBillingPeriod helper relies on hardcoded English strings like "weekly" or "monthly". If the Amazon Appstore SDK returns localized strings for the subscription period based on the device locale, this mapping will fail and return the raw localized string, which may not be a valid ISO 8601 duration (e.g., P1M). Please verify if the SDK guarantees English identifiers or consider a more robust mapping strategy.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. Amazon IAP Product does not expose a locale-independent billing duration in this SDK surface, so the helper normalizes known Amazon period labels and leaves unknown localized labels unchanged instead of guessing an incorrect ISO duration.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libraries/expo-iap/src/vega-adapter.ts`:
- Around line 587-589: The branch that handles record.errors currently assumes
array entries are objects and calls extractIapkitErrorMessage(errors[0], depth +
1), which fails when errors[0] is a string; update the logic in vega-adapter
around the record.errors handling to check the type of errors[0] and, if it's a
string, return it directly (or format it as the provider message) instead of
recursing, otherwise continue to call extractIapkitErrorMessage(errors[0], depth
+ 1); reference the variables record.errors, errors, and the helper
extractIapkitErrorMessage to locate where to add the type check and early
return.

In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 201-209: The filtering on includeSuspendedAndroid in
getAvailablePurchases never works because AmazonReceipt.toPurchase() currently
hardcodes PurchaseAndroid.isSuspendedAndroid = false; update
AmazonReceipt.toPurchase() to map the actual suspension state from the receipt
(e.g., receipt.suspended or equivalent field) into
PurchaseAndroid.isSuspendedAndroid so the purchases list reflects real suspended
status; also apply the same fix to the other mapping routine referenced around
the 824-845 region so both query handlers' filters behave correctly.
- Around line 605-611: The loop currently silently breaks when pageCount reaches
AMAZON_PURCHASE_UPDATES_MAX_PAGES which returns truncated purchase history;
instead, detect this condition and invoke the method's error/failure path (e.g.
reject the promise or call the existing error callback used by this API) with a
clear message (include TAG and page cap value) so callers receive an explicit
failure rather than partial results; update the block around pageCount,
AMAZON_PURCHASE_UPDATES_MAX_PAGES and OpenIapLog.w to call the function's
failure handler rather than break.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aaf044cc-3133-47ca-a928-d51a1a224cb5

📥 Commits

Reviewing files that changed from the base of the PR and between bb84885 and 72dca78.

📒 Files selected for processing (12)
  • libraries/expo-iap/src/index.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/expo-iap/src/vega.kepler.ts
  • libraries/flutter_inapp_purchase/example/README.md
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • packages/docs/src/pages/docs/setup/expo.tsx
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/kit/convex/projects/mutation.ts
  • packages/kit/convex/purchases/amazon.test.ts
  • packages/kit/convex/purchases/amazon.ts
  • packages/kit/src/pages/auth/organization/project/settings.tsx
💤 Files with no reviewable changes (1)
  • packages/kit/convex/purchases/amazon.ts

Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements Amazon Fire OS and Vega OS support across the OpenIAP ecosystem, introducing a new amazon Android flavor and a kepler runtime adapter. Feedback highlights an opportunity to optimize function allocation within Proxy traps in expo-iap, suggests hardening the currency parsing logic for specific locales, and recommends enforcing project-specific application identifiers to avoid potential data collisions.

Comment thread libraries/expo-iap/src/ExpoIapModule.ts Outdated
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread packages/kit/convex/purchases/amazon.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt (1)

826-836: 💤 Low value

Partial parse may accept trailing garbage.

position.index > 0 only checks that parsing started, not that the entire string was consumed. A malformed price like "$1.99 USD extra" could parse as 1.99 and ignore " USD extra". Since Amazon price strings are typically well-formed, this is low risk, but for defensive parsing you could add a check that position.index == value.length or that remaining characters are only whitespace.

🔧 Optional stricter check
     private fun parseLocalizedPrice(value: String): Double? {
         val locale = Locale.getDefault()
         return listOf(
             NumberFormat.getCurrencyInstance(locale),
             NumberFormat.getNumberInstance(locale)
         ).firstNotNullOfOrNull { format ->
             val position = ParsePosition(0)
             val parsed = format.parse(value, position)
-            if (parsed != null && position.index > 0) parsed.toDouble() else null
+            if (parsed != null && position.index == value.length) parsed.toDouble() else null
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`
around lines 826 - 836, The parseLocalizedPrice function currently accepts
partial parses because it only checks position.index > 0; update
parseLocalizedPrice (and its use of ParsePosition/position.index) to ensure the
parser consumed the whole input (e.g., require position.index == value.length)
or allow only trailing whitespace (e.g., check
value.substring(position.index).trim().isEmpty()), so
NumberFormat.getCurrencyInstance(Locale.getDefault()) and
NumberFormat.getNumberInstance(Locale.getDefault()) results are only returned
when the entire string is consumed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 826-836: The parseLocalizedPrice function currently accepts
partial parses because it only checks position.index > 0; update
parseLocalizedPrice (and its use of ParsePosition/position.index) to ensure the
parser consumed the whole input (e.g., require position.index == value.length)
or allow only trailing whitespace (e.g., check
value.substring(position.index).trim().isEmpty()), so
NumberFormat.getCurrencyInstance(Locale.getDefault()) and
NumberFormat.getNumberInstance(Locale.getDefault()) results are only returned
when the entire string is consumed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5cd7ba3a-fb57-4573-8c82-7f5d19212cd6

📥 Commits

Reviewing files that changed from the base of the PR and between 72dca78 and 83ca76f.

📒 Files selected for processing (5)
  • libraries/expo-iap/src/__tests__/vega-adapter.test.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, adding the amazon store flavor, Amazon Appstore SDK integration, and a runtime-selected JavaScript adapter for Vega OS. It also improves log redaction for sensitive keys and updates documentation. Feedback highlights a fragile price parsing heuristic for certain currencies and suggests expanding the list of redacted keys. Additionally, the review points out potential issues with hardcoded application names in the Expo plugin and emphasizes the importance of graceful exception handling for unsupported features on Fire OS to avoid application crashes.

Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated
Comment thread libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapLog.kt Outdated
Comment thread libraries/expo-iap/plugin/src/withIAP.ts Outdated
Comment on lines +401 to +404
// Amazon Appstore SDK does not expose a suspended-subscription event, so
// fail immediately instead of leaving consumers waiting forever.
throw OpenIapError.FeatureNotSupported()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The subscriptionBillingIssue handler is wired to throw FeatureNotSupported immediately. While this is better than suspending forever, ensure that framework wrappers (like react-native-iap or expo-iap) handle this exception gracefully so the app doesn't crash when a user registers a listener on Fire OS.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applied. Amazon does not expose a suspended-subscription listener, so throwing FeatureNotSupported immediately is the intended graceful failure path; wrappers receive the exception instead of hanging on a listener stream.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Amazon Fire OS and Vega OS to the OpenIAP project, introducing new Android flavors, runtime adapters for React Native and Expo, and server-side receipt verification via Amazon RVS. Feedback highlights that the Vega adapter for react-native-iap is incomplete and missing several interface methods, which could lead to runtime crashes. Furthermore, a bug was identified in the subscription detection logic where an array check is performed on a JSON string, and the use of stringified JSON for error messages is flagged as non-idiomatic.

Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Appstore distribution across the OpenIAP ecosystem. Key additions include a new amazon Gradle flavor for the native Android package using the Amazon Appstore SDK, and a runtime-selected JavaScript adapter for Amazon Vega OS in React Native and Expo. The IAPKit server now supports Amazon Receipt Verification Service (RVS) with a project-level shared secret. Corresponding updates were made to the GraphQL schema, documentation, and all framework libraries (React Native, Expo, Flutter, Godot, KMP, and MAUI) to include the amazon store type and verification parameters. Feedback was provided to ensure that restorePurchases in the Vega adapter correctly emits events and that the isSuspendedAndroid flag in the native Android module is properly mapped to the Amazon SDK's deferred state.

Comment thread libraries/react-native-iap/src/vega-adapter.ts
Comment thread packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt (1)

829-839: 💤 Low value

Consider verifying full string consumption for stricter parsing.

The parseLocalizedPrice function checks position.index > 0 but doesn't verify the entire input was consumed. A price like "$12.34 USD" may only partially parse. However, since this is a best-effort fast path with fallback to manual parsing, the current approach is acceptable for robustness.

♻️ Optional: stricter parse validation
     private fun parseLocalizedPrice(value: String): Double? {
         val locale = Locale.getDefault()
+        val trimmed = value.trim()
         return listOf(
             NumberFormat.getCurrencyInstance(locale),
             NumberFormat.getNumberInstance(locale)
         ).firstNotNullOfOrNull { format ->
             val position = ParsePosition(0)
-            val parsed = format.parse(value, position)
-            if (parsed != null && position.index > 0) parsed.toDouble() else null
+            val parsed = format.parse(trimmed, position)
+            if (parsed != null && position.index == trimmed.length) parsed.toDouble() else null
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`
around lines 829 - 839, The current parseLocalizedPrice function accepts a
successful parse if ParsePosition.index > 0, which allows partial parses like
"$12.34 USD"; update the parsing check in parseLocalizedPrice to require the
parser consumed the entire input (i.e., position.index == value.length) so only
full-string parses are accepted, falling back to the existing manual parsing
behavior when the formatted parse doesn't fully consume the input.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 829-839: The current parseLocalizedPrice function accepts a
successful parse if ParsePosition.index > 0, which allows partial parses like
"$12.34 USD"; update the parsing check in parseLocalizedPrice to require the
parser consumed the entire input (i.e., position.index == value.length) so only
full-string parses are accepted, falling back to the existing manual parsing
behavior when the formatted parse doesn't fully consume the input.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 878e9ae3-8a19-4222-873c-116cf21bb11c

📥 Commits

Reviewing files that changed from the base of the PR and between 72dca78 and 3b88e53.

📒 Files selected for processing (11)
  • libraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapLog.kt
  • libraries/expo-iap/plugin/src/withIAP.ts
  • libraries/expo-iap/src/ExpoIapModule.ts
  • libraries/expo-iap/src/__tests__/vega-adapter.test.ts
  • libraries/expo-iap/src/vega-adapter.ts
  • libraries/react-native-iap/src/__tests__/utils/error.test.ts
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/utils/error.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/kit/convex/purchases/amazon.ts

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem. Key changes include the addition of the amazon Android flavor using the Amazon Appstore SDK, a new JavaScript runtime adapter for Vega OS in React Native and Expo, and server-side receipt verification for Amazon via IAPKit. The update also features improved log sanitization for sensitive keys, expanded documentation, and new setup guides for the added platforms. Feedback focuses on defensive programming in the new Amazon native module to prevent potential null pointer exceptions when interacting with the Amazon Purchasing Service.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including a new amazon Android flavor and a kepler runtime adapter for Vega OS. The changes span the native Android package, multiple framework wrappers, GraphQL schema updates, and extensive documentation. Feedback focuses on improving the maintainability and robustness of the new integrations, specifically by making the hardcoded IAPKit verification URL configurable, consolidating redundant helper functions in the Amazon module, and refining the complex manual currency parsing logic used for Amazon price strings to better handle international locales.

Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt (1)

877-905: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

AmazonReceipt.isDeferred looks unsupported in this SDK.

Line 878 reads isDeferred from com.amazon.device.iap.model.Receipt. For the Amazon flavor, this receipt type only exposes cancellation state in the context provided here, so this is likely an unresolved-reference compile break and it also changes purchase-state semantics based on a field the SDK does not provide. Revert to the cancellation-only mapping unless the Appstore SDK version in use actually added a deferred/suspended receipt flag.

Suggested minimal fix
-        val receiptDeferred = isDeferred
         val state = when {
-            receiptDeferred -> PurchaseState.Pending
             receiptCanceled -> PurchaseState.Unknown
             else -> PurchaseState.Purchased
         }
         return PurchaseAndroid(
             autoRenewingAndroid =
-                isSubscription && !receiptCanceled && !receiptDeferred,
+                isSubscription && !receiptCanceled,
             currentPlanId = if (isSubscription) sku else null,
             dataAndroid = toJSON().toString(),
             id = receiptId,
             ids = listOf(sku),
             isAcknowledgedAndroid = null,
             isAutoRenewing =
-                isSubscription && !receiptCanceled && !receiptDeferred,
+                isSubscription && !receiptCanceled,
             packageNameAndroid = context.packageName,
             platform = IapPlatform.Android,
             productId = sku,
             purchaseState = state,
             purchaseToken = receiptId,
             quantity = 1,
             signatureAndroid = null,
             store = IapStore.Amazon,
             transactionDate = dateMillis,
             transactionId = receiptId,
-            isSuspendedAndroid = receiptDeferred
+            isSuspendedAndroid = false
         )
Does `com.amazon.device.iap.model.Receipt` in the current Amazon Appstore SDK expose an `isDeferred` property, or any equivalent deferred/suspended purchase-state field?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`
around lines 877 - 905, The code references a non-existent Amazon Receipt
property isDeferred and uses it in the PurchaseAndroid construction
(variables/fields: isDeferred, receiptDeferred, purchaseState,
isSuspendedAndroid, autoRenewingAndroid, isAutoRenewing); remove any use of
isDeferred and treat deferred/suspended as unsupported for the Amazon flavor by
setting receiptDeferred = false (or remove the variable) and update the when()
mapping to only use receiptCanceled -> PurchaseState.Unknown else -> Purchased,
and set isSuspendedAndroid, autoRenewingAndroid and isAutoRenewing to only
depend on isSubscription and receiptCanceled so the PurchaseAndroid creation no
longer reads the unsupported isDeferred field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 877-905: The code references a non-existent Amazon Receipt
property isDeferred and uses it in the PurchaseAndroid construction
(variables/fields: isDeferred, receiptDeferred, purchaseState,
isSuspendedAndroid, autoRenewingAndroid, isAutoRenewing); remove any use of
isDeferred and treat deferred/suspended as unsupported for the Amazon flavor by
setting receiptDeferred = false (or remove the variable) and update the when()
mapping to only use receiptCanceled -> PurchaseState.Unknown else -> Purchased,
and set isSuspendedAndroid, autoRenewingAndroid and isAutoRenewing to only
depend on isSubscription and receiptCanceled so the PurchaseAndroid creation no
longer reads the unsupported isDeferred field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e333e7a1-b725-4b62-94de-4bd895aa62db

📥 Commits

Reviewing files that changed from the base of the PR and between 3b88e53 and 8a05cc2.

📒 Files selected for processing (3)
  • libraries/react-native-iap/src/__tests__/vega-adapter.test.ts
  • libraries/react-native-iap/src/vega-adapter.ts
  • packages/google/openiap/src/amazon/java/dev/hyo/openiap/OpenIapModule.kt

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem, including native Android, React Native, Expo, and Flutter libraries. Key changes include the addition of the amazon Gradle flavor, a new JavaScript runtime adapter for Vega OS (Kepler), and server-side receipt verification via Amazon RVS in IAPKit. The documentation has been extensively updated with setup guides for these new targets. Feedback from the review highlights several improvement opportunities in the new Vega adapters, specifically regarding the need for safety limits on paginated purchase updates and the implementation of SKU chunking (batches of 100) when querying product data to comply with Amazon's API limits. Additionally, it was noted that the native Amazon implementation of getAvailableItems should include a product type hydration step for consistency with the JavaScript adapters.

Comment thread libraries/expo-iap/src/vega-adapter.ts
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS and Vega OS across the OpenIAP ecosystem. Key changes include the addition of the amazon Android build flavor, native implementation for the Amazon Appstore SDK, and a runtime JavaScript adapter for Vega OS in React Native and Expo. The GraphQL schema and IAPKit server-side verification have been updated to support Amazon RVS. The PR also features improved log sanitization, documentation for the new platforms, and a transition to the object-based finishTransaction API signature. I have no feedback to provide.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS (via the Amazon Appstore SDK) and Vega OS (via a runtime JavaScript adapter) across the OpenIAP ecosystem, including native Android, React Native, Expo, Flutter, Godot, KMP, and MAUI. It also updates the IAPKit backend to verify Amazon receipts via the Amazon Receipt Verification Service (RVS). The review feedback highlights critical issues: the Amazon receipt ID regex pattern needs to allow the + character to prevent valid base64 receipt IDs from failing validation, and null checks must be added before inserting keys or values into ConcurrentHashMap instances in OpenIapModule.kt to avoid potential NullPointerException crashes.

Comment thread packages/kit/server/api/v1/route-input-schemas.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
libraries/flutter_inapp_purchase/lib/flutter_inapp_purchase.dart (1)

1922-1929: ⚡ Quick win

Add tests for the new Amazon and malformed-result branches.

This change adds new request serialization plus stricter failure paths (iapkit object validation and missing provider), so it should be covered with channel tests before merge.

As per coding guidelines, "When adding or modifying code, always write tests for new code paths with aim for full branch coverage."

Also applies to: 1962-2027

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libraries/flutter_inapp_purchase/lib/flutter_inapp_purchase.dart` around
lines 1922 - 1929, Add channel tests that exercise the new Amazon branch and the
stricter failure paths introduced around iapkit validation and missing provider:
write tests that (1) serialize a request where iapkit.amazon is present and
contains receiptId, sandbox and userId to assert the map includes 'amazon' with
those keys (reference iapkit.amazon, receiptId, sandbox, userId), (2) send
malformed-result responses to assert the code path that treats iapkit as invalid
triggers the expected failure, and (3) send requests with a missing provider to
assert the missing-provider error branch. Target the serialization/handler that
emits this map (the code around the iapkit -> 'amazon' mapping) and add
assertions for both success and all new failure branches to achieve full branch
coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@libraries/flutter_inapp_purchase/macos/Classes/FlutterInappPurchasePlugin.swift`:
- Around line 802-812: The block handling iapkit["amazon"] should validate and
trim inputs and fail fast: inside FlutterInappPurchasePlugin where you inspect
iapkit and build amazonDict, check that amazon["receiptId"] exists and is a
non-empty string after trimming (reject blank values) and if missing/blank
immediately return a developerError (instead of silently omitting the block);
also trim userId and receiptId strings before adding them to amazonDict and
include sandbox only when present. Ensure the error returned is a clear
developerError indicating the missing/invalid amazon.receiptId.

In `@libraries/godot-iap/addons/godot-iap/godot_iap.gd`:
- Around line 673-675: The code calls _parse_request_id(pending) and then passes
the potentially empty request_id into
_await_products_fetched_for("verifyPurchase", request_id), which allows
cross-call mixups; add a fail-fast guard right after obtaining request_id: if
request_id is nil/empty, return or raise a failure (and log) immediately instead
of calling _await_products_fetched_for. Apply this same guard at the other
identical call site that obtains request_id (the second occurrence around the
verifyPurchase flow) so neither call proceeds with an empty request_id.

In `@libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIapLog.kt`:
- Around line 94-142: The Map-based sanitization currently only redacts keys
containing "token"; update the sanitizeMap implementation to reuse the same
sensitive-key logic as the JSON path by calling isSensitiveKey(key) (using the
same normalization) and replace sensitive Map values with "hidden"; keep using
sanitize/sanitizeJsonValue for non-sensitive values so structure and nested
objects/arrays are still sanitized consistently (refer to sanitizeMap,
isSensitiveKey, sanitize, sanitizeJsonValue, sanitizeJsonObject).

In `@packages/apple/Sources/OpenIapModule.swift`:
- Around line 789-796: In verifyPurchaseWithIapkit, don't convert
CancellationError into .networkError: when calling URLSession.shared.data(for:
request) catch CancellationError (or check for Task.isCancelled) and rethrow it
immediately, and only map other errors to makePurchaseError(code: .networkError,
...); update the catch block around the URLSession call (where OpenIapLog.warn
and makePurchaseError are used) to rethrow cancellations so task cancellation
semantics are preserved.

In `@packages/kit/public/llms-full.txt`:
- Around line 111-114: The response table currently conflates oversized payloads
with other malformed input by listing a 400/INVALID_INPUT response; update the
API spec so that oversized request bodies return 413 (Payload Too Large) and
leave 400/INVALID_INPUT for malformed/unknown-store cases—adjust the table row
entries and the related "input-size" section text to explicitly enumerate both
cases (400 -> INVALID_INPUT for malformed/unknown store, 413 ->
OVERSIZED_PAYLOAD or similar) so the contract is unambiguous and consistent with
the input-size description.

---

Nitpick comments:
In `@libraries/flutter_inapp_purchase/lib/flutter_inapp_purchase.dart`:
- Around line 1922-1929: Add channel tests that exercise the new Amazon branch
and the stricter failure paths introduced around iapkit validation and missing
provider: write tests that (1) serialize a request where iapkit.amazon is
present and contains receiptId, sandbox and userId to assert the map includes
'amazon' with those keys (reference iapkit.amazon, receiptId, sandbox, userId),
(2) send malformed-result responses to assert the code path that treats iapkit
as invalid triggers the expected failure, and (3) send requests with a missing
provider to assert the missing-provider error branch. Target the
serialization/handler that emits this map (the code around the iapkit ->
'amazon' mapping) and add assertions for both success and all new failure
branches to achieve full branch coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52b1c0bc-0404-458d-be33-da09ac5932a0

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb9f1a and 561762f.

📒 Files selected for processing (39)
  • knowledge/_claude-context/context.md
  • libraries/expo-iap/src/__tests__/useIAP.test.tsx
  • libraries/expo-iap/src/useIAP.ts
  • libraries/flutter_inapp_purchase/android/src/main/kotlin/io/github/hyochan/flutter_inapp_purchase/AndroidInappPurchasePlugin.kt
  • libraries/flutter_inapp_purchase/ios/Classes/FlutterInappPurchasePlugin.swift
  • libraries/flutter_inapp_purchase/lib/flutter_inapp_purchase.dart
  • libraries/flutter_inapp_purchase/macos/Classes/FlutterInappPurchasePlugin.swift
  • libraries/flutter_inapp_purchase/test/flutter_inapp_purchase_channel_test.dart
  • libraries/godot-iap/addons/godot-iap/godot_iap.gd
  • libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIap.kt
  • libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIapLog.kt
  • libraries/godot-iap/ios-gdextension/Sources/GodotIap/GodotIap.swift
  • libraries/godot-iap/ios-gdextension/Sources/GodotIap/GodotIapLog.swift
  • libraries/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.kt
  • libraries/kmp-iap/library/src/iosMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseIOS.kt
  • libraries/maui-iap/android/openiap/src/main/java/dev/hyo/openiap/maui/OpenIapMauiModule.kt
  • libraries/maui-iap/src/OpenIap.Maui.Bindings.iOS/ApiDefinition.cs
  • libraries/maui-iap/src/OpenIap.Maui/Platforms/iOS/OpenIapIOS.cs
  • libraries/react-native-iap/ios/HybridRnIap.swift
  • llms-full.txt
  • llms.txt
  • packages/apple/Sources/OpenIapModule+ObjC.swift
  • packages/apple/Sources/OpenIapModule.swift
  • packages/docs/public/llms-full.txt
  • packages/docs/public/llms.txt
  • packages/docs/src/pages/docs/foundation/sponsorship.tsx
  • packages/docs/src/pages/docs/kit-backend.tsx
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/kit/public/llms-full.txt
  • packages/kit/public/llms.txt
  • packages/kit/src/content/faq.md
  • packages/kit/src/content/terms-of-service.md
  • packages/kit/src/pages/blog/index.tsx
  • packages/kit/src/pages/blog/posts.ts
  • packages/kit/src/pages/docs/sections/introduction.tsx
  • packages/kit/src/pages/docs/sections/projects.tsx
  • packages/kit/src/pages/docs/sections/verification-apple.tsx
  • packages/kit/src/pages/landing.tsx
✅ Files skipped from review due to trivial changes (7)
  • packages/kit/src/pages/docs/sections/projects.tsx
  • packages/kit/src/pages/docs/sections/verification-apple.tsx
  • packages/docs/src/pages/docs/kit-backend.tsx
  • packages/kit/src/pages/blog/posts.ts
  • packages/kit/src/pages/landing.tsx
  • packages/kit/public/llms.txt
  • packages/docs/public/llms.txt

Comment thread libraries/flutter_inapp_purchase/macos/Classes/FlutterInappPurchasePlugin.swift Outdated
Comment thread libraries/godot-iap/addons/godot-iap/godot_iap.gd Outdated
Comment thread libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIapLog.kt Outdated
Comment thread packages/apple/Sources/OpenIapModule.swift
Comment thread packages/kit/public/llms-full.txt Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive support for Amazon Fire OS (Amazon Appstore SDK) and Vega OS across the OpenIAP ecosystem, including native Android/iOS packages, wrapper libraries (React Native, Expo, Flutter, Godot, KMP, MAUI), the IAPKit backend, and documentation. The review feedback correctly points out two critical safety issues in the Godot wrapper where a null payload returned from asynchronous operations could cause runtime crashes in GDScript during purchase verification.

Comment thread libraries/godot-iap/addons/godot-iap/godot_iap.gd Outdated
Comment thread libraries/godot-iap/addons/godot-iap/godot_iap.gd Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Amazon Fire OS support (via the amazon Gradle flavor and Amazon Appstore SDK) and Amazon Vega OS support (via a runtime-selected JavaScript adapter in react-native-iap and expo-iap using the kepler runtime) across the OpenIAP ecosystem. It also updates IAPKit verification to support Amazon RVS payloads (userId and receiptId). Feedback on these changes highlights a missing fallback dependency in expo-iap's build.gradle when no other store is enabled, compilation issues in shared Android tests due to direct references to BillingClient, and style guide violations where the single-use helper iapkitVerifyUrl should be declared locally inside verifyWithIapkit in both expo-iap and react-native-iap's Vega adapters.

Comment on lines 108 to 115
if (findProject(":openiap-google") != null) {
implementation project(":openiap-google")
} else if (fireOsEnabled) {
// Use openiap-google-amazon for Fire OS when fireOsEnabled is true
implementation "io.github.hyochan.openiap:openiap-google-amazon:${googleVersionString}"
} else if (horizonEnabled) {
// Use openiap-google-horizon for Meta Quest when horizonEnabled is true
implementation "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If findProject(":openiap-google") is null and both fireOsEnabled and horizonEnabled are false (which is the default for standard Google Play builds when installed from npm), no dependency on openiap-google is added. This will cause compilation errors because the library classes won't be found on the classpath.

Please add a fallback else block to implement the default openiap-google artifact.

    if (findProject(":openiap-google") != null) {
      implementation project(":openiap-google")
    } else if (fireOsEnabled) {
      // Use openiap-google-amazon for Fire OS when fireOsEnabled is true
      implementation "io.github.hyochan.openiap:openiap-google-amazon:${googleVersionString}"
    } else if (horizonEnabled) {
      // Use openiap-google-horizon for Meta Quest when horizonEnabled is true
      implementation "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"
    } else {
      // Use openiap-google for Google Play when no other store is enabled
      implementation "io.github.hyochan.openiap:openiap-google:${googleVersionString}"
    }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No code change needed. The fallback already exists in the current diff: libraries/expo-iap/android/build.gradle adds io.github.hyochan.openiap:openiap-google:${googleVersionString} in the final else branch when neither fireOsEnabled nor horizonEnabled is true and no local :openiap-google project is linked.

Comment thread packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt Outdated
Comment thread packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt Outdated
Comment thread libraries/expo-iap/src/vega-adapter.ts Outdated
Comment thread libraries/react-native-iap/src/vega-adapter.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/apple/Sources/OpenIapModule.swift (1)

751-776: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Restore non-Apple IAPKit payload handling in this builder.

This helper now hard-requires props.apple, so the new iapkit.amazon payload accepted by the Flutter Apple bridges will always fail here with a developer error before the network call. That narrows the RequestVerifyPurchaseWithIapkitProps contract and breaks the new Amazon verification path on Apple runtimes. Either serialize the Amazon variant here as well, or reject it at the bridge layer instead of accepting it upstream.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/apple/Sources/OpenIapModule.swift` around lines 751 - 776, The
builder currently assumes only props.apple and throws a developerError, breaking
the Amazon-in-Apple runtime path; update buildIapkitPayload and
verifyPurchaseWithIapkit to accept exactly one of
props.apple/props.google/props.amazon, determine the store (IapStore.apple or
.amazon) and serialize the corresponding payload variant (e.g., use a generic
IapkitPayload or separate structs for Apple vs Amazon) instead of always
building IapkitApplePayload, and ensure the payload body and store variable
reflect the chosen provider so the Amazon iapkit payload from
RequestVerifyPurchaseWithIapkitProps is correctly encoded and sent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/apple/Sources/OpenIapModule.swift`:
- Around line 751-776: The builder currently assumes only props.apple and throws
a developerError, breaking the Amazon-in-Apple runtime path; update
buildIapkitPayload and verifyPurchaseWithIapkit to accept exactly one of
props.apple/props.google/props.amazon, determine the store (IapStore.apple or
.amazon) and serialize the corresponding payload variant (e.g., use a generic
IapkitPayload or separate structs for Apple vs Amazon) instead of always
building IapkitApplePayload, and ensure the payload body and store variable
reflect the chosen provider so the Amazon iapkit payload from
RequestVerifyPurchaseWithIapkitProps is correctly encoded and sent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b263f77f-328b-424a-924b-506b5c6065d5

📥 Commits

Reviewing files that changed from the base of the PR and between da2658c and 28893dc.

📒 Files selected for processing (8)
  • libraries/flutter_inapp_purchase/ios/Classes/FlutterInappPurchasePlugin.swift
  • libraries/flutter_inapp_purchase/macos/Classes/FlutterInappPurchasePlugin.swift
  • libraries/godot-iap/addons/godot-iap/godot_iap.gd
  • libraries/godot-iap/android/src/main/java/dev/hyo/godotiap/GodotIapLog.kt
  • packages/apple/Sources/OpenIapModule.swift
  • packages/kit/public/llms-full.txt
  • packages/kit/public/llms.txt
  • packages/kit/src/pages/docs/sections/api.tsx
✅ Files skipped from review due to trivial changes (3)
  • packages/kit/public/llms-full.txt
  • packages/kit/src/pages/docs/sections/api.tsx
  • packages/kit/public/llms.txt

@hyochan

hyochan commented May 25, 2026

Copy link
Copy Markdown
Member Author

CodeRabbit outside-diff feedback addressed in 01d6d54. buildIapkitPayload now accepts exactly one of apple/google/amazon and serializes the matching store payload, so Flutter Apple bridges can pass amazon iapkit payloads without tripping the apple-only guard.

hyochan and others added 27 commits June 12, 2026 05:40
Add store-specific example walkthrough pages and recordings for Apple, Google, Horizon OS, and Fire OS. Update Amazon/Horizon subscription mapping so shared subscription APIs expose active state consistently, and cover multiple subscription group handling with Apple and Android flavor tests.
Guard PlatformTabs hash parsing for non-browser rendering. Populate Vega active subscription plan identifiers with the active product id for Expo and React Native, and cover the mapping in adapter tests.
Add Amazon Vega runtime adapters, Fire OS/Vega config plugin wiring, example app flows, App Tester setup files, IAPKit verification docs, and redacted Kit debug verification logs.

Refresh docs and generated LLM context for the Amazon support announcement and setup guides.
Keep Amazon Vega dependencies out of normal example installs while preserving Vega build command discovery in generated targets.

Clarify Expo and React Native Vega dependency boundaries in docs and catch native Fire OS purchase request failures in the example flow.
Add a Claude/Codex e2e-tests workflow for connected-device OpenIAP regression across Expo, React Native, FireOS, iOS, Android, and Vega paths.

Make AGENTS.md the root instruction SSOT with CLAUDE.md and GEMINI.md symlinked to it, and route the workflow from the Codex skill.
Document full e2e coverage across native packages and framework examples, including build-only Horizon and Onside rows plus blocked KMP/MAUI FireOS/Horizon rows.

Make Expo example Horizon and Onside toggles env-driven, and add resilient Vega install fallback for RN/Expo example run scripts.
@hyochan hyochan force-pushed the release/fireos-prerelease branch from 6f4819c to df0a65b Compare June 11, 2026 20:43
hyochan and others added 2 commits June 12, 2026 05:54
Normalize Expo generated Gradle syntax for Android, Fire OS, and Horizon prebuilds.

Make the Onside iOS bridge compile against the current SDK and keep Flutter/KMP examples buildable in CI-style checks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖 android Related to android cross-platform Cross-platform (both Android & iOS) 🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant