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

feat: add verifyPurchaseWithProvider for IAPKit verification#3100

Merged
hyochan merged 7 commits into
mainfrom
feat/verifyPurchaseWithProvider
Dec 7, 2025
Merged

feat: add verifyPurchaseWithProvider for IAPKit verification#3100
hyochan merged 7 commits into
mainfrom
feat/verifyPurchaseWithProvider

Conversation

@hyochan

@hyochan hyochan commented Dec 7, 2025

Copy link
Copy Markdown
Owner
  • Add verifyPurchaseWithProvider function to verify purchases with external providers (IAPKit)
  • Add hasActiveSubscriptions helper function
  • Update types: rename ReceiptValidation* to VerifyPurchase* for consistency with OpenIAP
  • Add verification method selection UI to example screens (PurchaseFlow, SubscriptionFlow)
  • Configure react-native-dotenv for IAPKIT_API_KEY environment variable
  • Fix iOS: use stringValue instead of rawValue for Nitro enum serialization
  • Fix Android: convert Nitro enum to lowercase string for OpenIAP compatibility
  • Add enum mapping helper functions for Android (mapIapkitPurchaseState, mapIapkitStore, mapPurchaseVerificationProvider)

Summary by CodeRabbit

  • New Features

    • Added provider-based purchase verification and a verifyPurchase API.
    • Added hasActiveSubscriptions check.
    • Example app: verification method selector (ignore/local/IAPKit) and hook support.
    • Exposed verification methods in the public IAP hook and types.
  • Documentation

    • Added example environment config and usage notes for IAPKit API key.
  • Chores

    • Updated build tooling for environment loading and bumped native dependency versions.

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

- Add verifyPurchaseWithProvider function to verify purchases with external providers (IAPKit)
- Add hasActiveSubscriptions helper function
- Update types: rename ReceiptValidation* to VerifyPurchase* for consistency with OpenIAP
- Add verification method selection UI to example screens (PurchaseFlow, SubscriptionFlow)
- Configure react-native-dotenv for IAPKIT_API_KEY environment variable
- Fix iOS: use stringValue instead of rawValue for Nitro enum serialization
- Fix Android: convert Nitro enum to lowercase string for OpenIAP compatibility
- Add enum mapping helper functions for Android (mapIapkitPurchaseState, mapIapkitStore, mapPurchaseVerificationProvider)
@hyochan hyochan added the 🎯 feature New feature label Dec 7, 2025
@coderabbitai

coderabbitai Bot commented Dec 7, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e4bfc63 and 4322623.

📒 Files selected for processing (4)
  • docs/docs/api/methods/core-methods.md (2 hunks)
  • example/src/utils/errorUtils.ts (1 hunks)
  • src/__tests__/index.test.ts (1 hunks)
  • src/index.ts (6 hunks)

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

Adds provider-based purchase verification (IAPKit) and active-subscription checks across Android and iOS native bridges, expands verification types and public API (verifyPurchase / verifyPurchaseWithProvider / hasActiveSubscriptions), updates error-code mapping, and wires example app configuration and UI for verification selection.

Changes

Cohort / File(s) Summary
Android native bridge
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
Added hasActiveSubscriptions(subscriptionIds: Array<String>?) and verifyPurchaseWithProvider(params: NitroVerifyPurchaseWithProviderProps) methods; added enum-to-Nitro mapping helpers (mapIapkitPurchaseState, mapIapkitStore, mapPurchaseVerificationProvider) and exception mapping to OpenIapException.
iOS native bridge
ios/HybridRnIap.swift
Added hasActiveSubscriptions(subscriptionIds: [String]?) and verifyPurchaseWithProvider(params: NitroVerifyPurchaseWithProviderProps) methods; builds/serializes provider props, delegates to OpenIapModule, maps results to Nitro types, and adjusted validateReceipt prop construction.
Core types & specs
src/types.ts, src/specs/RnIap.nitro.ts
Introduced VerifyPurchase* types and IAPKit-related types (IapkitPurchaseState, IapkitStore, provider/request/result shapes); replaced legacy ReceiptValidation* types with VerifyPurchase* variants; added hasActiveSubscriptions and verifyPurchaseWithProvider to RnIap interface; adjusted Purchase/Product platform typing.
API surface & hooks
src/index.ts, src/hooks/useIAP.ts
Exported/renamed VerifyPurchaseResult* types; added verifyPurchase alias and verifyPurchaseWithProvider mutation implementations; exposed verifyPurchase and verifyPurchaseWithProvider via useIAP hook.
Error mapping utilities
src/utils/errorMapping.ts
Added alias mappings for deprecated/kebab-case codes, extended common error map with PurchaseVerification codes, updated getUserFriendlyErrorMessage and normalizeErrorCodeFromNative to handle new verification error variants and aliases.
Example app - config & env
example/.env.example, example/.gitignore, example/babel.config.js, example/package.json, example/src/types/env.d.ts
Added .env.example with IAPKIT_API_KEY placeholder; updated .gitignore to ignore .env*; configured react-native-dotenv in Babel and added dependency; added TypeScript declaration for @env.
Example app - verification hook & utils
example/src/hooks/useVerificationMethod.ts, example/src/utils/errorUtils.ts
Added useVerificationMethod hook and VerificationMethod type to manage/choose verification mode (ignore/local/iapkit) with platform-specific selector; added getErrorMessage util for normalized error strings.
Example UI - purchase/subscription flows
example/screens/PurchaseFlow.tsx, example/screens/SubscriptionFlow.tsx
Added VerificationMethod UI selector and props (verificationMethod, onChangeVerificationMethod); implemented post-purchase verification paths for local and iapkit (payload creation, API key handling, result alerts, error handling) and ref-synced method state.
Dependency versions & podspec
openiap-versions.json, NitroIap.podspec
Bumped OpenIAP versions (apple 1.2.39→1.2.41, google 1.3.7→1.3.8, gql 1.2.5→1.2.7); changed CocoaPods openiap dependency to exact version string in NitroIap.podspec.

Sequence Diagram(s)

sequenceDiagram
    participant App as Example App
    participant Hook as useIAP / UI
    participant Bridge as HybridRnIap (Kotlin/Swift)
    participant OpenIAP as OpenIAP Module
    participant Provider as IAPKit Provider

    App->>Hook: user triggers verification (iapkit)
    Hook->>Bridge: verifyPurchaseWithProvider(params)
    activate Bridge
    Bridge->>OpenIAP: verifyPurchaseWithProvider(openIapProps)
    activate OpenIAP
    OpenIAP->>Provider: send verification request (token/jws + apiKey)
    Provider-->>OpenIAP: verification response (isValid, state, store)
    OpenIAP-->>Bridge: mapped OpenIAP result
    deactivate OpenIAP
    Bridge-->>Hook: NitroVerifyPurchaseWithProviderResult
    deactivate Bridge
    Hook->>App: display verification result / alert
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing focused review:
    • Native-to-JS type mappings and enum translations in HybridRnIap.kt and HybridRnIap.swift.
    • Consistency between VerifyPurchaseWithProviderResult shapes across specs, types, index exports, and native bridges.
    • Error normalization/alias logic in src/utils/errorMapping.ts for backward compatibility.
    • Example app async flows, ref synchronization, and environment variable handling (react-native-dotenv integration).

Possibly related PRs

Suggested labels

🤖 android, 📱 iOS, 📗 example

Poem

🐰
I hopped through code both swift and Kotlin,
Tuned verifications, no more ploddin',
API keys tucked in env so neat,
Purchases checked from cloud to seat.
Hooray — the example app hops to greet! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.00% 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 PR title accurately describes the main feature addition—introducing verifyPurchaseWithProvider for IAPKit verification—which is the primary objective. The title is concise, specific, and clearly conveys the core change.

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 enhances the in-app purchase (IAP) module by integrating robust external purchase verification capabilities through IAPKit. It introduces a new API for provider-based verification, refactors existing receipt validation types for better clarity and consistency, and provides a convenient helper for checking active subscriptions. The example application has also been updated to demonstrate these new features, including a user interface for selecting verification methods and proper environment variable configuration.

Highlights

  • New IAPKit Verification: Introduced a verifyPurchaseWithProvider function for external purchase verification via IAPKit, supporting both Apple (JWS) and Google (purchaseToken) platforms.
  • Purchase Verification Refactor: Renamed ReceiptValidation* types and functions to VerifyPurchase* for improved clarity and consistency with OpenIAP. The validateReceipt function now aliases verifyPurchase.
  • Active Subscriptions Helper: Added a hasActiveSubscriptions function to easily check for active subscriptions, enhancing subscription management capabilities.
  • Example App Enhancements: Updated the example screens (PurchaseFlow, SubscriptionFlow) with a user interface to select purchase verification methods (None, Local, IAPKit) and integrated react-native-dotenv for secure API key management.
  • Platform-Specific Fixes & Mappings: Implemented platform-specific enum serialization fixes (iOS stringValue, Android lowercase conversion) and added helper functions for mapping OpenIAP enums to Nitro types on Android for seamless integration.
  • OpenIAP Dependency Update: Upgraded the underlying OpenIAP dependency to its latest versions across Apple, Google, and GraphQL components, ensuring access to the newest features and bug fixes.
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.

@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 a significant new feature for purchase verification with external providers like IAPKit, along with a new hasActiveSubscriptions helper function. The changes are well-structured, including necessary updates to native Android and iOS code, TypeScript types, and the example application. A key improvement is the renaming of ReceiptValidation* types to VerifyPurchase* for better consistency with OpenIAP.

My review focuses on a critical type-safety issue in the TypeScript definitions that could lead to runtime errors, and I've also identified opportunities to reduce code duplication in the example app by refactoring shared logic into utility functions or custom hooks. Addressing these points will improve the robustness and maintainability of the new functionality.

Comment thread src/index.ts Outdated
Comment thread src/types.ts
Comment thread example/screens/PurchaseFlow.tsx Outdated
Comment thread example/screens/PurchaseFlow.tsx Outdated
Comment thread example/screens/SubscriptionFlow.tsx Outdated
Comment thread example/screens/SubscriptionFlow.tsx Outdated

@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

🧹 Nitpick comments (5)
example/src/types/env.d.ts (1)

1-3: Consider typing as string | undefined to reflect runtime behavior.

The babel config sets allowUndefined: true, meaning IAPKIT_API_KEY could be undefined at runtime if the .env file is missing or the key is not set. The current string type may mask this, leading to runtime errors when consuming code assumes a non-null value.

 declare module '@env' {
-  export const IAPKIT_API_KEY: string;
+  export const IAPKIT_API_KEY: string | undefined;
 }
ios/HybridRnIap.swift (1)

319-350: verifyPurchaseWithProvider mapping looks correct; consider tightening enum fallback

The new verifyPurchaseWithProvider flow correctly:

  • Logs provider via params.provider.stringValue (string enum) rather than raw numeric values.
  • Builds OpenIAP VerifyPurchaseWithProviderProps via JSON serialization.
  • Maps result.iapkit and result.provider back into Nitro enums using string raw values.

One minor robustness improvement: PurchaseVerificationProvider(fromString: result.provider.rawValue) ?? .iapkit silently maps unknown providers to .iapkit. If additional providers are added later, this could misrepresent the actual provider. Prefer either:

  • Throwing on unknown values, or
  • Falling back to a neutral sentinel (e.g. .none) rather than hard‑coding .iapkit.

Also applies to: 352-394

src/index.ts (1)

1769-1834: Consider delegating hasActiveSubscriptions to the new native bridge for efficiency

getActiveSubscriptions now goes through the native getActiveSubscriptions bridge and builds rich ActiveSubscription objects, while hasActiveSubscriptions still derives its result by fetching and mapping all active subscriptions:

const activeSubscriptions = await getActiveSubscriptions(subscriptionIds);
return activeSubscriptions.length > 0;

Now that RnIap exposes a native hasActiveSubscriptions(subscriptionIds?: string[]): Promise<boolean>, you could avoid the extra allocation and mapping by delegating directly:

export const hasActiveSubscriptions: QueryField<'hasActiveSubscriptions'> = async (
  subscriptionIds,
) => {
  try {
    return await IAP.instance.hasActiveSubscriptions(subscriptionIds ?? undefined);
  } catch (error) {
    RnIapConsole.warn('Error checking active subscriptions:', error);
    return false;
  }
};

Not mandatory, but this would better leverage the new bridge and reduce overhead on hot paths.

Also applies to: 1928-1939

example/screens/SubscriptionFlow.tsx (1)

1363-1371: Tighten IAPKit verification payload to be platform‑specific and reuse existing helpers where possible

The post‑purchase verification flow is well‑structured (optional based on verificationMethod, good logging, redaction, and guarded by IAPKIT_API_KEY and token checks). Two refinements would make it more robust:

  1. Only send the relevant platform block to IAPKit

Right now both apple and google are always populated with the same jwsOrToken:

const verifyRequest: VerifyPurchaseWithProviderProps = {
  provider: 'iapkit',
  iapkit: {
    apiKey,
    apple: { jws: jwsOrToken },
    google: { purchaseToken: jwsOrToken },
  },
};

Prefer branching on Platform.OS so the payload matches what the backend expects:

const base: VerifyPurchaseWithProviderProps = { provider: 'iapkit' };

const verifyRequest: VerifyPurchaseWithProviderProps =
  Platform.OS === 'ios'
    ? {
        ...base,
        iapkit: {
          apiKey,
          apple: { jws: jwsOrToken },
        },
      }
    : {
        ...base,
        iapkit: {
          apiKey,
          google: { purchaseToken: jwsOrToken },
        },
      };
  1. Optional: standardize error messaging

The manual extraction of errorMessage works, but if verification errors surface normalized IAP error shapes, consider using the library’s existing helpers (e.g. getUserFriendlyErrorMessage) in the future to keep messaging consistent across flows. (Only if those helpers are already exported for app code.)

Also applies to: 1425-1562

example/screens/PurchaseFlow.tsx (1)

411-418: Align IAPKit verification payload with the current platform and consider sharing the verification helper

The IAPKit verification logic in onPurchaseSuccess closely matches SubscriptionFlow’s and generally looks solid (good masking/logging, configuration checks, and user alerts). Two incremental improvements:

  1. Platform‑specific payload, as in SubscriptionFlow suggestion

Currently both apple and google blocks are filled with the same jwsOrToken. Restricting the payload to the active platform (only apple.jws on iOS, only google.purchaseToken on Android) will better match the expected contract and avoid surprising the backend.

  1. Optional: extract shared verification helper

The verification block here and in SubscriptionFlowContainer are nearly identical. Consider extracting a small helper (e.g. runPostPurchaseVerification({ purchase, verificationMethod })) or custom hook in the example app to keep the two flows in sync and reduce duplication.

Also applies to: 450-573

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 709e60c and 61b7d6c.

⛔ Files ignored due to path filters (2)
  • example/ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (4 hunks)
  • example/.env.example (1 hunks)
  • example/.gitignore (1 hunks)
  • example/babel.config.js (1 hunks)
  • example/package.json (1 hunks)
  • example/screens/PurchaseFlow.tsx (10 hunks)
  • example/screens/SubscriptionFlow.tsx (11 hunks)
  • example/src/types/env.d.ts (1 hunks)
  • ios/HybridRnIap.swift (3 hunks)
  • openiap-versions.json (1 hunks)
  • src/hooks/useIAP.ts (5 hunks)
  • src/index.ts (5 hunks)
  • src/specs/RnIap.nitro.ts (6 hunks)
  • src/types.ts (12 hunks)
  • src/utils/errorMapping.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When declaring API params/results in TS modules, import canonical types from src/types.ts rather than creating ad‑hoc interfaces.

Files:

  • src/hooks/useIAP.ts
  • src/index.ts
  • src/utils/errorMapping.ts
  • src/specs/RnIap.nitro.ts
  • src/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use type-only imports when importing types (import type).

Files:

  • src/hooks/useIAP.ts
  • src/index.ts
  • example/src/types/env.d.ts
  • src/utils/errorMapping.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example/screens/SubscriptionFlow.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:

  • src/hooks/useIAP.ts
  • src/index.ts
  • example/src/types/env.d.ts
  • src/utils/errorMapping.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example/screens/SubscriptionFlow.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/src/types/env.d.ts
  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
**/*.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
{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:

  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • ios/HybridRnIap.swift
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 (10)
📓 Common learnings
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3002
File: docs/docs/getting-started/setup-ios.md:55-60
Timestamp: 2025-09-14T00:13:04.055Z
Learning: The validateReceipt function from useIAP hook in react-native-iap expects a transaction identifier as parameter, which is accessed via purchase.id (not purchase.productId). This is confirmed by the maintainer hyochan and aligns with the library's migration from purchase.transactionId to purchase.id.
📚 Learning: 2025-09-18T16:45:10.582Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.

Applied to files:

  • openiap-versions.json
  • src/hooks/useIAP.ts
  • src/index.ts
  • src/utils/errorMapping.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • src/types.ts
  • example/screens/SubscriptionFlow.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:

  • src/hooks/useIAP.ts
  • src/index.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • src/types.ts
  • example/screens/SubscriptionFlow.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:

  • src/hooks/useIAP.ts
  • src/index.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • ios/HybridRnIap.swift
  • src/types.ts
  • example/screens/SubscriptionFlow.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 {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:

  • src/hooks/useIAP.ts
  • src/index.ts
  • src/utils/errorMapping.ts
  • example/screens/PurchaseFlow.tsx
  • src/types.ts
  • example/screens/SubscriptionFlow.tsx
📚 Learning: 2025-09-13T01:07:18.841Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 2999
File: android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt:644-660
Timestamp: 2025-09-13T01:07:18.841Z
Learning: In Android IAP error handling: purchaseToken and productId are distinct properties - purchaseToken identifies a completed purchase transaction (should be null in error cases), while productId is the product SKU for context

Applied to files:

  • src/index.ts
  • example/screens/PurchaseFlow.tsx
  • src/specs/RnIap.nitro.ts
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • src/types.ts
  • example/screens/SubscriptionFlow.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: Always use yarn for package management in this repo (Yarn 3 workspaces); run typecheck and lint before committing.

Applied to files:

  • example/.gitignore
📚 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:

  • src/utils/errorMapping.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Use Platform.OS checks for platform-specific logic in React Native code.

Applied to files:

  • example/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.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 **/*.nitro.ts : After modifying any .nitro.ts interface files, regenerate Nitro bridge files (yarn specs).

Applied to files:

  • src/specs/RnIap.nitro.ts
🧬 Code graph analysis (7)
src/hooks/useIAP.ts (2)
src/types.ts (4)
  • VerifyPurchaseProps (844-849)
  • VerifyPurchaseResult (851-851)
  • VerifyPurchaseWithProviderProps (885-888)
  • VerifyPurchaseWithProviderResult (890-894)
src/index.ts (2)
  • verifyPurchase (1423-1423)
  • verifyPurchaseWithProvider (1446-1473)
src/index.ts (4)
src/types.ts (3)
  • VerifyPurchaseResultIOS (874-883)
  • VerifyPurchaseResultAndroid (853-872)
  • MutationField (958-963)
src/utils/debug.ts (1)
  • RnIapConsole (18-49)
src/utils/error.ts (1)
  • parseErrorStringToJsonObj (27-77)
src/utils/errorMapping.ts (1)
  • createPurchaseError (115-134)
example/screens/PurchaseFlow.tsx (6)
src/hooks/useIAP.ts (1)
  • useIAP (118-476)
src/index.ts (3)
  • useIAP (106-106)
  • verifyPurchase (1423-1423)
  • verifyPurchaseWithProvider (1446-1473)
example/src/types/env.d.ts (1)
  • IAPKIT_API_KEY (2-2)
src/types.ts (1)
  • VerifyPurchaseWithProviderProps (885-888)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
  • verifyPurchaseWithProvider (1123-1174)
ios/HybridRnIap.swift (1)
  • verifyPurchaseWithProvider (352-394)
src/specs/RnIap.nitro.ts (1)
src/types.ts (7)
  • IapPlatform (181-181)
  • IapkitPurchaseState (184-184)
  • IapkitStore (186-186)
  • PurchaseVerificationProvider (549-549)
  • VerifyPurchaseAndroidOptions (837-842)
  • VerifyPurchaseProps (844-849)
  • VerifyPurchaseResultAndroid (853-872)
ios/HybridRnIap.swift (4)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (2)
  • hasActiveSubscriptions (553-578)
  • verifyPurchaseWithProvider (1123-1174)
src/index.ts (2)
  • hasActiveSubscriptions (1928-1939)
  • verifyPurchaseWithProvider (1446-1473)
src/types.ts (4)
  • VerifyPurchaseWithProviderProps (885-888)
  • IapkitPurchaseState (184-184)
  • IapkitStore (186-186)
  • PurchaseVerificationProvider (549-549)
src/specs/RnIap.nitro.ts (5)
  • NitroVerifyPurchaseWithIapkitResult (230-234)
  • IapkitPurchaseState (30-39)
  • IapkitStore (41-41)
  • NitroVerifyPurchaseWithProviderResult (236-239)
  • PurchaseVerificationProvider (44-44)
src/types.ts (1)
src/specs/RnIap.nitro.ts (3)
  • IapkitPurchaseState (30-39)
  • IapkitStore (41-41)
  • PurchaseVerificationProvider (44-44)
example/screens/SubscriptionFlow.tsx (7)
android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
  • result (14-16)
ios/RnIapLog.swift (2)
  • result (41-43)
  • error (35-35)
src/index.ts (2)
  • verifyPurchase (1423-1423)
  • verifyPurchaseWithProvider (1446-1473)
example/src/types/env.d.ts (1)
  • IAPKIT_API_KEY (2-2)
src/types.ts (1)
  • VerifyPurchaseWithProviderProps (885-888)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
  • verifyPurchaseWithProvider (1123-1174)
ios/HybridRnIap.swift (1)
  • verifyPurchaseWithProvider (352-394)
🪛 detekt (1.23.8)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt

[warning] 566-566: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 1162-1162: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-android
  • GitHub Check: build-ios
🔇 Additional comments (22)
openiap-versions.json (1)

2-4: LGTM!

Version bumps align with the new hasActiveSubscriptions and verifyPurchaseWithProvider features introduced in this PR. Based on learnings, these bumps will regenerate the types in src/types.ts from the updated GraphQL schema.

android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (3)

553-578: LGTM!

The hasActiveSubscriptions method follows the established patterns in this file: initializes connection, logs payload/result, delegates to OpenIAP, and maps exceptions consistently. The static analysis warning about swallowed exception at line 566 is a false positive - the exception is properly re-thrown as OpenIapException with context.


1123-1174: LGTM!

The verifyPurchaseWithProvider implementation is well-structured:

  • Correctly converts Nitro enum to lowercase string for OpenIAP compatibility
  • Uses null-safe let blocks for optional nested props
  • Follows established error handling pattern (re-throws as OpenIapException)

The static analysis warning about swallowed exception at line 1162 is a false positive - the exception is properly logged and re-thrown with context.


1407-1435: LGTM!

The mapping helpers are well-implemented:

  • mapIapkitPurchaseState correctly handles both underscore (PENDING_ACKNOWLEDGMENT) and kebab-case (PENDING-ACKNOWLEDGMENT) variations
  • mapIapkitStore defaulting to GOOGLE is appropriate since this is Android-specific code
  • mapPurchaseVerificationProvider safely defaults to NONE for unknown providers
src/utils/errorMapping.ts (3)

14-25: LGTM!

Good backward-compatibility approach for the naming migration from Receipt* to PurchaseVerification* error codes. The aliases cover:

  • Uppercase formats (RECEIPT_FAILED, E_RECEIPT_FAILED)
  • Kebab-case formats (receipt-failed)

This ensures existing native code using old error code formats will map correctly to the new enum values.


337-345: LGTM!

The fall-through pattern correctly groups deprecated Receipt* codes with their new PurchaseVerification* equivalents, ensuring consistent user-facing messages during the migration period. As per coding guidelines, using getUserFriendlyErrorMessage() with normalized ErrorCode is the preferred approach.


370-380: LGTM!

The dual alias lookup correctly handles both case formats:

  1. Uppercase lookup (line 371) for SCREAMING_SNAKE_CASE codes from native
  2. Lowercase lookup (line 377) for kebab-case codes like 'receipt-failed'

This ensures comprehensive backward compatibility with various native error code formats.

src/types.ts (1)

1-4: LGTM!

This is an auto-generated file from the OpenIAP GraphQL schema (as noted in the header and per coding guidelines). The new types (IapkitPurchaseState, IapkitStore, VerifyPurchase*, RequestVerifyPurchaseWithIapkit*) align with the openiap-gql version bump (1.2.5 → 1.2.7) and support the new IAPKit verification feature.

example/.gitignore (1)

76-80: LGTM!

Good addition for protecting sensitive environment variables like IAPKIT_API_KEY. The patterns cover standard env file naming conventions (.env, .env.local, .env*.local).

example/.env.example (1)

1-3: LGTM!

Clear example configuration with helpful instructions for obtaining the API key. The placeholder value is obviously not a real key.

example/babel.config.js (1)

3-15: LGTM!

The react-native-dotenv plugin configuration is correct. Note that allowUndefined: true means missing env variables won't cause build errors—ensure consuming code handles potentially undefined values gracefully (as flagged in the type declaration review).

src/hooks/useIAP.ts (5)

17-18: LGTM!

New verification functions imported correctly from the top-level module.


38-41: LGTM!

Type-only imports used correctly for the new verification types, following the coding guidelines.


83-90: LGTM!

Type signatures and JSDoc comments are clear and correctly define the new verification methods.


331-345: LGTM!

The implementations correctly delegate to the top-level functions using useCallback with empty dependencies, following the established pattern in this hook (e.g., requestPurchase).


453-454: LGTM!

New verification methods correctly exposed in the hook's return object.

ios/HybridRnIap.swift (1)

252-282: hasActiveSubscriptions implementation aligns with getActiveSubscriptions usage

The new hasActiveSubscriptions wrapper mirrors the getActiveSubscriptions pattern (connection check, payload/result logging, simple error propagation) and is consistent with the Android implementation. I don’t see functional issues here.

src/index.ts (2)

30-32: Receipt verification result remapping matches new VerifyPurchase types*

The updated validateReceipt implementation cleanly re-maps Nitro results into the new VerifyPurchaseResultIOS/VerifyPurchaseResultAndroid shapes, including converting latestTransaction via convertNitroPurchaseToPurchase and normalizing Android’s deferredSku to string | null. This matches the definitions in src/types.ts and looks sound.

Also applies to: 1337-1400


1413-1423: verifyPurchase alias keeps API backward-compatible

Exporting verifyPurchase as an alias of validateReceipt maintains OpenIAP naming while preserving existing behavior and types. No issues here.

example/screens/SubscriptionFlow.tsx (1)

12-33: VerificationMethod wiring and UI are cohesive and platform‑aware

The introduction of VerificationMethod, its inclusion in SubscriptionFlowProps, the header “Purchase Verification” selector, and the container’s verificationMethod state/ref (with iOS ActionSheetIOS vs Android Alert) all hang together cleanly. The use of a ref to avoid stale closures in async callbacks is a good touch.

Also applies to: 214-232, 590-623, 1348-1355, 1854-1900

example/screens/PurchaseFlow.tsx (1)

11-12: Purchase verification selector and state management are consistent with SubscriptionFlow

The Purchase flow mirrors the Subscription flow’s verification concept (VerificationMethod type, props, header selector, container state/ref, and platform‑specific selector UI). This symmetry will make the example app much easier to reason about.

Also applies to: 21-37, 41-53, 66-78, 190-207, 396-410, 674-720, 722-735

src/specs/RnIap.nitro.ts (1)

7-25: Nitro spec now aligns with VerifyPurchase and provider APIs; run yarn specs to regenerate bridges*

The spec changes are internally consistent:

  • Receipt validation params/options/results now use the VerifyPurchase* types from src/types.ts as a single source of truth
  • NitroPurchase.platform and NitroProduct.platform use the shared IapPlatform union matching public types
  • IAPKit provider types (NitroVerifyPurchaseWithIapkit*, NitroVerifyPurchaseWithProvider*) match the shapes consumed/produced by Android and iOS verifyPurchaseWithProvider implementations
  • New hasActiveSubscriptions and verifyPurchaseWithProvider entries on RnIap align with native bridges and JS surface

After modifying src/specs/RnIap.nitro.ts, regenerate bridge files by running yarn specs to keep native stubs in sync with this spec.

Comment thread example/package.json
Comment thread src/index.ts
- Extract getErrorMessage utility to reduce code duplication
- Extract useVerificationMethod hook for verification method selection
- Update PurchaseFlow and SubscriptionFlow to use shared utilities
- Fix openiap podspec to use exact version match

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@codecov

codecov Bot commented Dec 7, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.06452% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.97%. Comparing base (abaeaed) to head (4322623).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/utils/errorMapping.ts 18.18% 9 Missing ⚠️
src/hooks/useIAP.ts 33.33% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3100      +/-   ##
==========================================
- Coverage   63.00%   62.97%   -0.03%     
==========================================
  Files           9        9              
  Lines        1538     1564      +26     
  Branches      515      522       +7     
==========================================
+ Hits          969      985      +16     
- Misses        563      573      +10     
  Partials        6        6              
Flag Coverage Δ
library 62.97% <58.06%> (-0.03%) ⬇️

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

Files with missing lines Coverage Δ
src/index.ts 66.75% <100.00%> (+0.54%) ⬆️
src/hooks/useIAP.ts 41.33% <33.33%> (-0.34%) ⬇️
src/utils/errorMapping.ts 68.04% <18.18%> (-1.86%) ⬇️
🚀 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.

Replace unsafe type cast with runtime validation to ensure provider
is 'iapkit'. Throws DeveloperError if unsupported provider is returned.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add documentation for verifyPurchaseWithProvider() function
including usage examples, parameters, return types, and
platform-specific behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add bookmark links to Unified APIs section in core-methods.md
- Add comprehensive tests for verifyPurchaseWithProvider function:
  - iOS verification with JWS token
  - Android verification with purchase token
  - Provider validation (throws on unsupported provider)
  - Verification failure states (expired, etc.)
  - Error handling for native errors
  - Null iapkit param handling

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add tests matching expo-iap test patterns:
- Various IAPKit purchase states (all 9 states)
- Inauthentic verification response
- Ready-to-consume state for consumables
- Pending-acknowledgment state for subscriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

@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

♻️ Duplicate comments (1)
src/index.ts (1)

1446-1472: Unsafe type cast on result.provider remains unaddressed.

The cast result.provider as 'iapkit' on line 1455 masks the fact that the native layer can return other provider values (e.g., 'none'). This was flagged in previous reviews. Either update PurchaseVerificationProvider in src/types.ts to include all possible values ('iapkit' | 'none'), or add runtime validation before returning.

   return {
-      provider: result.provider as 'iapkit',
+      provider: result.provider,
       iapkit: result.iapkit.map((item) => ({

This requires updating the type definition in src/types.ts:

export type PurchaseVerificationProvider = 'iapkit' | 'none';
🧹 Nitpick comments (6)
NitroIap.podspec (1)

45-46: Consider using to_s instead of string interpolation.

The static analysis tool suggests using apple_version.to_s instead of "#{apple_version}" for cleaner code when no additional formatting is needed.

  # OpenIAP Apple for StoreKit 2 integration
  # Exact version match for consistent builds
-  s.dependency 'openiap', "#{apple_version}"
+  s.dependency 'openiap', apple_version.to_s
example/screens/PurchaseFlow.tsx (1)

495-506: Both apple and google objects are populated regardless of platform.

The verification request includes both apple.jws and google.purchaseToken with the same token value on every platform. Consider populating only the platform-specific field based on Platform.OS to avoid potential confusion on the server side and to send only the relevant data.

             const verifyRequest: VerifyPurchaseWithProviderProps = {
               provider: 'iapkit',
               iapkit: {
                 apiKey,
-                apple: {
-                  jws: jwsOrToken,
-                },
-                google: {
-                  purchaseToken: jwsOrToken,
-                },
+                ...(Platform.OS === 'ios'
+                  ? { apple: { jws: jwsOrToken } }
+                  : { google: { purchaseToken: jwsOrToken } }),
               },
             };
example/screens/SubscriptionFlow.tsx (4)

225-226: Verification method UI & wiring look good; optional reuse of label helper

The new verificationMethod / onChangeVerificationMethod props, the selector UI, and the wiring via useVerificationMethod in the container are cohesive and keep selection state nicely centralized. The styles for the verification block also match the existing header aesthetic.

If you want to avoid duplicating the mapping from VerificationMethod → label across screens, you could optionally reuse getVerificationMethodLabel from useVerificationMethod (e.g., pass a computed label down as a prop) while still decorating it with emojis here. Not required, just a small DRY/readability tweak.

Also applies to: 233-234, 245-253, 608-624, 1848-1857, 1897-1926


23-33: Prefer import type for type-only imports from react-native-iap

VerifyPurchaseWithProviderProps, ActiveSubscription, ProductSubscription, ProductSubscriptionAndroid, Purchase, and PurchaseError are used only as types. Per the repo's TypeScript guidelines, separate type-only imports using import type:

-import {
-  requestPurchase,
-  useIAP,
-  deepLinkToSubscriptions,
-  type ActiveSubscription,
-  type ProductSubscription,
-  type ProductSubscriptionAndroid,
-  type Purchase,
-  type PurchaseError,
-  type VerifyPurchaseWithProviderProps,
-  ErrorCode,
-} from 'react-native-iap';
+import {
+  requestPurchase,
+  useIAP,
+  deepLinkToSubscriptions,
+  ErrorCode,
+} from 'react-native-iap';
+import type {
+  ActiveSubscription,
+  ProductSubscription,
+  ProductSubscriptionAndroid,
+  Purchase,
+  PurchaseError,
+  VerifyPurchaseWithProviderProps,
+} from 'react-native-iap';

1590-1599: Use isUserCancelledError and getUserFriendlyErrorMessage for purchase error handling

For onPurchaseError (lines 1590–1599) and the requestPurchase catch block (lines 1809–1814), you currently use error.message directly. To align with the library's error handling patterns and improve UX:

  • Use isUserCancelledError(error) to detect and handle user cancellations separately (softer messaging or silent return).
  • Use getUserFriendlyErrorMessage(error) to get localized, user-friendly messages instead of raw error messages.

Example pattern:

-import {ErrorCode} from 'react-native-iap';
+import {ErrorCode, isUserCancelledError, getUserFriendlyErrorMessage} from 'react-native-iap';

onPurchaseError: (error: PurchaseError) => {
  console.error('Subscription failed:', error);
  setIsProcessing(false);

  if (isUserCancelledError(error)) {
    setPurchaseResult('❌ Subscription cancelled by user');
    return;
  }

  const dt = Date.now() - lastSuccessAtRef.current;
  if (error?.code === ErrorCode.ServiceError && dt >= 0 && dt < 1500) {
    return;
  }

  const message = getUserFriendlyErrorMessage(error);
  setPurchaseResult(`❌ Subscription failed: ${message}`);
  Alert.alert('Subscription Failed', message);
}

Apply the same pattern to the catch block at lines 1809–1814.


1425-1547: Consider platform-specific IAPKit payload structure for clearer intent and improved UX on verification failure

The verification flow is well-structured: you redact sensitive fields from logs, gate behavior on verificationMethodRef.current, validate IAPKIT_API_KEY, and use getErrorMessage for robust error messaging. Two refinements align with React Native best practices:

  1. Platform-specific iapkit payload

Currently you send both apple.jws and google.purchaseToken with the same jwsOrToken value. To make the payload unambiguous and reflect platform-specific intent, conditionally include only the relevant key:

-        const verifyRequest: VerifyPurchaseWithProviderProps = {
-          provider: 'iapkit',
-          iapkit: {
-            apiKey,
-            apple: {
-              jws: jwsOrToken,
-            },
-            google: {
-              purchaseToken: jwsOrToken,
-            },
-          },
-        };
+        const verifyRequest: VerifyPurchaseWithProviderProps = {
+          provider: 'iapkit',
+          iapkit: {
+            apiKey,
+            ...(Platform.OS === 'ios'
+              ? {apple: {jws: jwsOrToken}}
+              : {google: {purchaseToken: jwsOrToken}}),
+          },
+        };
  1. Clarify UX when verification fails but purchase succeeds

Because verification happens before finishTransaction and you always show a final "Purchase completed successfully!" alert, a failed verification currently surfaces both a "Verification Failed" alert and a generic success alert. For demo purposes this is acceptable, but consider adjusting success messaging or skipping the generic alert when verification fails, so sample behavior more closely mirrors production expectations.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61b7d6c and e4bfc63.

📒 Files selected for processing (6)
  • NitroIap.podspec (1 hunks)
  • example/screens/PurchaseFlow.tsx (8 hunks)
  • example/screens/SubscriptionFlow.tsx (9 hunks)
  • example/src/hooks/useVerificationMethod.ts (1 hunks)
  • example/src/utils/errorUtils.ts (1 hunks)
  • src/index.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use type-only imports when importing types (import type).

Files:

  • example/src/utils/errorUtils.ts
  • example/screens/PurchaseFlow.tsx
  • src/index.ts
  • example/src/hooks/useVerificationMethod.ts
  • example/screens/SubscriptionFlow.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/src/utils/errorUtils.ts
  • example/screens/PurchaseFlow.tsx
  • example/src/hooks/useVerificationMethod.ts
  • example/screens/SubscriptionFlow.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/src/utils/errorUtils.ts
  • example/screens/PurchaseFlow.tsx
  • src/index.ts
  • example/src/hooks/useVerificationMethod.ts
  • example/screens/SubscriptionFlow.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/index.ts
🧠 Learnings (7)
📓 Common learnings
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: 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
📚 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:

  • example/src/utils/errorUtils.ts
  • example/screens/PurchaseFlow.tsx
  • src/index.ts
  • example/screens/SubscriptionFlow.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:

  • example/screens/PurchaseFlow.tsx
  • src/index.ts
  • example/screens/SubscriptionFlow.tsx
📚 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/PurchaseFlow.tsx
  • src/index.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : In useIAP hook usage, do not expect returned data from methods that return Promise<void>; consume state from the hook instead.

Applied to files:

  • example/screens/PurchaseFlow.tsx
  • src/index.ts
  • example/src/hooks/useVerificationMethod.ts
  • example/screens/SubscriptionFlow.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/screens/PurchaseFlow.tsx
  • example/screens/SubscriptionFlow.tsx
📚 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/PurchaseFlow.tsx
  • src/index.ts
  • example/screens/SubscriptionFlow.tsx
🧬 Code graph analysis (2)
src/index.ts (3)
src/types.ts (3)
  • VerifyPurchaseResultIOS (874-883)
  • VerifyPurchaseResultAndroid (853-872)
  • MutationField (958-963)
src/utils/debug.ts (1)
  • RnIapConsole (18-49)
src/utils/error.ts (1)
  • parseErrorStringToJsonObj (27-77)
example/screens/SubscriptionFlow.tsx (7)
example/src/hooks/useVerificationMethod.ts (2)
  • VerificationMethod (4-4)
  • useVerificationMethod (17-97)
src/index.ts (2)
  • verifyPurchase (1423-1423)
  • verifyPurchaseWithProvider (1446-1472)
example/src/types/env.d.ts (1)
  • IAPKIT_API_KEY (2-2)
src/types.ts (1)
  • VerifyPurchaseWithProviderProps (885-888)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
  • verifyPurchaseWithProvider (1123-1174)
ios/HybridRnIap.swift (1)
  • verifyPurchaseWithProvider (352-394)
example/src/utils/errorUtils.ts (1)
  • getErrorMessage (5-25)
🪛 RuboCop (1.81.7)
NitroIap.podspec

[convention] 46-46: Prefer to_s over string interpolation.

(Style/RedundantInterpolation)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-android
  • GitHub Check: build-ios
🔇 Additional comments (7)
src/index.ts (2)

30-31: LGTM!

Type-only imports are correctly used for the renamed types, following the coding guidelines.


1413-1423: LGTM!

The verifyPurchase alias to validateReceipt maintains backward compatibility while aligning with the OpenIAP naming convention. The documentation clearly explains its purpose.

example/src/hooks/useVerificationMethod.ts (1)

17-96: LGTM!

Well-structured hook with proper platform-specific UI handling. The ref synchronization pattern correctly addresses stale closure issues in async callbacks. The type definitions and return interface are clean.

example/screens/PurchaseFlow.tsx (4)

32-37: LGTM!

Type-only import is correctly used for VerifyPurchaseWithProviderProps and related types, following the coding guidelines.


192-208: LGTM!

Clean UI implementation for the verification method selector with clear visual feedback.


450-557: LGTM on the verification flow structure.

Good use of verificationMethodRef.current to capture the current verification method in the async callback, avoiding stale closure issues. Error handling correctly uses getErrorMessage instead of parseErrorStringToJsonObj as per the coding guidelines for example code.


570-589: LGTM!

Error handling correctly uses ErrorCode.UserCancelled for checking user cancellation, following the preferred pattern from coding guidelines.

Comment thread example/src/utils/errorUtils.ts
Add early return for empty errors array to prevent displaying
'undefined' to users when JSON.stringify(undefined) is called.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant