feat(android): support android billing client 8.2.0#3103
Conversation
- Update openiap-google to 1.3.11, openiap-gql to 1.3.1 - Change oneTimePurchaseOfferDetailsAndroid from single object to array - Add new discount types: DiscountDisplayInfoAndroid, DiscountAmountAndroid - Add LimitedQuantityInfoAndroid, PreorderDetailsAndroid, RentalDetailsAndroid - Add ValidTimeWindowAndroid for time-limited offers - Add isSuspendedAndroid field to PurchaseAndroid (Billing Library 8.1.0+) - Update example screens to display Android offer arrays in product details BREAKING CHANGE: oneTimePurchaseOfferDetailsAndroid is now an array 🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add blog post for 14.5.1 release with Android one-time product discounts - Update types.md with new discount-related types and isSuspendedAndroid field - Document breaking change: oneTimePurchaseOfferDetailsAndroid is now an array 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…rences - Change blog sidebar active state to text color only (no background) - Light mode: gold text (#a6772e), dark mode: cream text (#f3e4d7) - Update References to include only Billing Library 8.1.0 link
|
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. WalkthroughIntroduces Android Billing Programs API support (Android 8.2.0+), adding four public Android-only methods for enabling/checking programs, creating reporting details, and launching external links; converts Android one-time offer details to an array with richer fields; updates type specs, native bridges (Android + iOS stubs), examples, tests, and docs. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App/Client
participant JS as react-native-iap (JS)
participant Hybrid as HybridRnIap (native Android)
participant OpenIap as OpenIapStore
participant Play as Google Play Billing Library
App->>JS: isBillingProgramAvailableAndroid(program)
JS->>Hybrid: isBillingProgramAvailableAndroid(program)
Hybrid->>OpenIap: isBillingProgramAvailable(mappedProgram)
OpenIap->>Play: query program availability
Play-->>OpenIap: { isAvailable }
OpenIap-->>Hybrid: availability result
Hybrid-->>JS: mapped result
JS-->>App: Promise<result>
App->>JS: launchExternalLinkAndroid(params)
JS->>Hybrid: launchExternalLinkAndroid(mappedParams)
Hybrid->>OpenIap: launchExternalLink(mappedParams)
OpenIap->>Play: request external link launch
Play-->>OpenIap: success/error
OpenIap-->>Hybrid: outcome
Hybrid-->>JS: Promise<boolean>
JS-->>App: Promise<boolean>
App->>JS: createBillingProgramReportingDetailsAndroid(program)
JS->>Hybrid: createBillingProgramReportingDetailsAndroid(program)
Hybrid->>OpenIap: createReportingDetails(mappedProgram)
OpenIap->>Play: generate externalTransactionToken
Play-->>OpenIap: { externalTransactionToken }
OpenIap-->>Hybrid: reporting details
Hybrid-->>JS: mapped details
JS-->>App: Promise<details>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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). (3)
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 |
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 significantly upgrades the Android billing capabilities by incorporating the latest features from Google Play Billing Library 7.0+ and 8.1.0+. It introduces robust support for one-time product discounts, allowing for detailed offer presentations, and adds a new field to track subscription suspension status. These changes are reflected across the core logic, API definitions, and user-facing documentation, ensuring developers can leverage these advanced billing options effectively. 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
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3103 +/- ##
==========================================
+ Coverage 63.14% 63.92% +0.77%
==========================================
Files 9 9
Lines 1582 1616 +34
Branches 531 535 +4
==========================================
+ Hits 999 1033 +34
Misses 576 576
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive support for Google Play one-time product discounts and the new isSuspendedAndroid purchase status field, aligning with Billing Library 7.0+ and 8.1.0+ respectively. The core change involves modifying the oneTimePurchaseOfferDetailsAndroid field from a single object to an array of ProductAndroidOneTimePurchaseOfferDetail objects, which is a breaking change. This array now includes detailed information such as discount display info, limited quantity, valid time windows, pre-order, and rental details. The HybridRnIap.kt file was updated to correctly map these new Android offer details and the isSuspendedAndroid field. Documentation (docs/blog/2025-12-11-release-14.5.1-android-discounts.md, docs/docs/api/types.md) and TypeScript type definitions (src/specs/RnIap.nitro.ts, src/types.ts) were updated to reflect these new structures and fields. The example application (example/screens/AllProducts.tsx, example/screens/PurchaseFlow.tsx, example/screens/SubscriptionFlow.tsx) was also updated to import new types, refactor product detail modals with ScrollView, and display the newly exposed one-time purchase and subscription offer details, along with new styling. Review comments highlight code duplication in the example app's product detail modals, suggesting extraction into a shared component, and recommend using nullish coalescing (?? []) instead of non-null assertions (!) for safer type handling in the SubscriptionFlow.tsx file.
- Add comprehensive tests for oneTimePurchaseOfferDetailsAndroid array - Cover discount display info (percentage and absolute) - Cover limited quantity info, valid time window - Cover preorder and rental details - Restore branch coverage threshold to 40%
- Create AndroidOneTimeOfferDetails component to reduce code duplication - Apply component in PurchaseFlow, AllProducts, and SubscriptionFlow - Replace non-null assertions with nullish coalescing (??) operator - Address code review feedback from Gemini
- Add checkBillingProgramAvailabilityAndroid for external billing programs - Add getBillingProgramReportingDetailsAndroid for transaction tokens - Add launchExternalLinkAndroid for external content links - Update openiap-google to 1.3.12 and openiap-gql to 1.3.2 - Update Kotlin version to 2.2.0 - Update docs with Billing Programs API and types
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (24)
docs/src/css/custom.css (1)
76-88: Minor: Selector brittleness with attribute substring matching.The selector
li[class*='sidebarItem']uses a substring match on class names, which could accidentally match unintended elements if class naming conventions change. Consider using a more specific selector if the exact class name is known.If the exact class name is known (e.g.,
.sidebar-item), prefer an exact match for better maintainability.docs/versioned_docs/version-14.5/guides/faq.md (4)
47-47: Add hyphen to compound modifier.
react-native-iap specificshould bereact-native-iap-specificwhen used as a compound modifier before "configuration".
261-261: Use American English spelling for consistency.Line 261 uses "afterwards" which is more common in British English. For consistency with American English conventions, change to "afterward".
289-289: Remove redundant phrase."final outcome" is redundant—simplify to just "outcome" for clearer wording.
543-543: Add hyphen to compound modifier.
yoga relatedshould beyoga-relatedwhen used as a compound modifier.docs/blog/2025-12-11-release-14.6.0-billing-programs.md (1)
88-99: Add blank lines around code blocks for markdown formatting consistency.Code blocks on lines 89-99 should be surrounded by blank lines. Add a blank line before the opening
\``typescriptand after the closing````.If you were accessing the price directly, update your code: + \`\`\`typescript // Before const price = product.oneTimePurchaseOfferDetailsAndroid?.formattedPrice; @@ -100,6 +100,7 @@ If you were accessing the price directly, update your code: const price = product.oneTimePurchaseOfferDetailsAndroid?.[0]?.formattedPrice; \`\`\` + ## New Typesdocs/versioned_docs/version-14.5/getting-started/setup-horizon.md (1)
19-19: Add hyphen to compound modifier.
react-native-iap specificshould bereact-native-iap-specificwhen used as a compound modifier before "configuration".docs/versioned_docs/version-14.5/api/use-iap.md (1)
342-392: ClarifyvalidateReceiptparameter - this takes a transaction identifier, not product ID.Based on learnings, the
validateReceiptfunction from theuseIAPhook expects a transaction identifier accessed viapurchase.id, notpurchase.productId. The documentation showsproductIdas the parameter name, which may be misleading.Consider updating the parameter name and examples to clarify this is actually the transaction identifier:
-const validatePurchase = async (productId: string, purchase: any) => { +const validatePurchase = async (transactionId: string, purchase: any) => { try { if (Platform.OS === 'ios') { // iOS: Simple validation with just transaction ID - const result = await validateReceipt(productId); + const result = await validateReceipt(transactionId); return result;Based on learnings, the
validateReceiptfunction expectspurchase.id(transaction identifier).example/screens/AlternativeBilling.tsx (1)
126-134: Consider usingisUserCancelledError()helper for error handling.Per coding guidelines, prefer using
isUserCancelledError()with normalizedErrorCodewhen handling purchase errors:+import {isUserCancelledError} from 'react-native-iap'; + onPurchaseError: (error: PurchaseError) => { console.error('Purchase failed:', error); setIsProcessing(false); setPurchaseResult(`❌ Purchase failed: ${error.message}`); - if (error.code !== 'user-cancelled') { + if (!isUserCancelledError(error)) { Alert.alert('Error', error.message); } },As per coding guidelines for example code.
docs/versioned_docs/version-14.5/examples/subscription-flow.md (1)
110-115: Tighten fenced code block formatting for the env variable snippetThe small env snippet under “Add environment variable” would be a bit more robust (and quiet markdownlint) if you:
- Add a blank line before and after the fenced block, and
- Specify a language, e.g.:
2. Add environment variable: ```bash EXPO_PUBLIC_IAPKIT_API_KEY=your_api_key_hereThis keeps the example rendering clean and satisfies MD031/MD040. </blockquote></details> <details> <summary>docs/versioned_docs/version-14.5/api/error-handling.md (1)</summary><blockquote> `24-47`: **Avoid `instanceof PurchaseError` if `PurchaseError` is type-only** In this example: ```ts if (error instanceof PurchaseError) { console.error(`Error code: ${error.code}, Message: ${error.message}`); }
PurchaseErroris documented as an interface, and in the type definitions it’s exposed as a TypeScript type, not necessarily a runtime class. In that caseinstanceof PurchaseErrorwon’t compile or work at runtime.Safer alternatives:
- Prefer the helpers already imported in this file:
const friendlyMessage = getUserFriendlyErrorMessage(error); // …
- Or use a structural guard if you really need direct access:
if (error && typeof error === 'object' && 'code' in error) { const {code, message} = error as PurchaseError; console.error(`Error code: ${code}, Message: ${message}`); }It would also help to clarify in this doc that
PurchaseErroris a type describing the error shape, and point readers to the helper functions for runtime checks.Also applies to: 56-64, 71-76
example/screens/PurchaseFlow.tsx (2)
389-398: Use error helper functions instead of manualErrorCodechecksIn
onPurchaseError, you’re manually comparingerror.codetoErrorCode.UserCancelledand interpolatingerror.message. Since this is example code and the library now exposes helpers, it would better demonstrate best practices to lean on:
isUserCancelledError(error)for cancellation checks, andgetUserFriendlyErrorMessage(error)for UI-safe messages.For example:
-import {IAPKIT_API_KEY} from '@env'; +import {IAPKIT_API_KEY} from '@env'; +import {isUserCancelledError, getUserFriendlyErrorMessage} from 'react-native-iap'; @@ onPurchaseError: (error: PurchaseError) => { console.error('Purchase failed:', error); console.error('Error code:', error.code); - console.error( - 'Is user cancelled:', - error.code === ErrorCode.UserCancelled, - ); + console.error('Is user cancelled:', isUserCancelledError(error)); setIsProcessing(false); - if (error.code === ErrorCode.UserCancelled) { + if (isUserCancelledError(error)) { setPurchaseResult('Purchase cancelled by user'); return; } - setPurchaseResult( - `Purchase failed: ${error.message} (code: ${error.code})`, - ); + const friendly = getUserFriendlyErrorMessage(error); + setPurchaseResult(`Purchase failed: ${friendly}`); },This keeps the example aligned with the documented error-handling helpers.
As per coding guidelines, these helpers are preferred over directErrorCodechecks in app code.
389-398: Remove duplicated/unused offer styles from this screen
AndroidOneTimeOfferDetailsowns its own styling (via its ownStyleSheet.create), and in this file you never referencestyles.offersSection,styles.offerCard,styles.offerLabel, etc. The block:offersSection: { … }, offersSectionTitle: { … }, offerCard: { … }, offerTitle: { … }, offerLabel: { … }, offerValue: { … }, offerValueDiscount: { … }, offerToken: { … },appears to be a copy of the styles already defined inside
AndroidOneTimeOfferDetailsand is unused here.Consider deleting these unused style entries to avoid confusion and future divergence between the two copies:
closeButtonText: { color: 'white', fontWeight: '600', fontSize: 16, }, modalScrollView: { maxHeight: '85%', }, - offersSection: { … }, - offersSectionTitle: { … }, - offerCard: { … }, - offerTitle: { … }, - offerLabel: { … }, - offerValue: { … }, - offerValueDiscount: { … }, - offerToken: { … }, });This keeps the screen styles focused on what this component actually renders.
Also applies to: 1018-1068
docs/versioned_docs/version-14.5/guides/purchases.md (2)
26-38: Suggest demonstrating error helper functions.While the error handling example is functional, consider demonstrating the recommended helper functions
isUserCancelledError()andgetUserFriendlyErrorMessage()from the library's error utilities for more idiomatic error handling:const {products, requestPurchase, finishTransaction} = useIAP({ onPurchaseSuccess: async (purchase) => { await finishTransaction({purchase, isConsumable: true}); }, onPurchaseError: (error) => { if (!isUserCancelledError(error)) { Alert.alert('Error', getUserFriendlyErrorMessage(error)); } }, });Based on learnings: Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
116-122: Consider showing error helper functions in examples.For consistency with library best practices, the error handling section could also demonstrate the recommended helper functions:
import {ErrorCode, isUserCancelledError, isNetworkError, getUserFriendlyErrorMessage} from 'react-native-iap'; if (isUserCancelledError(error)) return; if (isNetworkError(error)) showRetry(); // Or get user-friendly message Alert.alert('Error', getUserFriendlyErrorMessage(error));Based on learnings: Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
docs/docs/api/methods/core-methods.md (1)
1068-1106: Clarify placeholder function in example.Line 1104 calls
reportExternalTransaction()which appears to be a placeholder for the user's backend implementation. Consider adding a comment to make this clearer:// Step 6: Report to Google Play backend (implement this on your server) await reportExternalTransaction(details.externalTransactionToken); // Example: await fetch('https://your-server.com/report-google-transaction', { // method: 'POST', // body: JSON.stringify({ token: details.externalTransactionToken }) // });This helps prevent confusion about whether
reportExternalTransactionis a library function.docs/versioned_docs/version-14.5/guides/alternative-billing.md (1)
52-52: Minor wording nit: “outside of” → “outside”You could tighten the sentence by changing “outside of the platform's standard billing systems” to “outside the platform's standard billing systems.” Optional, but a bit cleaner.
docs/versioned_docs/version-14.5/api/methods/listeners.md (1)
86-117: Consider showcasing high-level error helpers in the error-listener exampleThe
handlePurchaseErrorswitch is fine, but since the library exposes helpers likeisUserCancelledError()andgetUserFriendlyErrorMessage()(with normalizedErrorCode), it might be worth mentioning or demonstrating them here so docs match the recommended error‑handling pattern elsewhere in the repo.docs/versioned_docs/version-14.5/getting-started/installation.md (1)
63-81: Verify Kotlin version in Expoexpo-build-propertiesexampleThe Expo config snippet pins
android.kotlinVersionto"2.1.20", while this release bumps the Kotlin dependency from 2.1.20 → 2.2.0. That may be intentional (Expo constraints vs app Gradle), but it’s easy for readers to assume they must use 2.1.20.Consider either:
- aligning this snippet with the new Kotlin version if safe for Expo, or
- adding a short note explaining why Expo projects should stay on 2.1.20 even though the library itself uses 2.2.0.
example-expo/components/AndroidOneTimeOfferDetails.tsx (1)
46-57: Handle zeropercentageDiscountexplicitlyThe discount rendering works, but using a simple truthy check:
offer.discountDisplayInfo.percentageDiscount ? `${offer.discountDisplayInfo.percentageDiscount}% off` : offer.discountDisplayInfo.discountAmount ? `${offer.discountDisplayInfo.discountAmount.formattedDiscountAmount} off` : 'N/A'means a
percentageDiscountof0would fall through to the amount /'N/A'branch. If0is a possible (or future) value, consider checkingpercentageDiscount != nullinstead of relying on truthiness.Overall, the component structure and optional field handling look solid.
example-expo/app/subscription-flow.tsx (2)
18-30: Use type-only imports for TypeScript types.Per coding guidelines, type imports should use
import type. The typesVerifyPurchaseWithProviderPropsandProductSubscriptionAndroidOfferDetailsare only used for type annotations.import { requestPurchase, useIAP, deepLinkToSubscriptions, - type ActiveSubscription, - type ProductSubscription, - type ProductSubscriptionAndroid, - type Purchase, - type PurchaseError, - type VerifyPurchaseWithProviderProps, - type ProductSubscriptionAndroidOfferDetails, ErrorCode, } from 'react-native-iap'; +import type { + ActiveSubscription, + ProductSubscription, + ProductSubscriptionAndroid, + Purchase, + PurchaseError, + VerifyPurchaseWithProviderProps, + ProductSubscriptionAndroidOfferDetails, +} from 'react-native-iap';
1599-1610: Consider sending only platform-specific verification data.The verification request includes both
appleandconst verifyRequest: VerifyPurchaseWithProviderProps = { provider: 'iapkit', iapkit: { apiKey, - apple: { - jws: jwsOrToken, - }, - google: { - purchaseToken: jwsOrToken, - }, + ...(Platform.OS === 'ios' + ? { apple: { jws: jwsOrToken } } + : { google: { purchaseToken: jwsOrToken } }), }, };src/index.ts (2)
2165-2222: Type definitions duplicated from src/types.ts.These types (
BillingProgramAndroid,ExternalLinkLaunchModeAndroid,ExternalLinkTypeAndroid,LaunchExternalLinkParamsAndroid,BillingProgramAvailabilityResultAndroid,BillingProgramReportingDetailsAndroid) are already defined insrc/types.ts(lines 68-98, 218-305) which is auto-generated.Since
src/index.tsalready re-exports everything fromsrc/types.tsviaexport * from './types'(line 65), these manual definitions will shadow the auto-generated ones. As per coding guidelines, prefer importing canonical types fromsrc/types.tsrather than creating ad-hoc interfaces.Consider removing these inline type definitions and importing them from the types file:
+import type { + BillingProgramAndroid, + ExternalLinkLaunchModeAndroid, + ExternalLinkTypeAndroid, + LaunchExternalLinkParamsAndroid, + BillingProgramAvailabilityResultAndroid, + BillingProgramReportingDetailsAndroid, +} from './types'; -export type BillingProgramAndroid = - | 'unspecified' - | 'external-content-link' - | 'external-offer'; - -export type ExternalLinkLaunchModeAndroid = - ...Based on coding guidelines, this avoids duplication and ensures the canonical types from the auto-generated file are used.
2239-2253: enableBillingProgramAndroid silently swallows errors.Unlike the other Billing Program functions that propagate errors, this function catches and logs errors without re-throwing. While this may be intentional since it's a configuration call before
initConnection(), consider whether callers should be notified of failures.export const enableBillingProgramAndroid = ( program: BillingProgramAndroid, ): void => { if (Platform.OS !== 'android') { RnIapConsole.warn( 'enableBillingProgramAndroid is only supported on Android', ); return; } try { IAP.instance.enableBillingProgramAndroid(program as any); } catch (error) { RnIapConsole.error('Failed to enable billing program:', error); + throw error; } };If the design intent is to fail silently, consider documenting this behavior in the JSDoc.
📜 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 (66)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt(6 hunks)docs/blog/2025-12-11-release-14.6.0-billing-programs.md(1 hunks)docs/docs/api/methods/core-methods.md(1 hunks)docs/docs/api/types.md(4 hunks)docs/docs/examples/alternative-billing.md(5 hunks)docs/docs/getting-started/installation.md(2 hunks)docs/docs/guides/alternative-billing.md(2 hunks)docs/docs/installation.md(2 hunks)docs/docusaurus.config.ts(1 hunks)docs/src/css/custom.css(1 hunks)docs/versioned_docs/version-14.5/api/error-codes.md(1 hunks)docs/versioned_docs/version-14.5/api/error-handling.md(1 hunks)docs/versioned_docs/version-14.5/api/index.md(1 hunks)docs/versioned_docs/version-14.5/api/methods/_category_.json(1 hunks)docs/versioned_docs/version-14.5/api/methods/core-methods.md(1 hunks)docs/versioned_docs/version-14.5/api/methods/listeners.md(1 hunks)docs/versioned_docs/version-14.5/api/types.md(1 hunks)docs/versioned_docs/version-14.5/api/use-iap.md(1 hunks)docs/versioned_docs/version-14.5/examples/_category_.json(1 hunks)docs/versioned_docs/version-14.5/examples/alternative-billing.md(1 hunks)docs/versioned_docs/version-14.5/examples/available-purchases.md(1 hunks)docs/versioned_docs/version-14.5/examples/offer-code.md(1 hunks)docs/versioned_docs/version-14.5/examples/purchase-flow.md(1 hunks)docs/versioned_docs/version-14.5/examples/subscription-flow.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/installation.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/setup-android.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/setup-horizon.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/setup-ios.md(1 hunks)docs/versioned_docs/version-14.5/guides/_category_.json(1 hunks)docs/versioned_docs/version-14.5/guides/alternative-billing.md(1 hunks)docs/versioned_docs/version-14.5/guides/error-handling.md(1 hunks)docs/versioned_docs/version-14.5/guides/expo-plugin.md(1 hunks)docs/versioned_docs/version-14.5/guides/faq.md(1 hunks)docs/versioned_docs/version-14.5/guides/lifecycle.md(1 hunks)docs/versioned_docs/version-14.5/guides/offer-code-redemption.md(1 hunks)docs/versioned_docs/version-14.5/guides/purchases.md(1 hunks)docs/versioned_docs/version-14.5/guides/subscription-offers.md(1 hunks)docs/versioned_docs/version-14.5/guides/subscription-validation.md(1 hunks)docs/versioned_docs/version-14.5/guides/support.md(1 hunks)docs/versioned_docs/version-14.5/guides/troubleshooting.md(1 hunks)docs/versioned_docs/version-14.5/installation.md(1 hunks)docs/versioned_docs/version-14.5/intro.md(1 hunks)docs/versioned_docs/version-14.5/sponsors.md(1 hunks)docs/versioned_sidebars/version-14.5-sidebars.json(1 hunks)docs/versions.json(1 hunks)example-expo/app/purchase-flow.tsx(10 hunks)example-expo/app/subscription-flow.tsx(11 hunks)example-expo/components/AndroidOneTimeOfferDetails.tsx(1 hunks)example-expo/hooks/useVerificationMethod.ts(1 hunks)example-expo/scripts/copy-screens.sh(1 hunks)example-expo/utils/errorUtils.ts(1 hunks)example/__tests__/screens/PurchaseFlow.test.tsx(2 hunks)example/android/build.gradle(1 hunks)example/screens/AllProducts.tsx(4 hunks)example/screens/AlternativeBilling.tsx(10 hunks)example/screens/PurchaseFlow.tsx(3 hunks)example/screens/SubscriptionFlow.tsx(4 hunks)example/src/components/AndroidOneTimeOfferDetails.tsx(1 hunks)ios/HybridRnIap.swift(1 hunks)openiap-versions.json(1 hunks)package.json(1 hunks)src/__tests__/index.test.ts(2 hunks)src/index.ts(1 hunks)src/specs/RnIap.nitro.ts(6 hunks)src/types.ts(13 hunks)src/utils/type-bridge.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use type-only imports when importing types (import type).
Files:
example-expo/utils/errorUtils.tsexample-expo/hooks/useVerificationMethod.tsdocs/docusaurus.config.tsexample/screens/AllProducts.tsxsrc/utils/type-bridge.tsexample/__tests__/screens/PurchaseFlow.test.tsxexample/screens/PurchaseFlow.tsxexample/src/components/AndroidOneTimeOfferDetails.tsxexample/screens/SubscriptionFlow.tsxexample/screens/AlternativeBilling.tsxsrc/index.tssrc/__tests__/index.test.tsexample-expo/components/AndroidOneTimeOfferDetails.tsxexample-expo/app/purchase-flow.tsxsrc/specs/RnIap.nitro.tssrc/types.tsexample-expo/app/subscription-flow.tsx
{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-expo/utils/errorUtils.tsexample-expo/hooks/useVerificationMethod.tsexample/screens/AllProducts.tsxexample/__tests__/screens/PurchaseFlow.test.tsxexample/screens/PurchaseFlow.tsxexample/src/components/AndroidOneTimeOfferDetails.tsxexample/screens/SubscriptionFlow.tsxexample/screens/AlternativeBilling.tsxexample-expo/components/AndroidOneTimeOfferDetails.tsxexample-expo/app/purchase-flow.tsxexample-expo/app/subscription-flow.tsx
{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:
example-expo/utils/errorUtils.tsexample-expo/hooks/useVerificationMethod.tsexample/screens/AllProducts.tsxsrc/utils/type-bridge.tsexample/__tests__/screens/PurchaseFlow.test.tsxexample/screens/PurchaseFlow.tsxexample/src/components/AndroidOneTimeOfferDetails.tsxexample/screens/SubscriptionFlow.tsxexample/screens/AlternativeBilling.tsxsrc/index.tssrc/__tests__/index.test.tsexample-expo/components/AndroidOneTimeOfferDetails.tsxexample-expo/app/purchase-flow.tsxsrc/specs/RnIap.nitro.tssrc/types.tsexample-expo/app/subscription-flow.tsx
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/utils/type-bridge.tssrc/index.tssrc/__tests__/index.test.tssrc/specs/RnIap.nitro.tssrc/types.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
**/*.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
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
🧠 Learnings (9)
📚 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.5/guides/error-handling.mddocs/versioned_docs/version-14.5/guides/troubleshooting.mdexample-expo/utils/errorUtils.tsdocs/versioned_docs/version-14.5/examples/alternative-billing.mdexample/__tests__/screens/PurchaseFlow.test.tsxdocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/api/use-iap.mdexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxdocs/versioned_docs/version-14.5/guides/subscription-offers.mddocs/versioned_docs/version-14.5/api/types.mdexample/screens/AlternativeBilling.tsxdocs/versioned_docs/version-14.5/api/methods/listeners.mddocs/docs/examples/alternative-billing.mddocs/versioned_docs/version-14.5/api/error-codes.mddocs/versioned_docs/version-14.5/api/error-handling.mddocs/versioned_docs/version-14.5/guides/purchases.mddocs/versioned_docs/version-14.5/guides/offer-code-redemption.mdexample-expo/app/purchase-flow.tsxsrc/types.tsexample-expo/app/subscription-flow.tsx
📚 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.5/guides/error-handling.mddocs/versioned_docs/version-14.5/guides/troubleshooting.mdexample-expo/hooks/useVerificationMethod.tsexample/screens/AllProducts.tsxdocs/versioned_docs/version-14.5/examples/purchase-flow.mddocs/versioned_docs/version-14.5/examples/alternative-billing.mddocs/versioned_docs/version-14.5/guides/expo-plugin.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/api/use-iap.mddocs/versioned_docs/version-14.5/examples/available-purchases.mddocs/versioned_docs/version-14.5/api/index.mddocs/versioned_docs/version-14.5/guides/subscription-validation.mddocs/versioned_docs/version-14.5/guides/lifecycle.mdexample/screens/PurchaseFlow.tsxdocs/versioned_docs/version-14.5/guides/subscription-offers.mddocs/versioned_docs/version-14.5/api/types.mdexample/screens/AlternativeBilling.tsxdocs/versioned_docs/version-14.5/examples/subscription-flow.mddocs/versioned_docs/version-14.5/api/methods/listeners.mddocs/docs/examples/alternative-billing.mddocs/versioned_docs/version-14.5/api/error-codes.mddocs/versioned_docs/version-14.5/guides/faq.mddocs/versioned_docs/version-14.5/guides/purchases.mdexample-expo/app/purchase-flow.tsxsrc/types.tsexample-expo/app/subscription-flow.tsx
📚 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.5/guides/troubleshooting.mdexample/screens/AllProducts.tsxdocs/versioned_docs/version-14.5/examples/purchase-flow.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/api/use-iap.mddocs/versioned_docs/version-14.5/getting-started/setup-ios.mddocs/versioned_docs/version-14.5/guides/subscription-validation.mddocs/versioned_docs/version-14.5/guides/lifecycle.mdexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxdocs/versioned_docs/version-14.5/api/types.mdexample/screens/AlternativeBilling.tsxdocs/versioned_docs/version-14.5/api/methods/listeners.mddocs/docs/examples/alternative-billing.mddocs/versioned_docs/version-14.5/guides/faq.mddocs/versioned_docs/version-14.5/guides/purchases.mdpackage.jsonexample-expo/app/purchase-flow.tsxsrc/types.tsexample-expo/app/subscription-flow.tsx
📚 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}} : Do not call parseErrorStringToJsonObj() in app/user code; errors are already normalized by the library.
Applied to files:
example-expo/utils/errorUtils.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:
example/screens/AllProducts.tsxsrc/utils/type-bridge.tsexample/__tests__/screens/PurchaseFlow.test.tsxdocs/versioned_docs/version-14.5/api/methods/core-methods.mdexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxdocs/versioned_docs/version-14.5/api/types.mdexample/screens/AlternativeBilling.tsxsrc/index.tssrc/__tests__/index.test.tsexample-expo/components/AndroidOneTimeOfferDetails.tsxdocs/docs/api/types.mddocs/versioned_docs/version-14.5/guides/purchases.mdexample-expo/app/purchase-flow.tsxsrc/specs/RnIap.nitro.tssrc/types.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:
example/screens/AllProducts.tsxexample/__tests__/screens/PurchaseFlow.test.tsxdocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/api/use-iap.mdexample/screens/PurchaseFlow.tsxexample/screens/SubscriptionFlow.tsxandroid/src/main/java/com/margelo/nitro/iap/HybridRnIap.ktdocs/docs/api/types.mddocs/versioned_docs/version-14.5/api/error-handling.mddocs/versioned_docs/version-14.5/guides/purchases.mdexample-expo/app/purchase-flow.tsxsrc/specs/RnIap.nitro.tssrc/types.tsexample-expo/app/subscription-flow.tsx
📚 Learning: 2025-12-08T20:40:05.783Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
Applied to files:
docs/versions.json
📚 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.5/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.5/api/types.md
🧬 Code graph analysis (12)
example/screens/AllProducts.tsx (1)
src/types.ts (3)
ProductAndroid(466-484)ProductSubscriptionAndroid(565-583)ProductSubscriptionAndroidOfferDetails(585-591)
example/screens/PurchaseFlow.tsx (1)
src/types.ts (1)
ProductAndroid(466-484)
example/src/components/AndroidOneTimeOfferDetails.tsx (1)
src/types.ts (1)
ProductAndroidOneTimePurchaseOfferDetail(490-521)
example/screens/SubscriptionFlow.tsx (2)
src/types.ts (2)
ProductSubscriptionAndroid(565-583)ProductSubscriptionAndroidOfferDetails(585-591)example/src/components/AndroidOneTimeOfferDetails.tsx (1)
AndroidOneTimeOfferDetails(12-127)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (2)
src/specs/RnIap.nitro.ts (9)
NitroOneTimePurchaseOfferDetail(356-369)NitroDiscountDisplayInfoAndroid(315-318)NitroDiscountAmountAndroid(307-310)NitroValidTimeWindowAndroid(347-350)NitroLimitedQuantityInfoAndroid(323-326)NitroPreorderDetailsAndroid(331-334)NitroRentalDetailsAndroid(339-342)NitroBillingProgramAvailabilityResultAndroid(287-292)NitroBillingProgramReportingDetailsAndroid(297-302)src/index.ts (1)
initConnection(989-1006)
src/index.ts (3)
src/utils/debug.ts (1)
RnIapConsole(18-49)ios/RnIapLog.swift (2)
error(35-35)result(41-43)android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
result(14-16)
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-expo/components/AndroidOneTimeOfferDetails.tsx (1)
src/types.ts (1)
ProductAndroidOneTimePurchaseOfferDetail(490-521)
example-expo/app/purchase-flow.tsx (5)
example-expo/hooks/useVerificationMethod.ts (2)
VerificationMethod(4-4)useVerificationMethod(19-99)example-expo/components/AndroidOneTimeOfferDetails.tsx (1)
AndroidOneTimeOfferDetails(12-127)src/types.ts (2)
ProductAndroid(466-484)VerifyPurchaseWithProviderProps(1144-1147)src/index.ts (3)
useIAP(107-107)verifyPurchase(1424-1424)verifyPurchaseWithProvider(1447-1483)example-expo/utils/errorUtils.ts (1)
getErrorMessage(5-28)
src/specs/RnIap.nitro.ts (2)
src/index.ts (3)
BillingProgramAndroid(2165-2168)ExternalLinkLaunchModeAndroid(2175-2178)ExternalLinkTypeAndroid(2185-2188)src/types.ts (3)
BillingProgramAndroid(72-72)ExternalLinkLaunchModeAndroid(223-223)ExternalLinkTypeAndroid(230-230)
src/types.ts (2)
src/index.ts (6)
BillingProgramAndroid(2165-2168)BillingProgramAvailabilityResultAndroid(2207-2212)BillingProgramReportingDetailsAndroid(2217-2222)ExternalLinkLaunchModeAndroid(2175-2178)ExternalLinkTypeAndroid(2185-2188)LaunchExternalLinkParamsAndroid(2193-2202)src/specs/RnIap.nitro.ts (3)
BillingProgramAndroid(48-51)ExternalLinkLaunchModeAndroid(53-56)ExternalLinkTypeAndroid(58-61)
example-expo/app/subscription-flow.tsx (7)
example-expo/hooks/useVerificationMethod.ts (2)
VerificationMethod(4-4)useVerificationMethod(19-99)src/types.ts (3)
ProductSubscriptionAndroid(565-583)ProductSubscriptionAndroidOfferDetails(585-591)VerifyPurchaseWithProviderProps(1144-1147)example-expo/components/AndroidOneTimeOfferDetails.tsx (1)
AndroidOneTimeOfferDetails(12-127)src/index.ts (2)
verifyPurchase(1424-1424)verifyPurchaseWithProvider(1447-1483)android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
verifyPurchaseWithProvider(1178-1238)ios/HybridRnIap.swift (1)
verifyPurchaseWithProvider(352-406)example-expo/utils/errorUtils.ts (1)
getErrorMessage(5-28)
🪛 detekt (1.23.8)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
[warning] 1421-1421: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1442-1442: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1477-1477: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🪛 LanguageTool
docs/versioned_docs/version-14.5/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.5/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)
docs/versioned_docs/version-14.5/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] ~655-~655: 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] ~958-~958: 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] ~1023-~1023: 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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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)
🪛 markdownlint-cli2 (0.18.1)
docs/versioned_docs/version-14.5/guides/subscription-validation.md
140-140: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
docs/versioned_docs/version-14.5/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)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md
89-89: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
95-95: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
docs/versioned_docs/version-14.5/intro.md
24-24: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ 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). (1)
- GitHub Check: build-ios
| const AndroidProductItem = ({product}: {product: Product}) => { | ||
| const {requestPurchase} = useIAP(); | ||
|
|
||
| const handlePurchase = () => { | ||
| if (product.platform === 'android') { | ||
| requestPurchase({ | ||
| request: {skus: [product.id]}, | ||
| type: 'in-app', | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| if (product.platform !== 'android') return null; | ||
|
|
||
| return ( | ||
| <TouchableOpacity onPress={handlePurchase}> | ||
| <Text>{product.title}</Text> | ||
| <Text>{product.oneTimePurchaseOfferDetails?.formattedPrice}</Text> | ||
| </TouchableOpacity> | ||
| ); | ||
| }; | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find and read the versioned docs file
cat -n docs/versioned_docs/version-14.5/getting-started/setup-android.md | sed -n '80,120p'Repository: hyochan/react-native-iap
Length of output: 1393
🏁 Script executed:
# Check the actual type definitions in src/types.ts
cat -n src/types.ts | head -200Repository: hyochan/react-native-iap
Length of output: 7953
🏁 Script executed:
# Search for ProductAndroid type definition
rg "export interface ProductAndroid" src/types.ts -A 30Repository: hyochan/react-native-iap
Length of output: 1920
🏁 Script executed:
# Search for ProductSubscriptionAndroid type definition
rg "export interface ProductSubscriptionAndroid" src/types.ts -A 30Repository: hyochan/react-native-iap
Length of output: 1788
🏁 Script executed:
# Search for ProductAndroidOneTimePurchaseOfferDetail
rg "ProductAndroidOneTimePurchaseOfferDetail" src/types.ts -A 15Repository: hyochan/react-native-iap
Length of output: 1410
🏁 Script executed:
# Get the full subscription example from the docs
cat -n docs/versioned_docs/version-14.5/getting-started/setup-android.md | sed -n '116,160p'Repository: hyochan/react-native-iap
Length of output: 1457
🏁 Script executed:
# Check PricingPhasesAndroid structure
rg "export interface PricingPhasesAndroid" src/types.ts -A 15Repository: hyochan/react-native-iap
Length of output: 509
🏁 Script executed:
# Double-check the Product union type to understand type narrowing
rg "export type Product = " src/types.ts -A 2Repository: hyochan/react-native-iap
Length of output: 175
🏁 Script executed:
# Check ProductSubscription type
rg "export type ProductSubscription = " src/types.ts -A 2Repository: hyochan/react-native-iap
Length of output: 223
Fix Android example property names and one-time offer array shape
The Android examples don't match the actual public type surface:
- One-time products:
ProductAndroidexposesoneTimePurchaseOfferDetailsAndroid, and it's an array of details (ProductAndroidOneTimePurchaseOfferDetail[]), not a singleoneTimePurchaseOfferDetailsobject. The example at line 105 will fail TypeScript type-checking. - Subscriptions: Android subscriptions use
subscriptionOfferDetailsAndroidon the type, notsubscription.subscriptionOfferDetailsat line 145. This also fails type-checking.
Update both examples to use the correct property names and handle the array shape:
// One-time product
if (product.platform !== 'android') return null;
const firstOffer =
product.oneTimePurchaseOfferDetailsAndroid?.[0] ?? null;
return (
<TouchableOpacity onPress={handlePurchase}>
<Text>{product.title}</Text>
<Text>{firstOffer?.formattedPrice ?? product.displayPrice}</Text>
</TouchableOpacity>
);
// Subscription
if (subscription.platform !== 'android') return null;
return (
<View>
<Text>{subscription.title}</Text>
{subscription.subscriptionOfferDetailsAndroid?.map((offer) => (
<TouchableOpacity
key={offer.offerId}
onPress={() => handleSubscribe(offer)}
>
<Text>
{offer.pricingPhases.pricingPhaseList
.map(
(phase) => `${phase.formattedPrice}/${phase.billingPeriod}`,
)
.join(' then ')}
</Text>
</TouchableOpacity>
))}
</View>
);There was a problem hiding this comment.
14.5 docs is backup. Shouldn't change.
There was a problem hiding this comment.
@hyochan, understood! You're absolutely right—versioned documentation is meant to preserve the API surface exactly as it existed in version 14.5, so these docs should remain unchanged. The examples should reflect the 14.5 API, not the 14.6 changes.
I'll make sure future reviews respect that versioned docs (like versioned_docs/version-14.5/) are intentional snapshots and should not be updated with new API changes. Only the current docs in the docs/ directory should reflect the latest API.
Thank you for the clarification!
🧠 Learnings used
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
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: 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.
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.
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.
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
| 1. **Configure expo-build-properties for Android** (required for Kotlin 2.0+ support): | ||
|
|
||
| Starting from version 14.0.0-rc, react-native-iap supports Google Play Billing Library v8.0.0, which requires Kotlin 2.0+. Since `expo-modules-core` doesn't support Kotlin 2.0 yet, you need to manually configure the Kotlin version. |
There was a problem hiding this comment.
Update Google Play Billing Library version reference.
Line 65 mentions "v8.0.0," but this PR adds support for Google Play Billing Library 8.2.0. Update the documentation to reflect the latest supported version.
- Starting from version 14.0.0-rc, react-native-iap supports Google Play Billing Library v8.0.0, which requires Kotlin 2.0+. Since `expo-modules-core` doesn't support Kotlin 2.0 yet, you need to manually configure the Kotlin version.
+ Starting from version 14.0.0-rc, react-native-iap supports Google Play Billing Library v8.2.0, which requires Kotlin 2.0+. Since `expo-modules-core` doesn't support Kotlin 2.0 yet, you need to manually configure the Kotlin version.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 1. **Configure expo-build-properties for Android** (required for Kotlin 2.0+ support): | |
| Starting from version 14.0.0-rc, react-native-iap supports Google Play Billing Library v8.0.0, which requires Kotlin 2.0+. Since `expo-modules-core` doesn't support Kotlin 2.0 yet, you need to manually configure the Kotlin version. | |
| 1. **Configure expo-build-properties for Android** (required for Kotlin 2.0+ support): | |
| Starting from version 14.0.0-rc, react-native-iap supports Google Play Billing Library v8.2.0, which requires Kotlin 2.0+. Since `expo-modules-core` doesn't support Kotlin 2.0 yet, you need to manually configure the Kotlin version. |
🤖 Prompt for AI Agents
In docs/versioned_docs/version-14.5/installation.md around lines 63 to 65, the
doc references Google Play Billing Library v8.0.0 but the code now supports
v8.2.0; update the text to mention v8.2.0 (e.g., replace "v8.0.0" with "v8.2.0")
and ensure any surrounding context or links that mention the billing library
version are updated to reflect 8.2.0.
There was a problem hiding this comment.
This is a versioned doc. Versioned doc shouldn't change to recent info.
There was a problem hiding this comment.
@hyochan, you're absolutely correct! I apologize for the confusion.
This is a versioned documentation file for version 14.5, which should reflect the Google Play Billing Library version that was supported at that time (v8.0.0). The new v8.2.0 support is part of version 14.6, so only the current docs (in the docs/ directory, not versioned_docs/version-14.5/) should reference v8.2.0.
This aligns with the established practice that versioned documentation should preserve the API surface and requirements that existed in that specific version, not reflect newer changes. Thank you for clarifying!
🧠 Learnings used
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
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.
| "expo-build-properties", | ||
| { | ||
| "android": { | ||
| "kotlinVersion": "2.1.20" |
There was a problem hiding this comment.
Update Kotlin version to match the PR's dependency bump.
Line 78 specifies kotlinVersion: 2.1.20, but the PR objectives indicate the Kotlin dependency was bumped to 2.2.0. Update this to reflect the latest version.
{
"android": {
- "kotlinVersion": "2.1.20"
+ "kotlinVersion": "2.2.0"
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "kotlinVersion": "2.1.20" | |
| { | |
| "android": { | |
| "kotlinVersion": "2.2.0" | |
| } | |
| } |
🤖 Prompt for AI Agents
In docs/versioned_docs/version-14.5/installation.md around line 78, update the
Kotlin version value from "2.1.20" to the new dependency bump "2.2.0"; replace
the old version literal so the documentation matches the PR's updated
dependency.
There was a problem hiding this comment.
It is a versioned docs shouldn't change!
There was a problem hiding this comment.
@hyochan You're absolutely correct! Versioned documentation should remain frozen as it was at that version's release. As noted in the learnings, versioned docs (like version-14.5) should reflect the API surface and requirements that existed in that specific version, not the latest changes.
The Kotlin version 2.1.20 is correct for version 14.5 documentation. The new 2.2.0 requirement should only appear in the current docs (in the docs/ directory) or in new version 14.6 documentation, not in the frozen 14.5 versioned docs.
The original suggestion to change this versioned documentation was incorrect. Thank you for catching this!
🧠 Learnings used
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
There was a problem hiding this comment.
This is a versioned doc. Versioned doc shouldn't change to recent info.
There was a problem hiding this comment.
@hyochan Understood and noted! I apologize for the initial confusion. Versioned documentation (like version-14.5) must remain frozen to reflect what was accurate at that version's release. The Kotlin 2.1.20 specification in version 14.5 docs is correct and should not be updated.
The 2.2.0 requirement belongs only in the current docs (docs/ directory) or in future version 14.6+ documentation. I'll make sure to respect the immutability of versioned docs in future reviews.
🧠 Learnings used
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
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.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
docs/versioned_docs/version-14.5/guides/offer-code-redemption.md (1)
85-94: ** Complete Example still uses wrong iOS function name**The "Complete Example" section still imports and calls
presentCodeRedemptionSheetinstead of the correct exported functionpresentCodeRedemptionSheetIOS(consistent with other iOS-specific methods in the library). This was already flagged in the iOS Implementation section, which correctly usespresentCodeRedemptionSheetIOS.Apply this diff to align with the library's naming convention:
import {Platform} from 'react-native'; import { - presentCodeRedemptionSheet, + presentCodeRedemptionSheetIOS, openRedeemOfferCodeAndroid, purchaseUpdatedListener, } from 'react-native-iap'; @@ -91,7 +91,7 @@ if (Platform.OS === 'ios') { // Present native iOS redemption sheet - const result = await presentCodeRedemptionSheet(); + const result = await presentCodeRedemptionSheetIOS(); if (result) { console.log('Redemption sheet presented'); }docs/versioned_docs/version-14.5/getting-started/setup-android.md (1)
102-112: Fix one-time offer array access to match subscription pattern.The code at lines 102–112 treats
oneTimePurchaseOfferDetailsAndroidas a single object (offerDetails?.formattedPrice), but per the PR objective, this is now an array to support multiple discount offers. This is inconsistent with the subscription example (lines 146–161), which correctly maps oversubscriptionOfferDetailsAndroidas an array.The comments at lines 102–103 acknowledge the breaking change but the code example doesn't reflect the array structure. Update the code to access the first offer from the array:
// In v14.5.x, oneTimePurchaseOfferDetailsAndroid is a single object // In v14.6.0+, it becomes an array for discount support - const offerDetails = product.oneTimePurchaseOfferDetailsAndroid; + const firstOffer = + product.oneTimePurchaseOfferDetailsAndroid?.[0] ?? null; return ( <TouchableOpacity onPress={handlePurchase}> <Text>{product.title}</Text> - <Text>{offerDetails?.formattedPrice ?? product.displayPrice}</Text> + <Text>{firstOffer?.formattedPrice ?? product.displayPrice}</Text> </TouchableOpacity> );
🧹 Nitpick comments (5)
docs/versioned_docs/version-14.5/examples/available-purchases.md (1)
66-66: Add blank lines around code fences per Markdown formatting rules.Lines 66 and 70 need blank lines before and after the code fence to comply with Markdown lint (MD031).
If you need the Promise-based return values, import the root API functions directly: + ```tsx import {getAvailablePurchases, getActiveSubscriptions} from 'react-native-iap'; @@ -69,6 +70,7 @@ const purchases = await getAvailablePurchases();
:::
Also applies to: 70-70 </blockquote></details> <details> <summary>docs/versioned_docs/version-14.5/guides/offer-code-redemption.md (1)</summary><blockquote> `44-44`: **Remove redundant "OS" text.** The phrase "the iOS system" is redundant since iOS already expands to "operating system"; simplify to "the iOS system" → "iOS" or "the system". ```diff - The redemption sheet is handled by the iOS system + The redemption sheet is handled by iOSdocs/versioned_docs/version-14.5/examples/purchase-flow.md (1)
67-69: Add language specifier and proper spacing around code fence.The fenced code block is missing a language specifier and lacks proper blank lines per Markdown style guidelines (MD031, MD040).
2. Add environment variable: + ```bash EXPO_PUBLIC_IAPKIT_API_KEY=your_api_key_here ``` +docs/versioned_docs/version-14.5/api/methods/core-methods.md (1)
958-958: Fix redundant preposition in alternative billing introduction.Line 958 uses the verbose construction "outside of the platform's"; prefer the concise "outside the platform's".
-Alternative billing enables developers to offer payment options outside of the platform's standard billing systems. +Alternative billing enables developers to offer payment options outside the platform's standard billing systems.example/src/utils/errorUtils.ts (1)
36-36: Fallback may produce unhelpful messages for complex objects.Using
String(error)on complex objects without a meaningfultoString()implementation will produce[object Object]. Consider usingJSON.stringify(error)as a fallback or simply return'Unknown error'if the error format is unrecognized.Apply this diff to provide more helpful output:
- return String(error ?? 'Unknown error'); + try { + return error ? JSON.stringify(error) : 'Unknown error'; + } catch { + return 'Unknown error'; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/docs/getting-started/installation.md(3 hunks)docs/docs/installation.md(3 hunks)docs/docs/troubleshooting/storekit2-migration.md(1 hunks)docs/versioned_docs/version-14.5/api/methods/core-methods.md(1 hunks)docs/versioned_docs/version-14.5/api/types.md(1 hunks)docs/versioned_docs/version-14.5/examples/available-purchases.md(1 hunks)docs/versioned_docs/version-14.5/examples/purchase-flow.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/installation.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/setup-android.md(1 hunks)docs/versioned_docs/version-14.5/getting-started/setup-ios.md(1 hunks)docs/versioned_docs/version-14.5/guides/offer-code-redemption.md(1 hunks)docs/versioned_docs/version-14.5/guides/subscription-validation.md(1 hunks)docs/versioned_docs/version-14.5/installation.md(1 hunks)example/src/utils/errorUtils.ts(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/versioned_docs/version-14.5/guides/subscription-validation.md
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/versioned_docs/version-14.5/api/types.md
- docs/versioned_docs/version-14.5/getting-started/installation.md
- docs/docs/installation.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use type-only imports when importing types (import type).
Files:
example/src/utils/errorUtils.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/src/utils/errorUtils.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:
example/src/utils/errorUtils.ts
🧠 Learnings (9)
📓 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.
📚 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/docs/troubleshooting/storekit2-migration.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/getting-started/setup-ios.mddocs/versioned_docs/version-14.5/guides/offer-code-redemption.mddocs/versioned_docs/version-14.5/examples/available-purchases.mddocs/versioned_docs/version-14.5/examples/purchase-flow.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/docs/troubleshooting/storekit2-migration.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/getting-started/setup-ios.mddocs/versioned_docs/version-14.5/getting-started/setup-android.mddocs/versioned_docs/version-14.5/guides/offer-code-redemption.mdexample/src/utils/errorUtils.tsdocs/versioned_docs/version-14.5/examples/available-purchases.mddocs/versioned_docs/version-14.5/examples/purchase-flow.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 {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/docs/troubleshooting/storekit2-migration.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/getting-started/setup-android.mddocs/versioned_docs/version-14.5/guides/offer-code-redemption.mddocs/versioned_docs/version-14.5/examples/available-purchases.mddocs/versioned_docs/version-14.5/examples/purchase-flow.md
📚 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:
docs/docs/troubleshooting/storekit2-migration.mddocs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/getting-started/setup-android.mddocs/versioned_docs/version-14.5/examples/available-purchases.md
📚 Learning: 2025-12-08T20:40:05.783Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3101
File: docs/versioned_docs/version-14.4/guides/subscription-offers.md:103-110
Timestamp: 2025-12-08T20:40:05.783Z
Learning: When reviewing documentation in versioned_docs directories (e.g., docs/versioned_docs/version-14.4/), examples and API references should use the API surface that existed in that specific version, not the latest API. Only the current docs (in docs/ directory) should reflect new API changes.
Applied to files:
docs/versioned_docs/version-14.5/installation.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:
docs/versioned_docs/version-14.5/api/methods/core-methods.mddocs/versioned_docs/version-14.5/getting-started/setup-ios.mddocs/versioned_docs/version-14.5/getting-started/setup-android.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}} : Use Platform.OS checks for platform-specific logic in React Native code.
Applied to files:
docs/versioned_docs/version-14.5/guides/offer-code-redemption.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 {example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Do not call parseErrorStringToJsonObj() in app/user code; errors are already normalized by the library.
Applied to files:
example/src/utils/errorUtils.ts
🪛 LanguageTool
docs/versioned_docs/version-14.5/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] ~655-~655: 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] ~958-~958: 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] ~1023-~1023: 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.5/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)
🪛 markdownlint-cli2 (0.18.1)
docs/docs/troubleshooting/storekit2-migration.md
10-10: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
docs/versioned_docs/version-14.5/examples/available-purchases.md
66-66: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
70-70: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
docs/versioned_docs/version-14.5/examples/purchase-flow.md
67-67: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
67-67: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ 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). (3)
- GitHub Check: example-expo
- GitHub Check: build-android
- GitHub Check: build-ios
🔇 Additional comments (12)
docs/docs/getting-started/installation.md (3)
63-66: Clear documentation of Kotlin 2.2+ requirement.The addition properly documents the breaking change in version 14.6.0: that Google Play Billing Library v8.2.0 requires Kotlin 2.2+, and explains why manual configuration is needed (expo-modules-core doesn't yet support Kotlin 2.2). This guidance will help Expo users avoid build failures.
78-78: Example configuration updated correctly.The kotlinVersion in the example JSON has been updated from "2.1.20" to "2.2.0", matching the dependency bump and ensuring the example reflects the current minimum requirement.
146-146: Minor formatting improvement."Real-world examples" is the grammatically correct hyphenation for compound adjectives, improving the heading consistency.
docs/versioned_docs/version-14.5/installation.md (2)
63-85: ✅ Versioned documentation is correctly frozen.This installation guide appropriately reflects version 14.5's requirements: Google Play Billing Library v8.0.0 and Kotlin 2.1.20. As you noted in addressing the earlier review, versioned documentation must remain frozen to the API surface that existed at that version's release. New features introduced in 14.6 (Billing Programs API, Kotlin 2.2.0) belong only in current docs or new version-14.6 documentation, not in frozen versioned docs.
1-153: Documentation structure and content look solid.The installation guide covers prerequisites, package setup, Expo-specific configuration (with appropriate Folly workaround and deprecation migration notes), store configuration links, and examples. The guide is well-organized and provides clear upgrade paths for different RN versions.
docs/versioned_docs/version-14.5/examples/available-purchases.md (2)
32-56: Correctly implements hook state consumption pattern.The restore flow example properly addresses the prior feedback: it extracts
availablePurchasesfrom hook state, calls hook methods (which returnPromise<void>), and then iterates over the hook state property. The inline comment clearly explains this behavior, which helps users avoid a common pitfall.
62-71: Excellent clarification of hook vs root API distinction.The "Hook vs Root API" section effectively explains when to use hook methods (for state updates) versus root API imports (for direct Promise-based results), removing ambiguity for users integrating the library.
docs/versioned_docs/version-14.5/examples/purchase-flow.md (1)
73-129: ✓ Excellent safety checks for optional values.The examples now properly guard against missing
apiKeyandpurchaseTokenbefore passing them toverifyPurchaseWithProvider(), preventing potential runtime errors. Both the standaloneverifyPurchase()anduseIAPhook examples follow this defensive pattern with early returns on validation failure.docs/docs/troubleshooting/storekit2-migration.md (2)
100-124: Code example demonstrates proper error handling and hook usage.The
useIAPimplementation follows best practices: it checks theconnectedstate before invokingfetchProducts, includes proper error handling viaonPurchaseErrorcallback, and avoids expecting return values from async methods. This aligns well with the library's patterns.
17-170: Comprehensive troubleshooting coverage for StoreKit 2 migration.The guide methodically addresses the most common causes of StoreKit 2 failures (agreements, iOS versions, product setup, sandbox config, clean builds) and provides actionable steps for each. The error code table (lines 128–140), debugging checklist (lines 142–151), and migration-specific sections (lines 153–170) give readers a clear path to diagnose and resolve issues. The cross-reference to Receipt Validation docs (line 170) is helpful for those with server-side receipt handling.
example/src/utils/errorUtils.ts (2)
10-17: LGTM: Primitive type handling is a good addition.Explicitly handling primitive types prevents them from falling through to the generic fallback and ensures they are properly converted to strings.
5-37: Consider using library's error handling utilities for purchase errors.This custom error extraction utility is used in the purchase flow. However, the library already provides
getUserFriendlyErrorMessage()andisUserCancelledError()that work with normalizedErrorCodevalues. These library utilities are designed specifically for purchase error handling and provide consistent, user-friendly messages.Consider whether this custom utility is necessary, or if the library's error handling should be used instead for purchase-related errors.
Based on learnings, the library normalizes errors and provides utilities specifically for this purpose.
- Configure docusaurus to use 14.5 as the default (current) version - Set 14.6 as "Next" with unreleased banner - Update 14.6 blog post with @next tag installation instructions
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md(1 hunks)docs/docusaurus.config.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/docusaurus.config.ts
🧰 Additional context used
🧠 Learnings (1)
📓 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.
🪛 markdownlint-cli2 (0.18.1)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md
89-89: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
95-95: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
⏰ 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-ios
- GitHub Check: build-android
🔇 Additional comments (1)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md (1)
1-194: Comprehensive and well-structured release documentation.The blog post effectively communicates all key features introduced in 14.6.0, including the new Billing Programs API, one-time product discounts, and subscription improvements. The breaking change is clearly highlighted with migration guidance showing before/after code patterns. All new types and APIs are documented with practical examples. External references to Google Play docs and internal documentation enhance discoverability.
Verify that the internal documentation links (
/docs/next/intro,/docs/api/types) exist and are correctly referenced in the published docs.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 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.
📚 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/blog/2025-12-11-release-14.6.0-billing-programs.md
⏰ 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: example-expo
🔇 Additional comments (2)
docs/blog/2025-12-11-release-14.6.0-billing-programs.md (2)
88-101: Previous markdown formatting issue (MD031) has been resolved.The code blocks now include proper blank lines before them (line 89 before the first code block, line 96 before the second), satisfying the markdown linting requirement. The breaking change section is clearly structured with before/after migration examples.
1-196: Documentation is well-structured and accurately reflects the PR features.The blog post clearly communicates the major features (Billing Programs API, one-time discounts, new types) and prominently documents the breaking change with practical migration guidance. Content aligns well with the PR objectives and provides sufficient context for users to understand the upgrade path.
Summary
This release adds support for Google Play Billing Library 8.2.0 features, including the new Billing Programs API for external billing and one-time product discount support from Billing Library 7.0+.
New Features
1. Billing Programs API (Android 8.2.0+)
New methods for handling external billing programs:
isBillingProgramAvailableAndroid(program)- Check if a billing program is available for the usercreateBillingProgramReportingDetailsAndroid(program)- Get external transaction token for reportinglaunchExternalLinkAndroid(params)- Launch external link for billing programsSupported Programs:
external-offer- External offer programsexternal-content-link- External content link programs2. One-Time Product Discounts (Android 7.0+)
oneTimePurchaseOfferDetailsAndroidis now an array to support multiple offers with discounts.New Fields:
offerId- Unique offer identifierfullPriceMicros- Original price before discountdiscountDisplayInfo- Percentage and amount discount infolimitedQuantityInfo- Purchase quantity limitsvalidTimeWindow- Offer validity periodpreorderDetailsAndroid- Preorder release inforentalDetailsAndroid- Rental period info3. Purchase Suspension Status (Android 8.1.0+)
isSuspendedAndroidfield toPurchaseAndroidtypeBreaking Changes
oneTimePurchaseOfferDetailsAndroidType ChangeBefore (14.5.x):
After (14.6.0):
Migration: Update code that accesses this field to handle arrays:
New Types
OpenIAP Updates
Dependency Updates
Documentation
Files Changed
Core Implementation
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt- Billing Programs API implementationios/HybridRnIap.swift- iOS stub implementations for cross-platform APIsrc/specs/RnIap.nitro.ts- Nitro interface definitionssrc/types.ts- TypeScript type definitionssrc/index.ts- Public API exportsExample App
example/screens/AlternativeBilling.tsx- Billing Programs API demo with mode selectorexample/screens/AllProducts.tsx- One-time offer discount displayexample/src/components/AndroidOneTimeOfferDetails.tsx- Shared discount componentDocumentation
docs/blog/2025-12-11-release-14.6.0-billing-programs.md- Release blog postdocs/docs/api/types.md- Updated type documentationdocs/docs/api/methods/core-methods.md- New method documentationdocs/docs/guides/alternative-billing.md- Billing Programs guidedocs/docs/examples/alternative-billing.md- Updated examplesdocs/versioned_docs/version-14.5/- Complete 14.5 version documentationTesting
References
Summary by CodeRabbit
New Features
Breaking Changes
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.