Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.

feat(android): support android billing client 8.2.0#3103

Merged
hyochan merged 16 commits into
mainfrom
feat/android-billing-8.1.0
Dec 11, 2025
Merged

feat(android): support android billing client 8.2.0#3103
hyochan merged 16 commits into
mainfrom
feat/android-billing-8.1.0

Conversation

@hyochan

@hyochan hyochan commented Dec 11, 2025

Copy link
Copy Markdown
Owner

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 user
  • createBillingProgramReportingDetailsAndroid(program) - Get external transaction token for reporting
  • launchExternalLinkAndroid(params) - Launch external link for billing programs

Supported Programs:

  • external-offer - External offer programs
  • external-content-link - External content link programs
import {
  isBillingProgramAvailableAndroid,
  createBillingProgramReportingDetailsAndroid,
  launchExternalLinkAndroid,
} from 'react-native-iap';

// Step 1: Check availability
const result = await isBillingProgramAvailableAndroid('external-offer');
if (!result.isAvailable) return;

// Step 2: Launch external link
await launchExternalLinkAndroid({
  billingProgram: 'external-offer',
  launchMode: 'launch-in-external-browser-or-app',
  linkType: 'link-to-digital-content-offer',
  linkUri: 'https://your-payment-site.com',
});

// Step 3: Get reporting token
const details = await createBillingProgramReportingDetailsAndroid('external-offer');
// Report token to Google Play within 24 hours

2. One-Time Product Discounts (Android 7.0+)

oneTimePurchaseOfferDetailsAndroid is now an array to support multiple offers with discounts.

New Fields:

  • offerId - Unique offer identifier
  • fullPriceMicros - Original price before discount
  • discountDisplayInfo - Percentage and amount discount info
  • limitedQuantityInfo - Purchase quantity limits
  • validTimeWindow - Offer validity period
  • preorderDetailsAndroid - Preorder release info
  • rentalDetailsAndroid - Rental period info

3. Purchase Suspension Status (Android 8.1.0+)

  • Added isSuspendedAndroid field to PurchaseAndroid type

Breaking Changes

oneTimePurchaseOfferDetailsAndroid Type Change

Before (14.5.x):

oneTimePurchaseOfferDetailsAndroid?: ProductAndroidOneTimePurchaseOfferDetail | null;

After (14.6.0):

oneTimePurchaseOfferDetailsAndroid?: ProductAndroidOneTimePurchaseOfferDetail[] | null;

Migration: Update code that accesses this field to handle arrays:

// Before
const price = product.oneTimePurchaseOfferDetailsAndroid?.formattedPrice;

// After
const offers = product.oneTimePurchaseOfferDetailsAndroid;
const price = offers?.[0]?.formattedPrice;

New Types

// Billing Programs
type BillingProgramAndroid = 'unspecified' | 'external-content-link' | 'external-offer';
type ExternalLinkLaunchModeAndroid = 'unspecified' | 'launch-in-external-browser-or-app' | 'caller-will-launch-link';
type ExternalLinkTypeAndroid = 'unspecified' | 'link-to-digital-content-offer' | 'link-to-app-download';

interface LaunchExternalLinkParamsAndroid {
  billingProgram: BillingProgramAndroid;
  launchMode: ExternalLinkLaunchModeAndroid;
  linkType: ExternalLinkTypeAndroid;
  linkUri: string;
}

interface BillingProgramAvailabilityResultAndroid {
  billingProgram: BillingProgramAndroid;
  isAvailable: boolean;
}

interface BillingProgramReportingDetailsAndroid {
  billingProgram: BillingProgramAndroid;
  externalTransactionToken: string;
}

// One-Time Product Discounts
interface DiscountDisplayInfoAndroid {
  percentageDiscount?: number | null;
  discountAmount?: DiscountAmountAndroid | null;
}

interface DiscountAmountAndroid {
  discountAmountMicros: string;
  formattedDiscountAmount: string;
}

interface LimitedQuantityInfoAndroid {
  maximumQuantity: number;
  remainingQuantity: number;
}

interface ValidTimeWindowAndroid {
  startTimeMillis: string;
  endTimeMillis: string;
}

interface PreorderDetailsAndroid {
  preorderReleaseTimeMillis: string;
  preorderPresaleEndTimeMillis: string;
}

interface RentalDetailsAndroid {
  rentalPeriod: string;
  rentalExpirationPeriod?: string | null;
}

OpenIAP Updates

Dependency Updates

  • Kotlin: 2.1.20 → 2.2.0

Documentation

  • Added versioned docs for 14.5 (preserving previous API documentation)
  • Updated types documentation with new Android discount fields
  • Updated alternative billing guide with Billing Programs API
  • Added release blog post for 14.6.0

Files Changed

Core Implementation

  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt - Billing Programs API implementation
  • ios/HybridRnIap.swift - iOS stub implementations for cross-platform API
  • src/specs/RnIap.nitro.ts - Nitro interface definitions
  • src/types.ts - TypeScript type definitions
  • src/index.ts - Public API exports

Example App

  • example/screens/AlternativeBilling.tsx - Billing Programs API demo with mode selector
  • example/screens/AllProducts.tsx - One-time offer discount display
  • example/src/components/AndroidOneTimeOfferDetails.tsx - Shared discount component

Documentation

  • docs/blog/2025-12-11-release-14.6.0-billing-programs.md - Release blog post
  • docs/docs/api/types.md - Updated type documentation
  • docs/docs/api/methods/core-methods.md - New method documentation
  • docs/docs/guides/alternative-billing.md - Billing Programs guide
  • docs/docs/examples/alternative-billing.md - Updated examples
  • docs/versioned_docs/version-14.5/ - Complete 14.5 version documentation

Testing

  • Added tests for Android one-time purchase offer array handling
  • Example app includes full Billing Programs API flow demonstration

References

Summary by CodeRabbit

  • New Features

    • Google Play Billing Programs API (Android 8.2.0+) — enable/availability/reporting and external-link launching; richer Android one‑time offer metadata; purchase suspended flag; example app verification UI and Android deep‑link flow.
  • Breaking Changes

    • oneTimePurchaseOfferDetailsAndroid changed from a single object to an array.
  • Documentation

    • Extensive docs, guides and examples for Billing Programs, alternative billing, offers, installation, error handling, and troubleshooting.
  • Chores

    • Kotlin requirement bumped to 2.2.0+ and Billing Library references updated.

✏️ Tip: You can customize this high-level summary in your review settings.

- 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
@coderabbitai

coderabbitai Bot commented Dec 11, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Introduces 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

Cohort / File(s) Summary
Android Bridge Implementation
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
Added enableBillingProgramAndroid, isBillingProgramAvailableAndroid, createBillingProgramReportingDetailsAndroid, launchExternalLinkAndroid; mapping helpers for BillingProgram/ExternalLink enums; lazy OpenIapStore integration and extended error handling.
iOS Bridge Stubs
ios/HybridRnIap.swift
Added Android-only stub methods that throw feature-not-supported errors on iOS for the new billing-program APIs.
Nitro Type Specs
src/specs/RnIap.nitro.ts
Added Billing Programs types and Nitro launch/reporting interfaces; extended NitroOneTimePurchaseOfferDetail with discount/quantity/timewindow/preorder/rental fields; changed oneTimePurchaseOfferDetailsAndroid to an array; added isSuspendedAndroid to NitroPurchase.
Public Type Exports & API
src/types.ts, src/index.ts
Exported BillingProgramAndroid, ExternalLinkLaunchModeAndroid, ExternalLinkTypeAndroid, LaunchExternalLinkParamsAndroid, BillingProgramAvailabilityResultAndroid, BillingProgramReportingDetailsAndroid; expanded ProductAndroidOneTimePurchaseOfferDetail and switched offer detail to array; added subscription replacement and discount/preorder/rental types; added four public Android API functions.
Type Bridge Utilities
src/utils/type-bridge.ts
Convert Nitro isSuspendedAndroid into public Purchase mapping.
Android Tests & Unit Tests
src/__tests__/index.test.ts
Added unit tests/mocks for new Android billing-program methods and external-link flows, with platform guards and error scenarios.
Examples — App Flows
example-expo/app/purchase-flow.tsx, example-expo/app/subscription-flow.tsx, example/screens/PurchaseFlow.tsx, example/screens/SubscriptionFlow.tsx
Added verification-selector (local/IAPKit), integrated verification in purchase flow, rendered Android one-time and subscription offers with new fields, and updated modal scrolling/UI.
Examples — Components & Hooks
example-expo/components/AndroidOneTimeOfferDetails.tsx, example/src/components/AndroidOneTimeOfferDetails.tsx, example-expo/hooks/useVerificationMethod.ts
New AndroidOneTimeOfferDetails component(s) to render offer details; new useVerificationMethod hook and VerificationMethod type.
Examples — Alternative Billing UI
example/screens/AlternativeBilling.tsx
Added Billing Programs mode with availability check, external link launch, reporting token creation, and new UI controls for selecting program/mode.
Example Utilities
example-expo/utils/errorUtils.ts, example/src/utils/errorUtils.ts
Added/updated getErrorMessage utilities to normalize error messages.
Example Scripts & Config
example-expo/scripts/copy-screens.sh, example/android/build.gradle
Extended import path rewrites; inlined IAPKIT_API_KEY fallback; bumped Kotlin version to 2.2.0.
Documentation — API & Guides
docs/docs/api/methods/core-methods.md, docs/docs/api/types.md, docs/docs/guides/alternative-billing.md, docs/docs/examples/alternative-billing.md, docs/docs/getting-started/installation.md
Added Billing Programs API docs, updated types docs to show one-time offer array and new types, added step-by-step guides and examples, updated Kotlin/Billing Library requirements.
Documentation — Release/Versioning & Site
docs/blog/2025-12-11-release-14.6.0-billing-programs.md, docs/versioned_docs/version-14.5/*, docs/docusaurus.config.ts, docs/versions.json, docs/versioned_sidebars/version-14.5-sidebars.json
Added release notes and a full versioned docs tree (14.5/14.6-next) including API reference, guides, examples, and sidebars.
Docs Styling
docs/src/css/custom.css
Adjusted blog sidebar active-state styling to color/weight-only for light/dark modes.
Example Tests (UI)
example/__tests__/screens/PurchaseFlow.test.tsx
Added fixtures and test cases for Android one-time offers (discount types, limited quantity, valid windows, preorder/rental).
OpenIAP Version Pins
openiap-versions.json
Updated OpenIAP versions: google 1.3.10 → 1.3.12, gql 1.3.0 → 1.3.2.

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>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt — OpenIAP lazy init, enum mappings, error translation, and Android-specific control flow.
  • src/specs/RnIap.nitro.ts, src/types.ts, src/index.ts — Breaking type change: oneTimePurchaseOfferDetailsAndroid from single object to array; confirm consumers, generated artifacts, and bindings updated.
  • Examples and tests — verify verification integration, new UI (ScrollView) and Android offer renderers behave correctly across platforms and test mocks align.
  • Error mapping/exception shapes — ensure OpenIAP error JSON mapping and OpenIapException handling are consistent and documented.

Possibly related PRs

Suggested labels

📖 documentation, ❄️ types, 🧪 test

Poem

🐰 Hopping through code with joyful cheer,

Offers bloom and links appear,
Billing programs leap to play,
Types grow wide and docs convey,
A rabbit's nod to changes near—hip, hop, hooray! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.16% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding support for Android Billing Client 8.2.0, which aligns with the primary objective of introducing the Billing Programs API and related features.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/android-billing-8.1.0

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 273af9b and 5d711b7.

📒 Files selected for processing (1)
  • docs/blog/2025-12-11-release-14.6.0-billing-programs.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • 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). (3)
  • GitHub Check: build-ios
  • GitHub Check: build-android
  • GitHub Check: example-expo

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @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

  • Enhanced Android One-Time Purchase Offers: Full support for Google Play's one-time product discounts, including detailed information such as discount percentages, limited quantity, valid time windows, pre-order, and rental specifics. The oneTimePurchaseOfferDetailsAndroid field is now an array to accommodate multiple offers.
  • Subscription Suspension Status: Integration of the isSuspendedAndroid field from Billing Library 8.1.0+, allowing applications to identify and react to suspended subscriptions.
  • API and Documentation Updates: Comprehensive updates to the API types (src/types.ts, src/specs/RnIap.nitro.ts) and documentation (docs/docs/api/types.md, docs/blog/2025-12-11-release-14.5.1-android-discounts.md) to reflect the new billing features and the breaking change for one-time offers.
  • Example App Enhancements: The example application has been updated to visually demonstrate the new one-time purchase and subscription offer details in product modals, improving clarity for developers.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov

codecov Bot commented Dec 11, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.92%. Comparing base (54ceba8) to head (5d711b7).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            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              
Flag Coverage Δ
library 63.92% <100.00%> (+0.77%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/index.ts 68.08% <100.00%> (+1.46%) ⬆️
src/utils/type-bridge.ts 73.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces comprehensive support for 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.

Comment thread example/screens/PurchaseFlow.tsx
Comment thread example/screens/SubscriptionFlow.tsx Outdated
- 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%
@hyochan hyochan added the 🎯 feature New feature label Dec 11, 2025
@hyochan hyochan added 🤖 android Related to android 🛠 bugfix All kinds of bug fixes labels Dec 11, 2025
- 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
@hyochan hyochan changed the title feat(android): support new android billing client feat(android): support android billing client 8.2.0 Dec 11, 2025
@hyochan hyochan marked this pull request as ready for review December 11, 2025 10:48
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 specific should be react-native-iap-specific when 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 related should be yoga-related when 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 Types
docs/versioned_docs/version-14.5/getting-started/setup-horizon.md (1)

19-19: Add hyphen to compound modifier.

react-native-iap specific should be react-native-iap-specific when used as a compound modifier before "configuration".

docs/versioned_docs/version-14.5/api/use-iap.md (1)

342-392: Clarify validateReceipt parameter - this takes a transaction identifier, not product ID.

Based on learnings, the validateReceipt function from the useIAP hook expects a transaction identifier accessed via purchase.id, not purchase.productId. The documentation shows productId as 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 validateReceipt function expects purchase.id (transaction identifier).

example/screens/AlternativeBilling.tsx (1)

126-134: Consider using isUserCancelledError() helper for error handling.

Per coding guidelines, prefer using isUserCancelledError() with normalized ErrorCode when 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 snippet

The 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_here

This 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}`);
}

PurchaseError is documented as an interface, and in the type definitions it’s exposed as a TypeScript type, not necessarily a runtime class. In that case instanceof PurchaseError won’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 PurchaseError is 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 manual ErrorCode checks

In onPurchaseError, you’re manually comparing error.code to ErrorCode.UserCancelled and interpolating error.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, and
  • getUserFriendlyErrorMessage(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 direct ErrorCode checks in app code.


389-398: Remove duplicated/unused offer styles from this screen

AndroidOneTimeOfferDetails owns its own styling (via its own StyleSheet.create), and in this file you never reference styles.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 AndroidOneTimeOfferDetails and 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() and getUserFriendlyErrorMessage() 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 reportExternalTransaction is 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 example

The handlePurchaseError switch is fine, but since the library exposes helpers like isUserCancelledError() and getUserFriendlyErrorMessage() (with normalized ErrorCode), 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 Expo expo-build-properties example

The Expo config snippet pins android.kotlinVersion to "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 zero percentageDiscount explicitly

The 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 percentageDiscount of 0 would fall through to the amount / 'N/A' branch. If 0 is a possible (or future) value, consider checking percentageDiscount != null instead 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 types VerifyPurchaseWithProviderProps and ProductSubscriptionAndroidOfferDetails are 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 apple and google fields, but only one is relevant based on the current platform. While the library handles this correctly, sending only the relevant data is cleaner.

             const 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 in src/types.ts (lines 68-98, 218-305) which is auto-generated.

Since src/index.ts already re-exports everything from src/types.ts via export * from './types' (line 65), these manual definitions will shadow the auto-generated ones. As per coding guidelines, prefer importing canonical types from src/types.ts rather 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

📥 Commits

Reviewing files that changed from the base of the PR and between 71c739e and 46484f5.

⛔ Files ignored due to path filters (1)
  • example/ios/Podfile.lock is 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.ts
  • example-expo/hooks/useVerificationMethod.ts
  • docs/docusaurus.config.ts
  • example/screens/AllProducts.tsx
  • src/utils/type-bridge.ts
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • example/screens/PurchaseFlow.tsx
  • example/src/components/AndroidOneTimeOfferDetails.tsx
  • example/screens/SubscriptionFlow.tsx
  • example/screens/AlternativeBilling.tsx
  • src/index.ts
  • src/__tests__/index.test.ts
  • example-expo/components/AndroidOneTimeOfferDetails.tsx
  • example-expo/app/purchase-flow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example-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.ts
  • example-expo/hooks/useVerificationMethod.ts
  • example/screens/AllProducts.tsx
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • example/screens/PurchaseFlow.tsx
  • example/src/components/AndroidOneTimeOfferDetails.tsx
  • example/screens/SubscriptionFlow.tsx
  • example/screens/AlternativeBilling.tsx
  • example-expo/components/AndroidOneTimeOfferDetails.tsx
  • example-expo/app/purchase-flow.tsx
  • example-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.ts
  • example-expo/hooks/useVerificationMethod.ts
  • example/screens/AllProducts.tsx
  • src/utils/type-bridge.ts
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • example/screens/PurchaseFlow.tsx
  • example/src/components/AndroidOneTimeOfferDetails.tsx
  • example/screens/SubscriptionFlow.tsx
  • example/screens/AlternativeBilling.tsx
  • src/index.ts
  • src/__tests__/index.test.ts
  • example-expo/components/AndroidOneTimeOfferDetails.tsx
  • example-expo/app/purchase-flow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example-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.ts
  • src/index.ts
  • src/__tests__/index.test.ts
  • src/specs/RnIap.nitro.ts
  • src/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.swift
  • android/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.md
  • docs/versioned_docs/version-14.5/guides/troubleshooting.md
  • example-expo/utils/errorUtils.ts
  • docs/versioned_docs/version-14.5/examples/alternative-billing.md
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/api/use-iap.md
  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
  • docs/versioned_docs/version-14.5/guides/subscription-offers.md
  • docs/versioned_docs/version-14.5/api/types.md
  • example/screens/AlternativeBilling.tsx
  • docs/versioned_docs/version-14.5/api/methods/listeners.md
  • docs/docs/examples/alternative-billing.md
  • docs/versioned_docs/version-14.5/api/error-codes.md
  • docs/versioned_docs/version-14.5/api/error-handling.md
  • docs/versioned_docs/version-14.5/guides/purchases.md
  • docs/versioned_docs/version-14.5/guides/offer-code-redemption.md
  • example-expo/app/purchase-flow.tsx
  • src/types.ts
  • example-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.md
  • docs/versioned_docs/version-14.5/guides/troubleshooting.md
  • example-expo/hooks/useVerificationMethod.ts
  • example/screens/AllProducts.tsx
  • docs/versioned_docs/version-14.5/examples/purchase-flow.md
  • docs/versioned_docs/version-14.5/examples/alternative-billing.md
  • docs/versioned_docs/version-14.5/guides/expo-plugin.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/api/use-iap.md
  • docs/versioned_docs/version-14.5/examples/available-purchases.md
  • docs/versioned_docs/version-14.5/api/index.md
  • docs/versioned_docs/version-14.5/guides/subscription-validation.md
  • docs/versioned_docs/version-14.5/guides/lifecycle.md
  • example/screens/PurchaseFlow.tsx
  • docs/versioned_docs/version-14.5/guides/subscription-offers.md
  • docs/versioned_docs/version-14.5/api/types.md
  • example/screens/AlternativeBilling.tsx
  • docs/versioned_docs/version-14.5/examples/subscription-flow.md
  • docs/versioned_docs/version-14.5/api/methods/listeners.md
  • docs/docs/examples/alternative-billing.md
  • docs/versioned_docs/version-14.5/api/error-codes.md
  • docs/versioned_docs/version-14.5/guides/faq.md
  • docs/versioned_docs/version-14.5/guides/purchases.md
  • example-expo/app/purchase-flow.tsx
  • src/types.ts
  • example-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.md
  • example/screens/AllProducts.tsx
  • docs/versioned_docs/version-14.5/examples/purchase-flow.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/api/use-iap.md
  • docs/versioned_docs/version-14.5/getting-started/setup-ios.md
  • docs/versioned_docs/version-14.5/guides/subscription-validation.md
  • docs/versioned_docs/version-14.5/guides/lifecycle.md
  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
  • docs/versioned_docs/version-14.5/api/types.md
  • example/screens/AlternativeBilling.tsx
  • docs/versioned_docs/version-14.5/api/methods/listeners.md
  • docs/docs/examples/alternative-billing.md
  • docs/versioned_docs/version-14.5/guides/faq.md
  • docs/versioned_docs/version-14.5/guides/purchases.md
  • package.json
  • example-expo/app/purchase-flow.tsx
  • src/types.ts
  • example-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.tsx
  • src/utils/type-bridge.ts
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
  • docs/versioned_docs/version-14.5/api/types.md
  • example/screens/AlternativeBilling.tsx
  • src/index.ts
  • src/__tests__/index.test.ts
  • example-expo/components/AndroidOneTimeOfferDetails.tsx
  • docs/docs/api/types.md
  • docs/versioned_docs/version-14.5/guides/purchases.md
  • example-expo/app/purchase-flow.tsx
  • src/specs/RnIap.nitro.ts
  • src/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.tsx
  • example/__tests__/screens/PurchaseFlow.test.tsx
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/api/use-iap.md
  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • docs/docs/api/types.md
  • docs/versioned_docs/version-14.5/api/error-handling.md
  • docs/versioned_docs/version-14.5/guides/purchases.md
  • example-expo/app/purchase-flow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example-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

Comment thread docs/versioned_docs/version-14.5/api/methods/core-methods.md Outdated
Comment thread docs/versioned_docs/version-14.5/api/types.md
Comment thread docs/versioned_docs/version-14.5/examples/available-purchases.md
Comment thread docs/versioned_docs/version-14.5/examples/purchase-flow.md
Comment on lines +88 to +109
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>
);
};
```

@coderabbitai coderabbitai Bot Dec 11, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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 -200

Repository: hyochan/react-native-iap

Length of output: 7953


🏁 Script executed:

# Search for ProductAndroid type definition
rg "export interface ProductAndroid" src/types.ts -A 30

Repository: hyochan/react-native-iap

Length of output: 1920


🏁 Script executed:

# Search for ProductSubscriptionAndroid type definition
rg "export interface ProductSubscriptionAndroid" src/types.ts -A 30

Repository: hyochan/react-native-iap

Length of output: 1788


🏁 Script executed:

# Search for ProductAndroidOneTimePurchaseOfferDetail
rg "ProductAndroidOneTimePurchaseOfferDetail" src/types.ts -A 15

Repository: 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 15

Repository: 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 2

Repository: hyochan/react-native-iap

Length of output: 175


🏁 Script executed:

# Check ProductSubscription type
rg "export type ProductSubscription = " src/types.ts -A 2

Repository: 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: ProductAndroid exposes oneTimePurchaseOfferDetailsAndroid, and it's an array of details (ProductAndroidOneTimePurchaseOfferDetail[]), not a single oneTimePurchaseOfferDetails object. The example at line 105 will fail TypeScript type-checking.
  • Subscriptions: Android subscriptions use subscriptionOfferDetailsAndroid on the type, not subscription.subscriptionOfferDetails at 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>
);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

14.5 docs is backup. Shouldn't change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

Comment on lines +63 to +65
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.

@coderabbitai coderabbitai Bot Dec 11, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This is a versioned doc. Versioned doc shouldn't change to recent info.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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"

@coderabbitai coderabbitai Bot Dec 11, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
"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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

It is a versioned docs shouldn't change!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This is a versioned doc. Versioned doc shouldn't change to recent info.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

Comment thread docs/versioned_docs/version-14.5/installation.md Outdated
Comment thread example-expo/utils/errorUtils.ts Outdated
Comment thread openiap-versions.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 presentCodeRedemptionSheet instead of the correct exported function presentCodeRedemptionSheetIOS (consistent with other iOS-specific methods in the library). This was already flagged in the iOS Implementation section, which correctly uses presentCodeRedemptionSheetIOS.

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 oneTimePurchaseOfferDetailsAndroid as 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 over subscriptionOfferDetailsAndroid as 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 iOS
docs/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 meaningful toString() implementation will produce [object Object]. Consider using JSON.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

📥 Commits

Reviewing files that changed from the base of the PR and between 46484f5 and 56e4732.

📒 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.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/getting-started/setup-ios.md
  • docs/versioned_docs/version-14.5/guides/offer-code-redemption.md
  • docs/versioned_docs/version-14.5/examples/available-purchases.md
  • docs/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.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/getting-started/setup-ios.md
  • docs/versioned_docs/version-14.5/getting-started/setup-android.md
  • docs/versioned_docs/version-14.5/guides/offer-code-redemption.md
  • example/src/utils/errorUtils.ts
  • docs/versioned_docs/version-14.5/examples/available-purchases.md
  • docs/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.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/getting-started/setup-android.md
  • docs/versioned_docs/version-14.5/guides/offer-code-redemption.md
  • docs/versioned_docs/version-14.5/examples/available-purchases.md
  • docs/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.md
  • docs/versioned_docs/version-14.5/api/methods/core-methods.md
  • docs/versioned_docs/version-14.5/getting-started/setup-android.md
  • docs/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.md
  • docs/versioned_docs/version-14.5/getting-started/setup-ios.md
  • docs/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 availablePurchases from hook state, calls hook methods (which return Promise<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 apiKey and purchaseToken before passing them to verifyPurchaseWithProvider(), preventing potential runtime errors. Both the standalone verifyPurchase() and useIAP hook 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 useIAP implementation follows best practices: it checks the connected state before invoking fetchProducts, includes proper error handling via onPurchaseError callback, 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() and isUserCancelledError() that work with normalized ErrorCode values. 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.

Comment thread docs/docs/troubleshooting/storekit2-migration.md Outdated
Comment thread docs/versioned_docs/version-14.5/getting-started/setup-ios.md
- 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 065b49d and d7569c1.

📒 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.

Comment thread docs/blog/2025-12-11-release-14.6.0-billing-programs.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7569c1 and 273af9b.

📒 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.

Comment thread docs/blog/2025-12-11-release-14.6.0-billing-programs.md Outdated
@hyochan hyochan merged commit 045933e into main Dec 11, 2025
12 checks passed
@hyochan hyochan deleted the feat/android-billing-8.1.0 branch December 11, 2025 14:47
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

🤖 android Related to android 🛠 bugfix All kinds of bug fixes 🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant