feat: migrate to openiap 1.3.0 with IapStore and store field#3101
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThe pull request restructures purchase verification and store mapping across the native and JavaScript layers. Key changes include adding a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 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 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3101 +/- ##
==========================================
+ Coverage 62.97% 63.14% +0.16%
==========================================
Files 9 9
Lines 1564 1582 +18
Branches 522 531 +9
==========================================
+ Hits 985 999 +14
- Misses 573 576 +3
- Partials 6 7 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Summary of ChangesHello @hyochan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces significant updates to the in-app purchase system by migrating to OpenIAP 1.3.0, enhancing type safety and clarity across the API. It refines the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
- Update openiap versions: apple 1.3.0, google 1.3.10, gql 1.3.0
- Add IapStore type ('unknown' | 'apple' | 'google' | 'horizon')
- Add store field to Purchase (platform field deprecated)
- Add apple/google fields to RequestPurchasePropsByPlatforms (ios/android deprecated)
- Change verifyPurchaseWithProvider iapkit from array to object
- Add errors field to VerifyPurchaseWithProviderResult
- Create versioned docs for 14.4, update current to 14.5
- Update documentation with correct types and examples
345345c to
58b7bca
Compare
There was a problem hiding this comment.
Code Review
This pull request migrates the project to openiap v1.3.0, introducing the IapStore type and a new store field to replace the deprecated platform field. It also updates the purchase request properties to use apple/google instead of ios/android, and refactors the verifyPurchaseWithProvider result structure. The changes are extensive, covering native code, TypeScript types, tests, and documentation, including versioning for the docs.
My review has identified a few issues. There's a significant issue in the Nitro spec where IapkitStore is used instead of the new IapStore, which could lead to incorrect store type mapping. I've also found a minor code generation issue causing duplicated @deprecated tags and an inconsistency in the newly created versioned documentation. Addressing these points will improve the correctness and clarity of the library.
| @@ -40,6 +40,9 @@ export type IapkitPurchaseState = | |||
|
|
|||
| export type IapkitStore = 'apple' | 'google'; | |||
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
docs/docs/examples/purchase-flow.md (1)
76-90: Update verification examples to handle optionaliapkitresult anderrorsfieldThe
VerifyPurchaseWithProviderResulttype hasiapkitanderrorsas optional fields. The current examples accessresult.iapkit.isValiddirectly, which will throw at runtime if verification fails andiapkitis null:if (result.iapkit.isValid) { // ❌ Will throw if iapkit is null/undefined // ... }Use optional chaining and handle errors:
if (result.iapkit?.isValid) { // Grant entitlement to user await finishTransaction({purchase, isConsumable: true}); } else if (result.errors?.length) { console.warn('Verification failed:', result.errors); }This applies to all three code examples in this section (the standalone function, the useIAP hook example, and the verification response section).
src/__tests__/index.test.ts (1)
1269-1285: Mock result should usenullinstead of empty array for consistency with new API.The test
should handle null iapkit paramhas its mock returningiapkit: [](empty array), but the new API contract usesiapkit: nullwhen verification data is absent. This inconsistency could mask issues if the actual implementation returnsnull.it('should handle null iapkit param', async () => { (Platform as any).OS = 'ios'; const mockResult = { provider: 'iapkit', - iapkit: [], + iapkit: null, }; mockIap.verifyPurchaseWithProvider.mockResolvedValueOnce(mockResult);docs/docs/api/methods/core-methods.md (1)
518-528: Code example inconsistent with updated type definition.The example iterates over
result.iapkitas an array (for (const item of result.iapkit)), but the type definition below showsiapkitis now a single optional object, not an array. This will cause a runtime error.- for (const item of result.iapkit) { - console.log('Is Valid:', item.isValid); - console.log('State:', item.state); // 'entitled', 'expired', 'canceled', etc. - console.log('Store:', item.store); // 'apple' or 'google' + if (result.iapkit) { + console.log('Is Valid:', result.iapkit.isValid); + console.log('State:', result.iapkit.state); // 'entitled', 'expired', 'canceled', etc. + console.log('Store:', result.iapkit.store); // 'apple' or 'google' + } + + if (result.errors) { + for (const error of result.errors) { + console.error('Verification error:', error.message); + } }
🧹 Nitpick comments (18)
docs/versioned_docs/version-14.4/guides/support.md (1)
25-25: Minor: Consider tightening the closing sentence.Line 25 uses "We appreciate your support!" which, while friendly, is somewhat verbose. Consider alternatives like "Thank you for your support!" or simply removing this sentence if earlier context suffices.
docs/versioned_docs/version-14.4/installation.md (1)
126-127: Clarify deprecation timeline for the Folly option key migration.The migration note explains that the option key was renamed from
with-folly-no-couroutinestowith-folly-no-coroutines, but it doesn't specify when the old key will be removed. Add clarity on the deprecation timeline to help users plan their config updates.Note migration: - - This option key was renamed from `with-folly-no-couroutines` to `with-folly-no-coroutines`. Update your Expo config accordingly. For compatibility, the plugin temporarily accepts the old key and logs a deprecation warning. + - This option key was renamed from `with-folly-no-couroutines` to `with-folly-no-coroutines`. Update your Expo config accordingly. For compatibility, the plugin temporarily accepts the old key and logs a deprecation warning (to be removed in a future release).docs/versioned_docs/version-14.4/examples/available-purchases.md (2)
27-60: Unused destructured variable reduces clarity.
activeSubscriptionsis destructured from the hook but never used in therestorefunction. Either use it to display active subscriptions in the restore flow or remove it from the destructuring to avoid confusion about what data is being processed.
18-18: Consider more professional documentation tone.The phrase "vibe-coded with Claude" is informal and may not be appropriate in production documentation. Consider revising to a more neutral description such as: "Note that the example code is intentionally verbose for educational purposes."
docs/versioned_docs/version-14.4/guides/alternative-billing.md (2)
52-52: Remove redundant phrase.The phrase "outside of" is redundant; simplify to "outside" for clarity.
-allow you to use external payment systems alongside or instead of the App Store/Google Play billing. +allow you to use external payment systems alongside or instead of App Store/Google Play billing.
1-428: Consider documenting the newstorefield andIapStoretype in alternative billing context.The PR introduces a new
storefield toPurchaseand a newIapStoretype ('unknown' | 'apple' | 'google' | 'horizon'). While the alternative billing guide covers purchasing workflows, it does not reference these type changes. Consider clarifying how these fields relate to alternative billing flows, or whether users need to be aware of thestorefield when handling purchases in alternative billing scenarios.Do the alternative billing examples or verification workflows need to reference or check the
storefield to distinguish between platform sources? This would help users understand the relationship between alternative billing modes and the purchase store information.docs/versioned_docs/version-14.4/sponsors.md (1)
5-11: Split MDX imports onto separate lines for readabilityConsider formatting the imports as separate statements to match typical MDX/TS style and keep diffs cleaner:
import SponsorSection from '@site/src/components/SponsorSection'; import GreatFrontEndTopFixed from '@site/src/uis/GreatFrontEndTopFixed';docs/versioned_docs/version-14.4/examples/alternative-billing.md (1)
23-55: Align examples with new platform keys and versioned docs behaviorTwo small consistency issues plus one optional improvement:
- Use
apple/ios/androidin request payloadsGiven the updated
RequestPurchasePropsByPlatformstypes and docs that introduceapple/ios/android, it would be clearer if the examples here used the new keys as the primary pattern, e.g.:await requestPurchase({ request: { apple: { sku: product.productId, quantity: 1, }, }, type: 'in-app', useAlternativeBilling: true, });and for Android user-choice:
request: { google: { skus: [product.productId], }, },If you still want to show
ios/android, maybe call them out explicitly as deprecated/fallback fields.
- Prefer versioned/relative links from versioned docs
Links like
/docs/guides/alternative-billing,/docs/guides/error-handling, and/docs/examples/purchase-flowwill route to the current docs version, not necessarily 14.4. If the intent is to keep users within the 14.4 snapshot, consider using relative links such as:
../guides/alternative-billing../guides/error-handling../examples/purchase-flow
- (Optional) Showcase error helper utilities in examples
To mirror the recommended runtime usage, you might tweak the error handling samples to use helpers such as
isUserCancelledErrorandgetUserFriendlyErrorMessageinstead of directly checkingerror.codeorerror.message. This would keep the docs aligned with the normalizedPurchaseErrorsurface and helper APIs. Based on learnings, this is the preferred pattern in example code.Also applies to: 69-118, 132-169, 193-209, 235-237
docs/versioned_docs/version-14.4/getting-started/setup-horizon.md (1)
11-21: Minor wording tweak: hyphenate “react-native-iap-specific”In “This guide focuses on react-native-iap specific configuration.” you may want to hyphenate as:
This guide focuses on react-native-iap-specific configuration.
Purely a grammar/readability polish; the rest of the guide content and structure look good.
docs/versioned_docs/version-14.4/examples/subscription-flow.md (1)
112-114: Fix markdown formatting for code block. The Markdown specification requires blank lines surrounding fenced code blocks and language specifiers for syntax highlighting.- ``` + ```bash EXPO_PUBLIC_IAPKIT_API_KEY=your_api_key_here - ``` + ```docs/versioned_docs/version-14.4/api/methods/core-methods.md (1)
130-134: Minor style note on platform differences explanation. Consider rephrasing for clarity:- > - **iOS**: Can only purchase one product at a time (uses `sku: string`) + > - **iOS**: Can purchase only one product at a time (uses `sku: string`)docs/versioned_docs/version-14.4/guides/troubleshooting.md (1)
373-376: Fix markdown formatting for code block. Add a blank line before the fenced code block per Markdown spec:// android/app/src/main/AndroidManifest.xml + ```xmlsrc/index.ts (1)
1426-1445: Update JSDoc to reflect the new return shape.The function implementation has changed to return
iapkitas a single object (or null) instead of an array, and now includes anerrorsfield. The JSDoc should be updated to document this.Update the JSDoc example to clarify the new return shape:
* @example * ```typescript * const result = await verifyPurchaseWithProvider({ * provider: 'iapkit', * iapkit: { * apiKey: 'your-api-key', * apple: { jws: purchase.purchaseToken }, * google: { purchaseToken: purchase.purchaseToken }, * }, * }); + * + * // result.iapkit is a single object (or null), not an array + * if (result.iapkit?.isValid) { + * console.log('Purchase valid, store:', result.iapkit.store); + * } + * + * // Check for errors if iapkit is null + * if (result.errors) { + * console.error('Verification errors:', result.errors); + * } * ```docs/versioned_docs/version-14.4/api/types.md (1)
195-195: Remove strayEOFtoken from the rendered docsThe closing sentence currently ends with “EOF”, which looks like a leftover editor marker rather than intentional content. Consider trimming it so the page ends cleanly:
-If you need to regenerate types place new schema definitions under the GraphQL inputs and rerun the generator. EOF +If you need to regenerate types place new schema definitions under the GraphQL inputs and rerun the generator.src/utils/type-bridge.ts (1)
34-37: Store normalization looks correct; consider logging unexpected store valuesThe new
IapStoreplumbing andnormalizeStoreusage inconvertNitroPurchaseToPurchaseare consistent and type‑safe:
normalizeStorelowercases the incomingnitroPurchase.storestring and maps it to'apple' | 'google' | 'horizon' | 'unknown'.- The resulting
storeis attached to bothPurchaseIOSandPurchaseAndroid, matching the updated generated types.One optional improvement: for debugging and forward compatibility, you might want to log unexpected store strings before defaulting to
'unknown', similar to hownormalizeProductTypeIOSwarns on unknown product types. For example:function normalizeStore(value?: Nullable<string>): IapStore { - switch (value?.toLowerCase()) { + const normalized = value?.toLowerCase(); + switch (normalized) { case 'apple': return STORE_APPLE; case 'google': return STORE_GOOGLE; case 'horizon': return STORE_HORIZON; default: - return STORE_UNKNOWN; + if (value) { + RnIapConsole.warn( + `[react-native-iap] Unknown store "${value}", defaulting to "unknown".`, + ); + } + return STORE_UNKNOWN; } }Not required for correctness, but it would make it easier to spot schema or bridge mismatches in development.
Also applies to: 65-76, 353-363, 450-458
docs/versioned_docs/version-14.4/getting-started/setup-android.md (1)
52-64: Missing dependency inuseEffectdependency array.The
fetchProductsfunction is called inside the effect but not listed in the dependency array, which may cause stale closure issues or lint warnings.React.useEffect(() => { if (connected) { // Fetch products and subscriptions fetchProducts({ skus: androidProductIds.filter((id) => !id.includes('subscription')), type: 'in-app', }); fetchProducts({ skus: androidProductIds.filter((id) => id.includes('subscription')), type: 'subs', }); } - }, [connected]); + }, [connected, fetchProducts]);ios/HybridRnIap.swift (1)
378-395: Remove redundantnilinitializations.SwiftLint correctly flags that initializing optional variables with
nilis redundant in Swift, as optionals default tonil.- var nitroIapkitResult: NitroVerifyPurchaseWithIapkitResult? = nil + var nitroIapkitResult: NitroVerifyPurchaseWithIapkitResult? if let item = result.iapkit { nitroIapkitResult = NitroVerifyPurchaseWithIapkitResult( isValid: item.isValid, state: IapkitPurchaseState(fromString: item.state.rawValue) ?? .unknown, store: IapkitStore(fromString: item.store.rawValue) ?? .apple ) } // Convert errors if present - var nitroErrors: [NitroVerifyPurchaseWithProviderError]? = nil + var nitroErrors: [NitroVerifyPurchaseWithProviderError]? if let errors = result.errors {docs/docs/api/types.md (1)
136-155: Documentation correctly reflects the property key migration.The deprecation notices for
ios/androidin favor ofapple/However, consider adding documentation for the other significant type changes in this PR:
- The new
IapStoretype ('unknown' | 'apple' | 'google' | 'horizon')- The new
storefield onPurchaseCommon(and the deprecatedplatformfield)- The
VerifyPurchaseWithProviderResult.errorsfield andVerifyPurchaseWithProviderErrortypeThese are breaking changes that API consumers would benefit from seeing documented.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
example/ios/Podfile.lockis excluded by!**/*.lock
📒 Files selected for processing (53)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt(3 hunks)docs/docs/api/methods/core-methods.md(2 hunks)docs/docs/api/types.md(1 hunks)docs/docs/examples/purchase-flow.md(2 hunks)docs/docs/intro.md(1 hunks)docs/docusaurus.config.ts(1 hunks)docs/versioned_docs/version-14.4/api/error-codes.md(1 hunks)docs/versioned_docs/version-14.4/api/error-handling.md(1 hunks)docs/versioned_docs/version-14.4/api/index.md(1 hunks)docs/versioned_docs/version-14.4/api/methods/_category_.json(1 hunks)docs/versioned_docs/version-14.4/api/methods/core-methods.md(1 hunks)docs/versioned_docs/version-14.4/api/methods/listeners.md(1 hunks)docs/versioned_docs/version-14.4/api/types.md(1 hunks)docs/versioned_docs/version-14.4/api/use-iap.md(1 hunks)docs/versioned_docs/version-14.4/examples/_category_.json(1 hunks)docs/versioned_docs/version-14.4/examples/alternative-billing.md(1 hunks)docs/versioned_docs/version-14.4/examples/available-purchases.md(1 hunks)docs/versioned_docs/version-14.4/examples/offer-code.md(1 hunks)docs/versioned_docs/version-14.4/examples/purchase-flow.md(1 hunks)docs/versioned_docs/version-14.4/examples/subscription-flow.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/installation.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/setup-android.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/setup-horizon.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/setup-ios.md(1 hunks)docs/versioned_docs/version-14.4/guides/_category_.json(1 hunks)docs/versioned_docs/version-14.4/guides/alternative-billing.md(1 hunks)docs/versioned_docs/version-14.4/guides/error-handling.md(1 hunks)docs/versioned_docs/version-14.4/guides/expo-plugin.md(1 hunks)docs/versioned_docs/version-14.4/guides/faq.md(1 hunks)docs/versioned_docs/version-14.4/guides/lifecycle.md(1 hunks)docs/versioned_docs/version-14.4/guides/offer-code-redemption.md(1 hunks)docs/versioned_docs/version-14.4/guides/purchases.md(1 hunks)docs/versioned_docs/version-14.4/guides/subscription-offers.md(1 hunks)docs/versioned_docs/version-14.4/guides/subscription-validation.md(1 hunks)docs/versioned_docs/version-14.4/guides/support.md(1 hunks)docs/versioned_docs/version-14.4/guides/troubleshooting.md(1 hunks)docs/versioned_docs/version-14.4/installation.md(1 hunks)docs/versioned_docs/version-14.4/intro.md(1 hunks)docs/versioned_docs/version-14.4/sponsors.md(1 hunks)docs/versioned_sidebars/version-14.4-sidebars.json(1 hunks)docs/versions.json(1 hunks)example/screens/PurchaseFlow.tsx(1 hunks)example/screens/SubscriptionFlow.tsx(1 hunks)ios/HybridRnIap.swift(1 hunks)ios/RnIapHelper.swift(1 hunks)openiap-versions.json(1 hunks)src/__tests__/hooks/useIAP.test.ts(1 hunks)src/__tests__/index.test.ts(8 hunks)src/__tests__/utils/type-bridge.test.ts(2 hunks)src/index.ts(1 hunks)src/specs/RnIap.nitro.ts(4 hunks)src/types.ts(9 hunks)src/utils/type-bridge.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When declaring API params/results in TS modules, import canonical types from src/types.ts rather than creating ad‑hoc interfaces.
Files:
src/__tests__/index.test.tssrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tssrc/index.tssrc/types.tssrc/specs/RnIap.nitro.tssrc/utils/type-bridge.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use type-only imports when importing types (import type).
Files:
src/__tests__/index.test.tsdocs/docusaurus.config.tsexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxsrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tssrc/index.tssrc/types.tssrc/specs/RnIap.nitro.tssrc/utils/type-bridge.ts
{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (CLAUDE.md)
{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}: Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
Use Platform.OS checks for platform-specific logic in React Native code.
Files:
src/__tests__/index.test.tsexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxsrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tssrc/index.tssrc/types.tssrc/specs/RnIap.nitro.tssrc/utils/type-bridge.ts
{example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (CLAUDE.md)
{example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}: In useIAP hook usage, do not expect returned data from methods that return Promise; consume state from the hook instead.
Do not call parseErrorStringToJsonObj() in app/user code; errors are already normalized by the library.
Files:
example/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsx
{ios/**/*.swift,android/src/main/java/**/*.kt}
📄 CodeRabbit inference engine (CLAUDE.md)
Follow the native class function ordering: (1) properties/init, (2) public cross-platform methods, (3) platform-specific public methods (IOS/Android suffix), (4) event listener methods, (5) private helpers.
Files:
ios/HybridRnIap.swiftios/RnIapHelper.swiftandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
src/types.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Never edit src/types.ts manually; it is generated. Import canonical types from this file instead of defining ad‑hoc interfaces.
Files:
src/types.ts
**/*.nitro.ts
📄 CodeRabbit inference engine (CLAUDE.md)
After modifying any .nitro.ts interface files, regenerate Nitro bridge files (yarn specs).
Files:
src/specs/RnIap.nitro.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3002
File: docs/docs/getting-started/setup-ios.md:55-60
Timestamp: 2025-09-14T00:13:04.055Z
Learning: The validateReceipt function from useIAP hook in react-native-iap expects a transaction identifier as parameter, which is accessed via purchase.id (not purchase.productId). This is confirmed by the maintainer hyochan and aligns with the library's migration from purchase.transactionId to purchase.id.
📚 Learning: 2025-09-14T00:13:04.055Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3002
File: docs/docs/getting-started/setup-ios.md:55-60
Timestamp: 2025-09-14T00:13:04.055Z
Learning: The validateReceipt function from useIAP hook in react-native-iap expects a transaction identifier as parameter, which is accessed via purchase.id (not purchase.productId). This is confirmed by the maintainer hyochan and aligns with the library's migration from purchase.transactionId to purchase.id.
Applied to files:
docs/versioned_docs/version-14.4/guides/lifecycle.mddocs/versioned_docs/version-14.4/getting-started/setup-ios.mdsrc/__tests__/index.test.tsdocs/versioned_docs/version-14.4/examples/purchase-flow.mddocs/versioned_docs/version-14.4/guides/troubleshooting.mddocs/versioned_docs/version-14.4/guides/faq.mdexample/screens/PurchaseFlow.tsxdocs/versioned_docs/version-14.4/api/methods/listeners.mddocs/versioned_docs/version-14.4/guides/subscription-validation.mddocs/versioned_docs/version-14.4/api/use-iap.mddocs/docs/examples/purchase-flow.mdexample/screens/SubscriptionFlow.tsxsrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tsdocs/docs/intro.mddocs/versioned_docs/version-14.4/api/methods/core-methods.mdios/HybridRnIap.swiftsrc/index.tsdocs/versioned_docs/version-14.4/guides/purchases.mdsrc/types.tssrc/specs/RnIap.nitro.tsdocs/docs/api/methods/core-methods.mdsrc/utils/type-bridge.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : In useIAP hook usage, do not expect returned data from methods that return Promise<void>; consume state from the hook instead.
Applied to files:
docs/versioned_docs/version-14.4/guides/lifecycle.mddocs/versioned_docs/version-14.4/api/error-codes.mdsrc/__tests__/index.test.tsdocs/versioned_docs/version-14.4/examples/purchase-flow.mddocs/versioned_docs/version-14.4/guides/troubleshooting.mddocs/versioned_docs/version-14.4/guides/error-handling.mddocs/versioned_docs/version-14.4/guides/faq.mddocs/versioned_docs/version-14.4/examples/available-purchases.mdexample/screens/PurchaseFlow.tsxdocs/versioned_docs/version-14.4/api/methods/listeners.mddocs/versioned_docs/version-14.4/guides/subscription-validation.mddocs/versioned_docs/version-14.4/api/use-iap.mddocs/docs/examples/purchase-flow.mdexample/screens/SubscriptionFlow.tsxdocs/docs/api/types.mddocs/versioned_docs/version-14.4/examples/alternative-billing.mddocs/versioned_docs/version-14.4/api/types.mdsrc/__tests__/hooks/useIAP.test.tsdocs/versioned_docs/version-14.4/api/methods/core-methods.mddocs/versioned_docs/version-14.4/guides/subscription-offers.mddocs/versioned_docs/version-14.4/api/index.mddocs/versioned_docs/version-14.4/examples/subscription-flow.mdsrc/index.tsdocs/versioned_docs/version-14.4/guides/purchases.mdsrc/types.tsdocs/versioned_docs/version-14.4/guides/expo-plugin.mdsrc/specs/RnIap.nitro.tsdocs/docs/api/methods/core-methods.mdsrc/utils/type-bridge.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
Applied to files:
docs/versioned_docs/version-14.4/api/error-codes.mdsrc/__tests__/index.test.tsdocs/versioned_docs/version-14.4/guides/troubleshooting.mddocs/versioned_docs/version-14.4/guides/error-handling.mdexample/screens/PurchaseFlow.tsxdocs/versioned_docs/version-14.4/api/methods/listeners.mddocs/versioned_docs/version-14.4/api/use-iap.mddocs/versioned_docs/version-14.4/api/error-handling.mdexample/screens/SubscriptionFlow.tsxdocs/docs/api/types.mddocs/versioned_docs/version-14.4/examples/alternative-billing.mddocs/versioned_docs/version-14.4/api/types.mddocs/docs/intro.mddocs/versioned_docs/version-14.4/api/methods/core-methods.mddocs/versioned_docs/version-14.4/guides/subscription-offers.mdsrc/index.tsdocs/versioned_docs/version-14.4/guides/purchases.mdsrc/types.tsdocs/docs/api/methods/core-methods.mdsrc/utils/type-bridge.ts
📚 Learning: 2025-09-18T16:45:10.582Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.
Applied to files:
src/__tests__/index.test.tsopeniap-versions.jsonexample/screens/PurchaseFlow.tsxdocs/docs/examples/purchase-flow.mdexample/screens/SubscriptionFlow.tsxdocs/docs/api/types.mddocs/versioned_docs/version-14.4/api/types.mdsrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tsdocs/docs/intro.mddocs/versioned_docs/version-14.4/api/methods/core-methods.mdios/HybridRnIap.swiftsrc/index.tssrc/types.tsandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.ktsrc/specs/RnIap.nitro.tsdocs/docs/api/methods/core-methods.mdsrc/utils/type-bridge.ts
📚 Learning: 2025-09-13T01:07:18.841Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 2999
File: android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt:644-660
Timestamp: 2025-09-13T01:07:18.841Z
Learning: In Android IAP error handling: purchaseToken and productId are distinct properties - purchaseToken identifies a completed purchase transaction (should be null in error cases), while productId is the product SKU for context
Applied to files:
docs/versioned_docs/version-14.4/api/use-iap.mddocs/versioned_docs/version-14.4/api/error-handling.mdsrc/__tests__/hooks/useIAP.test.tssrc/__tests__/utils/type-bridge.test.tsdocs/versioned_docs/version-14.4/api/methods/core-methods.mddocs/versioned_docs/version-14.4/guides/purchases.mdsrc/types.tsandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.ktsrc/specs/RnIap.nitro.tsdocs/docs/api/methods/core-methods.mdsrc/utils/type-bridge.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to src/types.ts : Never edit src/types.ts manually; it is generated. Import canonical types from this file instead of defining ad‑hoc interfaces.
Applied to files:
docs/versioned_docs/version-14.4/api/types.md
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to src/**/*.{ts,tsx} : When declaring API params/results in TS modules, import canonical types from src/types.ts rather than creating ad‑hoc interfaces.
Applied to files:
docs/versioned_docs/version-14.4/api/types.md
🧬 Code graph analysis (9)
src/__tests__/index.test.ts (2)
android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
result(14-16)ios/RnIapLog.swift (1)
result(41-43)
example/screens/PurchaseFlow.tsx (2)
android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
result(14-16)ios/RnIapLog.swift (1)
result(41-43)
ios/HybridRnIap.swift (2)
src/specs/RnIap.nitro.ts (5)
NitroVerifyPurchaseWithIapkitResult(239-243)IapkitPurchaseState(30-39)IapkitStore(41-41)NitroVerifyPurchaseWithProviderError(245-248)NitroVerifyPurchaseWithProviderResult(250-254)src/types.ts (1)
IapkitPurchaseState(186-186)
src/index.ts (2)
android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
result(14-16)ios/RnIapLog.swift (1)
result(41-43)
ios/RnIapHelper.swift (2)
src/specs/RnIap.nitro.ts (1)
IapStore(44-44)src/types.ts (1)
IapStore(183-183)
src/types.ts (1)
src/specs/RnIap.nitro.ts (1)
PurchaseVerificationProvider(47-47)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
src/specs/RnIap.nitro.ts (3)
NitroVerifyPurchaseWithIapkitResult(239-243)NitroVerifyPurchaseWithProviderError(245-248)NitroVerifyPurchaseWithProviderResult(250-254)
src/specs/RnIap.nitro.ts (1)
src/types.ts (2)
IapStore(183-183)IapPlatform(181-181)
src/utils/type-bridge.ts (2)
src/specs/RnIap.nitro.ts (1)
IapStore(44-44)src/types.ts (2)
IapStore(183-183)PurchaseIOS(508-548)
🪛 LanguageTool
docs/versioned_docs/version-14.4/guides/alternative-billing.md
[style] ~52-~52: This phrase is redundant. Consider using “outside”.
Context: ...les developers to offer payment options outside of the platform's standard billing systems...
(OUTSIDE_OF)
docs/versioned_docs/version-14.4/getting-started/setup-horizon.md
[grammar] ~19-~19: Use a hyphen to join words.
Context: ... This guide focuses on react-native-iap specific configuration. ::: ## Prerequ...
(QB_NEW_EN_HYPHEN)
docs/versioned_docs/version-14.4/getting-started/installation.md
[grammar] ~146-~146: Use a hyphen to join words.
Context: ...ogle Play Billing configuration ## Real world examples For detailed platform-sp...
(QB_NEW_EN_HYPHEN)
docs/versioned_docs/version-14.4/guides/faq.md
[grammar] ~47-~47: Use a hyphen to join words.
Context: ...s! react-native-iap works in both Expo managed and bare React Native projects. ...
(QB_NEW_EN_HYPHEN)
[locale-violation] ~261-~261: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ... a transient error that arrives shortly afterwards. Tip (dedupe in app logic): ```tsx im...
(AFTERWARDS_US)
[style] ~289-~289: This phrase is redundant. Consider writing “outcome”.
Context: ...on await requestPurchase(...) for the final outcome; multiple events and inter-session comp...
(FINAL_END)
[grammar] ~543-~543: Use a hyphen to join words.
Context: ...ue to 'internal' protection level(yoga related) **Root Cause:** TheappTransa...
(QB_NEW_EN_HYPHEN)
docs/versioned_docs/version-14.4/api/methods/listeners.md
[grammar] ~496-~496: Ensure spelling is correct
Context: ...our purchase listener. ## Alternative: useIAP Hook For simpler usage, consider using...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~496-~496: Ensure spelling is correct
Context: ...e listener. ## Alternative: useIAP Hook For simpler usage, consider using the `u...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
docs/versioned_docs/version-14.4/guides/support.md
[style] ~25-~25: Consider using a shorter alternative to avoid wordiness.
Context: ...he library is being used and how we can make it better. We appreciate your support! ## Become...
(MADE_IT_JJR)
docs/versioned_docs/version-14.4/guides/offer-code-redemption.md
[style] ~44-~44: This phrase is redundant (‘OS’ stands for ‘operating system’). Use simply “iOS”.
Context: ... The redemption sheet is handled by the iOS system - After successful redemption, purchase...
(ACRONYM_TAUTOLOGY)
docs/versioned_docs/version-14.4/installation.md
[grammar] ~146-~146: Use a hyphen to join words.
Context: ...ogle Play Billing configuration ## Real world examples For detailed platform-sp...
(QB_NEW_EN_HYPHEN)
docs/versioned_docs/version-14.4/api/methods/core-methods.md
[style] ~130-~130: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...tform Differences:** > > - iOS: Can only purchase one product at a time (uses `s...
(ONLY_EXCLUSIVELY_STYLE)
[grammar] ~615-~615: Use a hyphen to join words.
Context: ...ly helpers expose StoreKit and App Store specific capabilities. Most day‑to‑day f...
(QB_NEW_EN_HYPHEN)
[style] ~918-~918: This phrase is redundant. Consider using “outside”.
Context: ...les developers to offer payment options outside of the platform's standard billing systems...
(OUTSIDE_OF)
[uncategorized] ~983-~983: Do not mix variants of the same word (‘cancelled’ and ‘canceled’) within a single text.
Context: ...-trueif user accepted,false` if cancelled Platform: Android only #### creat...
(EN_EXACT_COHERENCY_RULE)
docs/versioned_docs/version-14.4/guides/purchases.md
[grammar] ~24-~24: Ensure spelling is correct
Context: ...on()` after validation ## Recommended: useIAP Hook ```tsx const {products, requestPu...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~24-~24: Ensure spelling is correct
Context: ... validation ## Recommended: useIAP Hook tsx const {products, requestPurchase, finishTransaction} = useIAP({ onPurchaseSuccess: async (purchase) => { // Validate on your server, then finish await finishTransaction({purchase, isConsumable: true}); }, onPurchaseError: (error) => { if (error.code !== ErrorCode.UserCancelled) { Alert.alert('Error', error.message); } }, }); ## Direct Listeners ```tsx useEffect(() =>...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
docs/versioned_docs/version-14.4/guides/expo-plugin.md
[uncategorized] ~30-~30: The official name of this software platform is spelled with a capital “H”.
Context: ...on to AndroidManifest.xml. - Adds the io.github.hyochan.openiap:openiap-google depende...
(GITHUB)
🪛 markdownlint-cli2 (0.18.1)
docs/versioned_docs/version-14.4/guides/troubleshooting.md
373-373: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
docs/versioned_docs/version-14.4/examples/subscription-flow.md
112-112: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
112-112: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 SwiftLint (0.57.0)
ios/HybridRnIap.swift
[Warning] 378-378: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
[Warning] 387-387: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-android
- GitHub Check: build-ios
🔇 Additional comments (45)
docs/versioned_docs/version-14.4/guides/support.md (2)
1-7: Well-structured support documentation page.The frontmatter, component import, and overall structure are clean and follow documentation conventions. This provides clear guidance on obtaining community support.
9-26: Content is welcoming and comprehensive.The support sections effectively guide users to GitHub Issues, Slack for real-time help, and community showcasing. Tone and messaging encourage engagement.
Please verify that the external links are accessible and accurate:
- GitHub Issues: https://github.com/hyochan/react-native-iap/issues
- Slack community: https://hyo.dev/joinSlack
- GitHub discussion: #1343
Also confirm the relative path reference
../sponsorson line 29 correctly points to the sponsors documentation page in the version-14.4 structure.docs/versioned_docs/version-14.4/installation.md (2)
1-9: Documentation structure is clear and consistent.The frontmatter, component imports, and section organization follow the pattern of other version-14.4 documentation pages. The GreatFrontEndTopFixed header placement and Docusaurus conventions are correct.
143-144: All referenced documentation files exist in the version-14.4 structure—no action required.Verification confirms that all linked documentation files exist in
docs/versioned_docs/version-14.4/:
getting-started/setup-ios.md✓getting-started/setup-android.md✓examples/purchase-flow.md✓examples/subscription-flow.md✓examples/available-purchases.md✓examples/offer-code.md✓The relative links in the installation guide are valid and functional.
docs/versioned_docs/version-14.4/guides/alternative-billing.md (2)
239-239: The AlternativeBilling.tsx file exists at the referenced path (example/screens/AlternativeBilling.tsx), so the documentation link is valid and users can access the complete working example.
423-424: Internal documentation links are valid.The referenced pages exist:
/docs/api/methods/core-methods#alternative-billing-apis(line 916) and/docs/api/typesare both present in version 14.4 and properly published.docs/versioned_docs/version-14.4/examples/_category_.json (1)
1-8: Category config for v14.4 examples looks correctLabel, position, and generated index setup are consistent with Docusaurus conventions and the surrounding docs structure.
docs/versions.json (1)
1-1: Docs versions list is consistent with configAdding "14.4" as the newest version in the array matches the docs configuration where 14.5 is current and 14.4 is the latest versioned snapshot.
docs/docusaurus.config.ts (1)
53-60: Docs versioning update is coherentPromoting 14.5 to the current label and adding an explicit 14.4 version entry (with matching path) lines up with the new versions.json and the added 14.4 docs.
docs/versioned_sidebars/version-14.4-sidebars.json (1)
1-75: Sidebar structure for 14.4 is well-organizedThe tutorialSidebar groups getting started guides, API reference (including methods and listeners), examples (including the new alternative billing flow), and sponsors in a clear hierarchy that matches the new 14.4 docs.
openiap-versions.json (1)
2-4: Versions are correctly integrated into build system via dynamic loading; clarify gql usageThe openiap versions in the JSON file are properly utilized:
- iOS (Podspec): Reads
apple 1.3.0from openiap-versions.json and passes it to theopeniappod dependency- Android (Gradle): Reads
google 1.3.10from openiap-versions.json and applies it toopeniap-googleandopeniap-google-horizonimplementationsBoth build systems validate version format and existence, ensuring generation and runtime stay in sync through a single source of truth.
However, the
gql 1.3.0version is defined in the JSON but appears unused in the codebase. Clarify whether this is intentional or should be removed.docs/versioned_docs/version-14.4/intro.md (1)
1-153: LGTM. The introduction page provides a clear overview of react-native-iap and aligns well with the version 14.4 documentation structure.docs/versioned_docs/version-14.4/api/methods/listeners.md (1)
1-519: LGTM. The listeners documentation is comprehensive and provides clear examples for both functional and class components, with proper cleanup patterns demonstrated throughout.docs/versioned_docs/version-14.4/examples/subscription-flow.md (1)
104-137: iapkit result structure correctly documented. The IAPKit verification example correctly showsresult.iapkit.isValid(singular object), which aligns with the PR's change from array to optional object structure.docs/docs/intro.md (1)
90-91: Platform key naming updated correctly. The changes fromios/androidtoapple/docs/versioned_docs/version-14.4/api/methods/core-methods.md (1)
518-567: Verify iapkit result structure against implementation. The documentation showsiapkitas anArray, but the PR summary indicates it was changed from array to an optional single object with an additionalerrorsarray. The code example iterates overresult.iapkitand the TypeScript interface defines it asArray<{...}>, but this may not reflect the actual post-migration API structure.Please confirm:
- Is
iapkitnow a single nullable object (not an array)?- Should the type be
iapkit?: { isValid, state, store } | nullinstead ofArray<...>?- Is there a new
errors?: VerifyPurchaseWithProviderError[]field?If confirmed, the documented examples and type definitions need updates:
- for (const item of result.iapkit) { - console.log('Is Valid:', item.isValid); - console.log('State:', item.state); - console.log('Store:', item.store); - } + if (result.iapkit) { + console.log('Is Valid:', result.iapkit.isValid); + console.log('State:', result.iapkit.state); + console.log('Store:', result.iapkit.store); + } else if (result.errors) { + console.error('Verification errors:', result.errors); + }And update the TypeScript interface:
interface VerifyPurchaseWithProviderResult { provider: 'iapkit'; - iapkit: Array<{ + iapkit?: { isValid: boolean; state: IapkitPurchaseState; - store: 'apple' | 'google'; - }>; + store: IapkitPurchaseState; + } | null; + errors?: Array<{code?: string | null; message: string}>; }docs/versioned_docs/version-14.4/guides/troubleshooting.md (1)
186-220: Verify purchase identifier property name. The code example usespurchase.transactionId, but learnings indicate the library migrated topurchase.idas the transaction identifier. Please confirm whethertransactionIdis still valid or if this should be updated to:const isValid = await validateReceiptOnServer({ - transactionId: purchase.transactionId, + transactionId: purchase.id, productId: purchase.productId, });docs/versioned_docs/version-14.4/guides/subscription-validation.md (1)
1-196: LGTM. The subscription validation guide provides clear explanations of StoreKit 2 and Google Play Billing APIs, with practical examples showing how to usegetAvailablePurchases,getActiveSubscriptions, and server-side validation patterns.docs/versioned_docs/version-14.4/api/use-iap.md (1)
1-632: LGTM. The useIAP hook documentation clearly explains the hook's behavior, especially the critical distinction that methods likefetchProductsandrequestPurchasereturnPromise<void>and update internal state (not returning data). The examples throughout follow best practices and the important notes section appropriately highlights the void-return pattern.src/__tests__/hooks/useIAP.test.ts (1)
81-90: LGTM!The addition of the
store: 'apple'field correctly aligns the test fixture with the newIapStoretype introduced in this PR. The value matches the platform ('ios' → 'apple').docs/versioned_docs/version-14.4/api/methods/_category_.json (1)
1-4: LGTM!Category configuration is properly structured for the Methods documentation section.
src/__tests__/utils/type-bridge.test.ts (2)
125-141: LGTM!The addition of
store: 'apple'correctly updates the iOS purchase fixture to include the new store field with the appropriate value.
143-161: LGTM!The addition of
store: 'google'correctly updates the Android purchase fixture to include the new store field with the appropriate value.docs/versioned_docs/version-14.4/guides/_category_.json (1)
1-4: LGTM!Category configuration is properly structured for the Guides documentation section.
ios/RnIapHelper.swift (1)
112-118: LGTM!The store field initialization correctly:
- Reads from the dictionary using a safe optional unwrap pattern
- Uses the
IapStore(fromString:)initializer for type-safe conversion- Defaults to
.applewhen missing or invalid, which is appropriate for iOS platform- Follows the same pattern as the platform field handling above
src/index.ts (1)
1462-1472: LGTM!The implementation correctly transforms the verification result:
- Returns
iapkitas a single object (withisValid,state,store) ornullinstead of an array- Adds the new
errorsfield with proper null handling- This aligns with the PR objectives to change the iapkit parameter from array to object
This is a breaking change for consumers who previously accessed
result.iapkit[0], but it's necessary and well-documented in the PR.docs/versioned_docs/version-14.4/api/index.md (1)
1-54: LGTM!The API reference landing page is well-structured with:
- Clear organization of available APIs
- Appropriate links to OpenIAP for canonical reference
- Simple Quick Start example demonstrating useIAP hook
- Helpful resource links
The documentation provides a good entry point for developers exploring the API.
docs/versioned_docs/version-14.4/guides/expo-plugin.md (1)
15-60: Expo plugin guide looks consistent and clearThe described plugin configuration (permissions, OpenIAP dependency, CocoaPods source, and the optional Folly workaround flag) matches the expected responsibilities of the config plugin and reads clearly. No changes needed from a code/API perspective.
docs/versioned_docs/version-14.4/examples/offer-code.md (1)
21-71: Offer code example is internally consistent and matches documented behaviourThe iOS example correctly treats
presentCodeRedemptionSheetIOSas not returning a value, and the Android flow usesLinking.openURLplusgetAvailablePurchases/getActiveSubscriptionswithout assuming return data from the hook methods. This looks good as a reference implementation.example/screens/SubscriptionFlow.tsx (1)
1523-1538: LGTM! Verification result handling correctly updated.The code properly handles the new verification result structure where
result.iapkitis a single object (or undefined) andresult.errorsis an optional array. The implementation:
- Checks for
result.iapkitpresence and displays verification details- Falls back to aggregating and displaying error messages from
result.errors- Provides clear user feedback in both scenarios
example/screens/PurchaseFlow.tsx (1)
533-548: LGTM! Verification result handling properly updated.The implementation correctly handles the new verification result structure:
- Accesses
result.iapkitas a single object withisValid,state, andstoreproperties- Provides fallback error handling via
result.errorsarray when iapkit is absent- Displays clear verification feedback to users
This is consistent with the changes in SubscriptionFlow.tsx.
src/__tests__/index.test.ts (1)
1143-1180: LGTM!Test correctly updated to verify single-object
iapkitstructure with proper optional chaining for assertions (result.iapkit?.isValid,result.iapkit?.state,result.iapkit?.store).docs/versioned_docs/version-14.4/guides/faq.md (1)
1-756: Well-structured and comprehensive FAQ documentation.The FAQ covers essential topics including setup, testing, error handling, migration, and platform-specific issues with practical code examples and troubleshooting tips.
ios/HybridRnIap.swift (1)
352-406: LGTM!The
verifyPurchaseWithProviderimplementation correctly transforms the OpenIAP result to Nitro types, handling:
- Single optional
iapkitobject instead of array- New optional
errorsarray mapping- Proper enum conversions with fallback defaults
The changes align with the updated type definitions in
src/specs/RnIap.nitro.ts.docs/docs/api/methods/core-methods.md (1)
547-575: LGTM!The type definitions correctly document the updated API:
iapkitas optional single object withIapStoretype forstore- New
errorsarray for verification error handling- New
IapStoretype including'horizon'value- Expanded
IapkitPurchaseStateunionandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (3)
925-925: LGTM!The new
storefield is correctly populated using themapIapStorehelper, aligning with the updatedNitroPurchaseinterface that now includes astore: IapStorefield.
974-982: LGTM!The
mapIapStorehelper correctly maps all OpenIAPIapStoreenum values (Apple,Horizon,Unknown) to their corresponding NitroIapStorevalues. The function follows the established pattern of other mapping functions in this file and is placed correctly in the private helpers section.
1157-1178: LGTM! The iapkit conversion aligns with the updated API contract.The refactoring correctly:
- Changes from array-based to single nullable
iapkitresult- Adds proper
errorsarray conversion- Updates logging to use a boolean
hasIapkitcheckThis matches the updated
NitroVerifyPurchaseWithProviderResultinterface whereiapkitis nowNitroVerifyPurchaseWithIapkitResult | nullinstead of an array.src/types.ts (3)
183-184: LGTM! NewIapStoretype andstorefield additions.The new
IapStoretype correctly includes all supported stores ('unknown' | 'apple' | 'google' | 'horizon'), and thestorefield is consistently added toPurchaseCommon,PurchaseAndroid, andPurchaseIOSwith appropriate JSDoc documentation.Also applies to: 470-471, 497-498, 540-541
911-926: LGTM! Verification result type changes align with the API migration.The changes correctly:
- Add
VerifyPurchaseWithProviderErrorinterface withcode(optional) andmessage(required)- Update
VerifyPurchaseWithProviderResultto have optionalerrorsarray and changeiapkitfrom array to optional single objectThis matches the PR objective of changing
verifyPurchaseWithProvideriapkit parameter from an array to an object.
731-738: LGTM! Platform-specific property migration with backward compatibility.The addition of
apple/ios/androidkeys in bothRequestPurchasePropsByPlatformsandRequestSubscriptionPropsByPlatformsmaintains backward compatibility while guiding users toward the new naming convention.Also applies to: 769-776
src/specs/RnIap.nitro.ts (4)
43-44: LGTM!The
IapStoretype definition correctly mirrors the type insrc/types.ts, ensuring consistency between the Nitro spec and the canonical types.
91-98: LGTM! NitroPurchaseRequest updated with store-based naming.The interface correctly adds
appleandiosandandroidwith deprecation notices for backward compatibility. This aligns with the broader migration from platform-based to store-based naming.
245-253: LGTM! Verification types updated to match new API contract.The changes correctly:
- Add
NitroVerifyPurchaseWithProviderErrorwith optionalcodeand requiredmessage- Update
NitroVerifyPurchaseWithProviderResultto haveiapkitas optional single object (was array) and add optionalerrorsarrayThis matches the corresponding changes in
src/types.tsand the Android/iOS native implementations.
270-273: LGTM! NitroPurchase updated with store field, and Nitro bridge files have been regenerated.The
platformfield is correctly marked as deprecated with guidance to usestoreinstead. The newstore: IapStorefield is properly documented and the corresponding Nitro bridge files (HybridRnIap.swiftandHybridRnIap.kt) have been regenerated to include the store field mappings in their conversion logic.
- Remove IapkitStore type, use IapStore instead - Fix NitroVerifyPurchaseWithIapkitResult.store type to IapStore - Fix 14.4 docs: use ios/android instead of apple/google - Fix validateReceipt example to use purchase.id - Fix getUserFriendlyMessage return type to string | null - Fix withIAPContext HOC usage example - Fix presentCodeRedemptionSheet to presentCodeRedemptionSheetIOS - Add missing useEffect import in offer-code-redemption - Fix "Real world" to "Real-world" hyphenation - Fix handlePurchase to use event-driven pattern - Fix confusing self-referential FAQ question
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
docs/versioned_docs/version-14.4/guides/faq.md (2)
381-383: Fix self-referential library comparison.The text states "
react-native-iapis the official successor toreact-native-iap", which is self-referential and confusing. This should clarify what library is being migrated from.-`react-native-iap` is the official successor to `react-native-iap`. The migration is straightforward with these key changes: +`react-native-iap` is the official successor to the older `react-native-iap` package. The migration is straightforward with these key changes:Or, if referring to a different package name, clarify the actual predecessor library name.
401-403: Fix incorrectwithIAPContextusage pattern.
withIAPContextis a Higher-Order Component (HOC), not a JSX element. The syntax shown would not execute—it should wrap the component function, not render as a tag.-const App = withIAPContext(() => { - return <YourApp />; -}); +const AppWithIAP = withIAPContext(() => { + return <YourApp />; +}); + +export default AppWithIAP;Alternatively, if the library exposes a context provider, use the provider form:
<IAPContextProvider> <YourApp /> </IAPContextProvider>
🧹 Nitpick comments (5)
docs/versioned_docs/version-14.4/guides/faq.md (2)
537-537: Add hyphen to compound modifier.The phrase "yoga related" should use a hyphen to form a compound adjective: "yoga-related".
-- `Property 'unit' is inaccessible due to 'internal' protection level` (yoga related) +- `Property 'unit' is inaccessible due to 'internal' protection level` (yoga-related)
261-261: Use American English spelling preference.In American English, "afterward" is preferred over "afterwards".
-This can briefly happen due to StoreKit 2 event ordering and native background work. If you've already received a success and processed it, you can safely ignore a transient error that arrives shortly afterwards. +This can briefly happen due to StoreKit 2 event ordering and native background work. If you've already received a success and processed it, you can safely ignore a transient error that arrives shortly afterward.docs/versioned_docs/version-14.4/guides/offer-code-redemption.md (1)
82-119: Wrap the hook usage in a React component for accuracy.The
useEffecthook at lines 109-118 is shown outside of a React component, which would be invalid in actual code. Consider wrapping the complete example in a proper component function to avoid confusion.```typescript import {useEffect} from 'react'; import {Platform} from 'react-native'; import { presentCodeRedemptionSheetIOS, openRedeemOfferCodeAndroid, purchaseUpdatedListener, } from 'react-native-iap'; +const RedeemCodeScreen = () => { const handleRedeemCode = async () => { try { if (Platform.OS === 'ios') { // Present native iOS redemption sheet const result = await presentCodeRedemptionSheetIOS(); if (result) { console.log('Redemption sheet presented'); } } else if (Platform.OS === 'android') { // Open Play Store for Android await openRedeemOfferCodeAndroid(); } } catch (error) { console.error('Error redeeming code:', error); } }; // Set up listener for purchase updates after redemption useEffect(() => { const subscription = purchaseUpdatedListener((purchase) => { console.log('Purchase updated after redemption:', purchase); // Handle the new purchase/subscription }); return () => { subscription.remove(); }; }, []); + + return ( + // Your component JSX here + ); +};</blockquote></details> <details> <summary>ios/HybridRnIap.swift (2)</summary><blockquote> `378-378`: **Remove redundant nil initialization.** SwiftLint reports that initializing an optional variable with `nil` is redundant since optionals default to `nil`. ```diff - var nitroIapkitResult: NitroVerifyPurchaseWithIapkitResult? = nil + var nitroIapkitResult: NitroVerifyPurchaseWithIapkitResult?
387-387: Remove redundant nil initialization.SwiftLint reports that initializing an optional variable with
nilis redundant.- var nitroErrors: [NitroVerifyPurchaseWithProviderError]? = nil + var nitroErrors: [NitroVerifyPurchaseWithProviderError]?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt(4 hunks)docs/versioned_docs/version-14.4/api/error-handling.md(1 hunks)docs/versioned_docs/version-14.4/api/types.md(1 hunks)docs/versioned_docs/version-14.4/examples/available-purchases.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/installation.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/setup-android.md(1 hunks)docs/versioned_docs/version-14.4/getting-started/setup-ios.md(1 hunks)docs/versioned_docs/version-14.4/guides/error-handling.md(1 hunks)docs/versioned_docs/version-14.4/guides/faq.md(1 hunks)docs/versioned_docs/version-14.4/guides/offer-code-redemption.md(1 hunks)docs/versioned_docs/version-14.4/installation.md(1 hunks)ios/HybridRnIap.swift(1 hunks)src/specs/RnIap.nitro.ts(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/versioned_docs/version-14.4/guides/error-handling.md
🚧 Files skipped from review as they are similar to previous changes (5)
- docs/versioned_docs/version-14.4/getting-started/setup-ios.md
- docs/versioned_docs/version-14.4/getting-started/setup-android.md
- docs/versioned_docs/version-14.4/examples/available-purchases.md
- docs/versioned_docs/version-14.4/api/error-handling.md
- docs/versioned_docs/version-14.4/api/types.md
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When declaring API params/results in TS modules, import canonical types from src/types.ts rather than creating ad‑hoc interfaces.
Files:
src/specs/RnIap.nitro.ts
**/*.nitro.ts
📄 CodeRabbit inference engine (CLAUDE.md)
After modifying any .nitro.ts interface files, regenerate Nitro bridge files (yarn specs).
Files:
src/specs/RnIap.nitro.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use type-only imports when importing types (import type).
Files:
src/specs/RnIap.nitro.ts
{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (CLAUDE.md)
{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}: Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
Use Platform.OS checks for platform-specific logic in React Native code.
Files:
src/specs/RnIap.nitro.ts
{ios/**/*.swift,android/src/main/java/**/*.kt}
📄 CodeRabbit inference engine (CLAUDE.md)
Follow the native class function ordering: (1) properties/init, (2) public cross-platform methods, (3) platform-specific public methods (IOS/Android suffix), (4) event listener methods, (5) private helpers.
Files:
ios/HybridRnIap.swiftandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
🧠 Learnings (6)
📓 Common learnings
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3002
File: docs/docs/getting-started/setup-ios.md:55-60
Timestamp: 2025-09-14T00:13:04.055Z
Learning: The validateReceipt function from useIAP hook in react-native-iap expects a transaction identifier as parameter, which is accessed via purchase.id (not purchase.productId). This is confirmed by the maintainer hyochan and aligns with the library's migration from purchase.transactionId to purchase.id.
📚 Learning: 2025-09-18T16:45:10.582Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.
Applied to files:
src/specs/RnIap.nitro.tsios/HybridRnIap.swift
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : In useIAP hook usage, do not expect returned data from methods that return Promise<void>; consume state from the hook instead.
Applied to files:
src/specs/RnIap.nitro.tsdocs/versioned_docs/version-14.4/guides/faq.mddocs/versioned_docs/version-14.4/guides/offer-code-redemption.md
📚 Learning: 2025-09-14T00:13:04.055Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3002
File: docs/docs/getting-started/setup-ios.md:55-60
Timestamp: 2025-09-14T00:13:04.055Z
Learning: The validateReceipt function from useIAP hook in react-native-iap expects a transaction identifier as parameter, which is accessed via purchase.id (not purchase.productId). This is confirmed by the maintainer hyochan and aligns with the library's migration from purchase.transactionId to purchase.id.
Applied to files:
src/specs/RnIap.nitro.tsios/HybridRnIap.swiftdocs/versioned_docs/version-14.4/guides/faq.mddocs/versioned_docs/version-14.4/guides/offer-code-redemption.md
📚 Learning: 2025-09-13T01:07:18.841Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 2999
File: android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt:644-660
Timestamp: 2025-09-13T01:07:18.841Z
Learning: In Android IAP error handling: purchaseToken and productId are distinct properties - purchaseToken identifies a completed purchase transaction (should be null in error cases), while productId is the product SKU for context
Applied to files:
src/specs/RnIap.nitro.tsandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.ktdocs/versioned_docs/version-14.4/guides/faq.md
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
Applied to files:
docs/versioned_docs/version-14.4/guides/faq.mddocs/versioned_docs/version-14.4/guides/offer-code-redemption.md
🧬 Code graph analysis (3)
src/specs/RnIap.nitro.ts (1)
src/types.ts (2)
IapStore(183-183)IapPlatform(181-181)
ios/HybridRnIap.swift (2)
src/specs/RnIap.nitro.ts (5)
NitroVerifyPurchaseWithIapkitResult(237-241)IapkitPurchaseState(30-39)IapStore(42-42)NitroVerifyPurchaseWithProviderError(243-246)NitroVerifyPurchaseWithProviderResult(248-252)src/types.ts (2)
IapkitPurchaseState(186-186)IapStore(183-183)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
src/specs/RnIap.nitro.ts (3)
NitroVerifyPurchaseWithIapkitResult(237-241)NitroVerifyPurchaseWithProviderError(243-246)NitroVerifyPurchaseWithProviderResult(248-252)
🪛 LanguageTool
docs/versioned_docs/version-14.4/guides/faq.md
[grammar] ~47-~47: Use a hyphen to join words.
Context: ...s! react-native-iap works in both Expo managed and bare React Native projects. ...
(QB_NEW_EN_HYPHEN)
[locale-violation] ~261-~261: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ... a transient error that arrives shortly afterwards. Tip (dedupe in app logic): ```tsx im...
(AFTERWARDS_US)
[style] ~289-~289: This phrase is redundant. Consider writing “outcome”.
Context: ...on await requestPurchase(...) for the final outcome; multiple events and inter-session comp...
(FINAL_END)
[grammar] ~537-~537: Use a hyphen to join words.
Context: ...ue to 'internal' protection level(yoga related) **Root Cause:** TheappTransa...
(QB_NEW_EN_HYPHEN)
docs/versioned_docs/version-14.4/guides/offer-code-redemption.md
[style] ~44-~44: This phrase is redundant (‘OS’ stands for ‘operating system’). Use simply “iOS”.
Context: ... The redemption sheet is handled by the iOS system - After successful redemption, purchase...
(ACRONYM_TAUTOLOGY)
🪛 SwiftLint (0.57.0)
ios/HybridRnIap.swift
[Warning] 378-378: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
[Warning] 387-387: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-android
- GitHub Check: build-ios
🔇 Additional comments (14)
docs/versioned_docs/version-14.4/installation.md (4)
13-25: Compatibility section is clear and well-structured.The Nitro compatibility warning effectively communicates version requirements, provides fallback options (pre-Nitro 13.1.0), mentions the Swift 6 C++ interop workaround, and suggests expo-iap as an alternative for constrained environments. The progression from problem statement to solutions is logical.
37-51: Package installation section is concise and accurate.Covers npm installation, iOS Pod dependencies for React Native CLI projects, and mentions automatic native module linking. The distinction between React Native CLI and Expo workflows is appropriate for this section header.
55-137: Expo Configuration section is comprehensive.The section properly addresses the custom development client requirement, provides the critical Kotlin 2.0+ configuration with clear context (Google Play Billing v8.0.0 requirement), includes expo-dev-client installation, prebuild instructions, and a well-documented Folly workaround with migration guidance. The note at line 126-127 about the renamed config option (
with-folly-no-couroutines→with-folly-no-coroutines) with deprecation warning support is helpful for users with existing configurations.
5-5: No action needed. All referenced resources exist in the codebase: the GreatFrontEndTopFixed component is located atdocs/src/uis/GreatFrontEndTopFixed.tsx, and the relative paths to setup guides correctly resolve to files indocs/versioned_docs/version-14.4/getting-started/. The imports and references are valid.src/specs/RnIap.nitro.ts (4)
41-42: LGTM!The
IapStoretype is correctly defined with all supported store values ('unknown', 'apple', 'google', 'horizon'), aligning with the openiap 1.3.0 migration.
88-97: LGTM!The deprecation strategy for
ios/androidfields in favor ofapple/
237-251: LGTM!The verification result structure is updated correctly:
storenow usesIapStoretypeNitroVerifyPurchaseWithProviderErrorprovides structured error informationiapkitchanged from array to optional single objecterrorsarray added for surfacing verification errorsThese changes align with the openiap 1.3.0 API updates.
268-271: LGTM!The
NitroPurchaseinterface correctly adds the newstorefield withIapStoretype while deprecatingplatformwith appropriate documentation.docs/versioned_docs/version-14.4/getting-started/installation.md (1)
1-153: LGTM!Comprehensive installation guide covering:
- Clear compatibility notes for Nitro 14.x vs pre-Nitro versions
- Proper Expo configuration with Kotlin version requirements
- Helpful migration notes for the Folly coroutine workaround key rename
- Well-structured sections with appropriate cross-references
ios/HybridRnIap.swift (1)
376-400: LGTM on the verification result conversion logic.The conversion from OpenIAP result to Nitro types is correct:
- Single
iapkitobject with proper state and store mapping- Errors array populated when present
- Provider mapping with appropriate fallback
The changes align with the new
NitroVerifyPurchaseWithProviderResultstructure.android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (4)
974-982: LGTM!The
mapIapStorehelper correctly maps OpenIAP store values to NitroIapStoreenum values, handling all four cases: Apple, Google, Horizon, and Unknown.
925-925: LGTM!The
storefield is correctly added toNitroPurchaseusing the newmapIapStorehelper function.
1157-1179: LGTM!The verification result handling is correctly updated:
iapkitis now a single optional object viaresult.iapkit?.let { ... }- Errors are mapped to
NitroVerifyPurchaseWithProviderErrorarray- Result construction passes both
iapkitanderrorsfieldsThis aligns with the iOS implementation and the updated type definitions.
1441-1448: LGTM!The
mapIapkitStorefunction is updated to returnIapStoretype with correct mapping for all store values including the newHORIZONcase.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.