fix: modernize store API integrations - #299
Conversation
Add the Advanced Commerce subscription period, document the current StoreKit intent contracts, and regenerate every synchronized platform type.
Preserve PurchaseIntent win-back offers, select current entitlements deterministically, modernize offer-code redemption, and keep AppTransaction compatibility across supported toolchains.
Guard suspended-subscription queries by Play capability, require a foreground Horizon activity, enforce single-product requests, and isolate composite build outputs.
Propagate current StoreKit and Billing behavior across React Native, Expo, Flutter, Godot, KMP, and MAUI examples, bridges, tests, and build isolation.
Document StoreKit toolchain and platform fallbacks, Horizon activity requirements, current entitlement semantics, and nullable redemption results across searchable, compiled, and knowledge references.
Build the Expo config plugin before prebuild so generated native projects exercise local Google build-directory isolation during verification.
|
Warning Review limit reached
Next review available in: 13 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (6)
📒 Files selected for processing (31)
📝 WalkthroughWalkthroughThe PR updates Apple StoreKit flows, adds iOS subscription-period metadata across SDKs, improves Android billing and Horizon handling, isolates Gradle builds, and updates documentation, examples, tests, and verification commands. ChangesPlatform purchase flows
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant OpenIapModule
participant PromotedPurchaseIntentOfferStore
participant StoreKit
OpenIapModule->>StoreKit: receive promoted purchase intent
OpenIapModule->>PromotedPurchaseIntentOfferStore: record subscription offer
OpenIapModule->>PromotedPurchaseIntentOfferStore: take offer for product purchase
OpenIapModule->>StoreKit: create purchase options with win-back offer
OpenIapModule->>StoreKit: present redemption sheet
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Gate Product.currentEntitlements on the Xcode 26 Swift compiler so Xcode 16.4 builds retain the Transaction.currentEntitlements fallback.
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
libraries/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.kt (1)
229-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the required
Androidsuffix to the helper.
availablePurchasesQueryParamsis declared inandroidMainand is used only by Android code. Rename it toavailablePurchasesQueryParamsAndroidand update the subscription query call.Proposed rename
-internal fun availablePurchasesQueryParams( +internal fun availablePurchasesQueryParamsAndroid( ... - val params = availablePurchasesQueryParams( + val params = availablePurchasesQueryParamsAndroid(As per coding guidelines: “Use
IOSandAndroidas platform suffixes, never as prefixes.”Also applies to: 1793-1797
🤖 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/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.kt` around lines 229 - 245, Rename the Android-only helper availablePurchasesQueryParams to availablePurchasesQueryParamsAndroid, and update the subscription query call site that invokes it to use the new name. Preserve the existing parameter handling and builder behavior.Source: Coding guidelines
libraries/expo-iap/plugin/src/__tests__/withLocalOpenIAP.test.ts (1)
25-27: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert that the build-directory assignment is scoped to
:openiap-google.The Groovy test checks only the assignment text. The Kotlin test checks the project name and assignment independently. Neither test proves that both lines are in the same project block.
Use a scoped regular expression or an exact block assertion for both DSL variants.
Proposed assertion
- expect(result).toContain( - 'layout.buildDirectory.set(rootProject.layout.buildDirectory.dir("openiap-google"))', - ); + expect(result).toMatch( + /project\(":openiap-google"\)\s*\{\s*layout\.buildDirectory\.set\(rootProject\.layout\.buildDirectory\.dir\("openiap-google"\)\)\s*\}/, + );Also applies to: 44-47
🤖 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/__tests__/withLocalOpenIAP.test.ts` around lines 25 - 27, Update the assertions in withLocalOpenIAP tests for both Groovy and Kotlin DSL variants to verify that the project identifier :openiap-google and the build-directory assignment occur within the same project block. Replace the independent text checks with a scoped regular expression or exact block assertion while preserving coverage of both DSL formats.packages/apple/Tests/OpenIapTests.swift (1)
48-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
IOSsuffix for these iOS tests.Rename the tests to end with
IOS. For example, usetestEntitlementSelectionUsesLatestDateThenTransactionIdIOSandtestPromotedPurchaseIntentOfferIsProductScopedAndOneShotIOS.As per coding guidelines, “iOS functions must end with the
IOSsuffix.”🤖 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/Tests/OpenIapTests.swift` around lines 48 - 82, Rename the test methods testEntitlementSelectionUsesLatestDateThenTransactionId and testPromotedPurchaseIntentOfferIsProductScopedAndOneShot to append the IOS suffix, preserving their existing test logic.Source: Coding guidelines
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (1)
383-387: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
IOSsuffixes to Apple-only helpers.These helpers accept StoreKit types or run only on iOS. Rename them to follow the platform naming rule.
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift#L383-L387: RenamepurchaseOptionstopurchaseOptionsIOS.packages/apple/Sources/Helpers/StoreKitTypesBridge.swift#L769-L795: RenamestandardizedDiscountOfferTypeandpurchaseOfferTypeStringwithIOSsuffixes.packages/apple/Sources/OpenIapModule.swift#L141-L142: Update the listener call after renaming.packages/apple/Sources/OpenIapModule.swift#L1782-L1807: RenamestartPromotedPurchaseIntentListenerIfAvailableto end withIOS.As per coding guidelines, “iOS functions must end with the
IOSsuffix.”🤖 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/Helpers/StoreKitTypesBridge.swift` around lines 383 - 387, Rename purchaseOptions to purchaseOptionsIOS in packages/apple/Sources/Helpers/StoreKitTypesBridge.swift at lines 383-387, and rename standardizedDiscountOfferType and purchaseOfferTypeString with IOS suffixes at lines 769-795. Update the corresponding listener call in packages/apple/Sources/OpenIapModule.swift at lines 141-142, and rename startPromotedPurchaseIntentListenerIfAvailable to an IOS-suffixed name at lines 1782-1807, updating its references consistently.Source: Coding guidelines
libraries/react-native-iap/ios/RnIapHelper.swift (1)
132-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the
IOSsuffix to the new iOS helper functions.Rename
wrapSubscriptionPeriodValuetowrapSubscriptionPeriodValueIOSandconvertSubscriptionPeriodValuetoconvertSubscriptionPeriodValueIOS. Update the call inconvertAdvancedCommerceInfo.As per coding guidelines, iOS functions in
*.swiftfiles must end with theIOSsuffix.Proposed rename
-static func wrapSubscriptionPeriodValue( +static func wrapSubscriptionPeriodValueIOS( @@ - convertSubscriptionPeriodValue(dictionary["period"]) + convertSubscriptionPeriodValueIOS(dictionary["period"]) @@ - static func convertSubscriptionPeriodValue(_ value: Any?) -> SubscriptionPeriodValueIOS? { + static func convertSubscriptionPeriodValueIOS(_ value: Any?) -> SubscriptionPeriodValueIOS? {Also applies to: 195-205
🤖 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/ios/RnIapHelper.swift` around lines 132 - 137, Rename the Swift helpers wrapSubscriptionPeriodValue and convertSubscriptionPeriodValue to wrapSubscriptionPeriodValueIOS and convertSubscriptionPeriodValueIOS, respectively. Update all references, including the calls within convertAdvancedCommerceInfo, so the iOS helper functions consistently use the IOS suffix.Source: Coding guidelines
🤖 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 `@knowledge/external/storekit2-api.md`:
- Around line 84-92: Update the GraphQL-facing iOS API declaration for
presentCodeRedemptionSheetIOS from non-null PurchaseIOS to nullable PurchaseIOS,
then regenerate or manually synchronize the corresponding generated GraphQL
types and other generated SDKs. Preserve the existing field name and PurchaseIOS
mapping.
- Line 375: Split the “AppTransaction Updates” documentation into sections
matching API availability: keep fields available in Xcode 16.4+ separate from
revocationDate and storeType under an Xcode 27 SDK-only heading. Update the
examples and explanatory text accordingly, then regenerate
knowledge/_claude-context/context.md so it reflects the revised headings and
content.
In `@libraries/godot-iap/addons/godot-iap/godot_iap.gd`:
- Around line 1344-1345: Update the return documentation near the Godot IAP
purchase method to describe all implemented outcomes: return Types.PurchaseIOS
on successful supported requests, and return null for unsupported platforms or
native requests, missing or invalid purchaseJson, and other failures, including
when the system sheet cannot provide the transaction directly.
In `@libraries/react-native-iap/ios/RnIapHelper.swift`:
- Around line 205-213: Update convertSubscriptionPeriodValue to validate
periodValue before constructing SubscriptionPeriodValueIOS: reject non-finite
values and any fractional value, accepting only values representable by the
integer contract. Keep returning nil for invalid payloads and preserve the
existing unit and valid-integer conversion flow.
In `@packages/docs/src/pages/docs/apis/ios/present-code-redemption-sheet-ios.tsx`:
- Around line 31-40: Correct the Mac Catalyst contract throughout the redemption
documentation: in
packages/docs/src/pages/docs/apis/ios/present-code-redemption-sheet-ios.tsx at
lines 31-40, 80-83, and 94, describe AppStore.presentOfferCodeRedeemSheet(in:)
as throwing StoreKitError.unknown and
SKPaymentQueue.presentCodeRedemptionSheet() as having no effect rather than a
successful nil-returning path, and update examples with explicit error or
unsupported handling. Apply the corresponding contract and example updates in
packages/docs/src/pages/docs/features/offer-code-redemption.tsx at lines 29-31
and 94-100, packages/docs/src/pages/docs/updates/deprecations.tsx at line 82,
and packages/docs/public/llms-full.txt at lines 1463-1465.
In `@packages/docs/src/pages/docs/setup/godot.tsx`:
- Around line 126-130: Update the StoreKit offer-code documentation near the
Xcode 27 build guidance to state both requirements: the framework must be built
with Xcode 27 or later and run on an Apple OS 27 or later runtime. Explicitly
preserve the behavior that older runtimes return null even when using an Xcode
27 build, and distinguish runtime requirements from SDK or device-version
references.
In `@packages/docs/src/pages/docs/types/ios/app-transaction-ios.tsx`:
- Around line 139-153: Update the appTransactionID and originalPlatform
descriptions in
packages/docs/src/pages/docs/types/ios/app-transaction-ios.tsx:139-153 to state
their platform-specific runtime floors (iOS 18.4, macOS 15.4, tvOS 18.4, watchOS
11.4, visionOS 2.4) and Xcode 16.4+ back-deployment, keeping the
originalPlatform compatibility modifier clear. Apply the same qualification to
both fields in
packages/docs/src/pages/docs/apis/ios/get-app-transaction-ios.tsx:120-121. In
packages/docs/public/llms-full.txt:1752, keep the heading’s Xcode requirement
separate from the per-field platform runtime-floor notes.
In `@packages/docs/src/pages/docs/types/purchase.tsx`:
- Around line 540-545: Update the generated machine-readable reference in
llms-full.txt for Advanced Commerce API (iOS 18.4+) to include
AdvancedCommerceInfoIOS.period with its optional subscription period unit and
value description, keeping it consistent with the rendered type documentation.
---
Nitpick comments:
In `@libraries/expo-iap/plugin/src/__tests__/withLocalOpenIAP.test.ts`:
- Around line 25-27: Update the assertions in withLocalOpenIAP tests for both
Groovy and Kotlin DSL variants to verify that the project identifier
:openiap-google and the build-directory assignment occur within the same project
block. Replace the independent text checks with a scoped regular expression or
exact block assertion while preserving coverage of both DSL formats.
In
`@libraries/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.kt`:
- Around line 229-245: Rename the Android-only helper
availablePurchasesQueryParams to availablePurchasesQueryParamsAndroid, and
update the subscription query call site that invokes it to use the new name.
Preserve the existing parameter handling and builder behavior.
In `@libraries/react-native-iap/ios/RnIapHelper.swift`:
- Around line 132-137: Rename the Swift helpers wrapSubscriptionPeriodValue and
convertSubscriptionPeriodValue to wrapSubscriptionPeriodValueIOS and
convertSubscriptionPeriodValueIOS, respectively. Update all references,
including the calls within convertAdvancedCommerceInfo, so the iOS helper
functions consistently use the IOS suffix.
In `@packages/apple/Sources/Helpers/StoreKitTypesBridge.swift`:
- Around line 383-387: Rename purchaseOptions to purchaseOptionsIOS in
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift at lines 383-387, and
rename standardizedDiscountOfferType and purchaseOfferTypeString with IOS
suffixes at lines 769-795. Update the corresponding listener call in
packages/apple/Sources/OpenIapModule.swift at lines 141-142, and rename
startPromotedPurchaseIntentListenerIfAvailable to an IOS-suffixed name at lines
1782-1807, updating its references consistently.
In `@packages/apple/Tests/OpenIapTests.swift`:
- Around line 48-82: Rename the test methods
testEntitlementSelectionUsesLatestDateThenTransactionId and
testPromotedPurchaseIntentOfferIsProductScopedAndOneShot to append the IOS
suffix, preserving their existing test logic.
🪄 Autofix
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 Plus
Run ID: 5aab644e-b857-42d5-8a29-7991f3040ebc
⛔ Files ignored due to path filters (6)
packages/gql/src/generated/Types.csis excluded by!**/generated/**packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/Types.swiftis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**packages/gql/src/generated/types.gdis excluded by!**/generated/**packages/gql/src/generated/types.tsis excluded by!**/generated/**
📒 Files selected for processing (66)
.claude/commands/verify-all.mdknowledge/_claude-context/context.mdknowledge/external/google-billing-api.mdknowledge/external/horizon-api.mdknowledge/external/storekit2-api.mdlibraries/expo-iap/android/src/main/java/expo/modules/iap/ExpoIapModule.ktlibraries/expo-iap/example/__tests__/offer-code.test.tsxlibraries/expo-iap/example/app/offer-code.tsxlibraries/expo-iap/plugin/src/__tests__/withLocalOpenIAP.test.tslibraries/expo-iap/plugin/src/withLocalOpenIAP.tslibraries/expo-iap/src/modules/ios.tslibraries/expo-iap/src/types.tslibraries/flutter_inapp_purchase/example/lib/src/screens/offer_code_screen.dartlibraries/flutter_inapp_purchase/lib/types.dartlibraries/flutter_inapp_purchase/test/ios_methods_test.dartlibraries/godot-iap/Example/tests/test_types_only.gdlibraries/godot-iap/addons/godot-iap/godot_iap.gdlibraries/godot-iap/addons/godot-iap/types.gdlibraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/screens/OfferCodeScreen.ktlibraries/kmp-iap/library/src/androidMain/kotlin/io/github/hyochan/kmpiap/InAppPurchaseAndroid.ktlibraries/kmp-iap/library/src/androidUnitTest/kotlin/io/github/hyochan/kmpiap/BillingQueryLifecycleTest.ktlibraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.ktlibraries/kmp-iap/library/src/iosTest/kotlin/io/github/hyochan/kmpiap/ProductPayloadNormalizerTestIOS.ktlibraries/maui-iap/example/OpenIap.Maui.Example/Pages/OfferCodePage.xaml.cslibraries/maui-iap/src/OpenIap.Maui/Types.cslibraries/maui-iap/tests/OpenIap.Maui.Tests/RecordJsonTests.cslibraries/react-native-iap/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.ktlibraries/react-native-iap/example/screens/OfferCode.tsxlibraries/react-native-iap/ios/RnIapHelper.swiftlibraries/react-native-iap/src/index.tslibraries/react-native-iap/src/specs/RnIap.nitro.tslibraries/react-native-iap/src/types.tspackages/apple/Example/OpenIapExample/Screens/OfferCodeScreen.swiftpackages/apple/Example/OpenIapExample/Screens/uis/TestingNotesCard.swiftpackages/apple/Sources/Helpers/IapState.swiftpackages/apple/Sources/Helpers/StoreKitTypesBridge.swiftpackages/apple/Sources/Models/Types.swiftpackages/apple/Sources/OpenIapModule.swiftpackages/apple/Tests/OpenIapTests.swiftpackages/docs/public/llms-full.txtpackages/docs/public/llms.txtpackages/docs/src/lib/searchData.tspackages/docs/src/pages/docs/apis/index.tsxpackages/docs/src/pages/docs/apis/init-connection.tsxpackages/docs/src/pages/docs/apis/ios/current-entitlement-ios.tsxpackages/docs/src/pages/docs/apis/ios/get-app-transaction-ios.tsxpackages/docs/src/pages/docs/apis/ios/get-promoted-product-ios.tsxpackages/docs/src/pages/docs/apis/ios/present-code-redemption-sheet-ios.tsxpackages/docs/src/pages/docs/events/ios/promoted-product-listener-ios.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/foundation/one-pager.tsxpackages/docs/src/pages/docs/setup/godot.tsxpackages/docs/src/pages/docs/types/ios/app-transaction-ios.tsxpackages/docs/src/pages/docs/types/purchase.tsxpackages/docs/src/pages/docs/updates/deprecations.tsxpackages/google/build.gradle.ktspackages/google/openiap/build.gradle.ktspackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/google/openiap/src/testHorizon/java/dev/hyo/openiap/HorizonPurchaseSafetyTest.ktpackages/google/openiap/src/testPlay/java/dev/hyo/openiap/QueryPurchasesRaceTest.ktpackages/gql/src/api-ios.graphqlpackages/gql/src/event.graphqlpackages/gql/src/type-ios.graphqlscripts/agent/compile-context.ts
Validate native bridge inputs, apply platform naming conventions, document Catalyst and toolchain behavior accurately, and regenerate synchronized contracts and context.
Summary
Validation
Preview
A preview recording is not applicable because this PR changes native store integration behavior, generated contracts, documentation, and CI rather than introducing a user-interface feature.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation