Skip to content

Conversation

@hyochan
Copy link
Owner

@hyochan hyochan commented Dec 28, 2025

  • Add External Payments program (Japan only) with side-by-side payment choice
  • Add developerProvidedBillingListenerAndroid for handling external payment selection
  • Add enableBillingProgramAndroid to InitConnectionConfig for cleaner setup
  • Add DeveloperBillingOptionParamsAndroid and DeveloperBillingLaunchModeAndroid types
  • Update openiap versions (gql 1.3.10, google 1.3.19, apple 1.3.8)
  • Add documentation and tests for External Payments API

Summary by CodeRabbit

  • New Features

    • Android External Payments support (Google Play Billing 8.3.0+), including developer-provided billing flow and listener APIs; new public types for passing external transaction tokens
    • Example app UI updated to demonstrate External Payments flow and token handling
  • Documentation

    • Added comprehensive guides, API docs, examples, and platform notes (Japan availability, 24‑hour reporting)
  • Tests

    • Added tests covering External Payments and developer-provided billing listener behavior

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

- Add External Payments program (Japan only) with side-by-side payment choice
- Add developerProvidedBillingListenerAndroid for handling external payment selection
- Add enableBillingProgramAndroid to InitConnectionConfig for cleaner setup
- Add DeveloperBillingOptionParamsAndroid and DeveloperBillingLaunchModeAndroid types
- Update openiap versions (gql 1.3.10, google 1.3.19, apple 1.3.8)
- Add documentation and tests for External Payments API
@hyochan hyochan added 🤖 android Related to android 🎯 feature New feature labels Dec 28, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 50 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 8e582ff and 842494a.

📒 Files selected for processing (1)
  • src/index.ts

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

Walkthrough

Adds Android External Payments (Developer Provided Billing) support: native OpenIAP listener wiring, new public listener APIs and types, init-time billing program enablement, TypeScript API wrappers, tests, docs, and version bumps for OpenIAP deps.

Changes

Cohort / File(s) Summary
Android native integration
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
Added OpenIapDeveloperProvidedBillingListener wiring, preserved alternativeBillingModeAndroid mapping, added enableBillingProgramAndroid handling, developerProvidedBilling listener collection, add/remove native methods, and internal dispatch (sendDeveloperProvidedBilling).
Nitro spec/types
src/specs/RnIap.nitro.ts, src/types.ts
Added external-payments to BillingProgramAndroid, introduced DeveloperBillingLaunchModeAndroid, DeveloperProvidedBillingDetailsAndroid, DeveloperBillingOptionParamsAndroid, expanded IapEvent/InitConnectionConfig/RequestPurchase* types and subscription fields for developer-provided billing.
TypeScript public API
src/index.ts
New developerProvidedBillingListenerAndroid public function, DeveloperProvidedBillingDetailsAndroid interface, WeakMap wrapper type and lifecycle handling for add/remove listeners with platform/init guards.
Examples / UI
example/screens/AlternativeBilling.tsx
UI and flow updated to support external-payments: new token state, listener registration, purchase routing for developerBillingOption, token display and clear action.
Tests
src/__tests__/index.test.ts
Added tests for developerProvidedBillingListenerAndroid behavior (platform guard, attach/forward/remove) and billing program enablement for external-payments.
Documentation
docs/docs/api/methods/listeners.md, docs/docs/guides/alternative-billing.md, docs/blog/2025-12-28-release-14.6.4.md
Added External Payments docs: usage, listener examples, config notes, comparisons with User Choice Billing, and release notes (Japan availability, 24-hour reporting).
Dependency versions
openiap-versions.json
Bumped versions: apple: 1.3.7 → 1.3.8, google: 1.3.16 → 1.3.19, gql: 1.3.8 → 1.3.10.

Sequence Diagram(s)

sequenceDiagram
    participant App as App Code
    participant IAP as React Native IAP
    participant Nitro as Nitro Bridge (HybridRnIap)
    participant OpenIAP as OpenIAP Library
    participant GP as Google Play Billing

    rect rgba(230,245,255,0.4)
    Note over App,IAP: Init + enable external-payments
    App->>IAP: initConnection({ enableBillingProgramAndroid: 'external-payments' })
    IAP->>Nitro: initConnection(config)
    Nitro->>OpenIAP: init with enableBillingProgram
    Nitro->>Nitro: register OpenIapDeveloperProvidedBillingListener
    end

    rect rgba(230,245,255,0.15)
    Note over App,IAP: Listener registration
    App->>IAP: developerProvidedBillingListenerAndroid(listener)
    IAP->>Nitro: addDeveloperProvidedBillingListenerAndroid(listener)
    Nitro->>Nitro: store listener
    end

    rect rgba(200,230,255,0.15)
    Note over App,GP: Purchase flow (external payments)
    App->>IAP: requestPurchase({ developerBillingOption: {...} })
    IAP->>Nitro: requestPurchase(params)
    Nitro->>OpenIAP: launch external billing flow
    OpenIAP->>GP: show developer/payment option
    GP-->>OpenIAP: user selects external payment
    OpenIAP->>Nitro: onDeveloperProvidedBilling(token)
    Nitro->>IAP: sendDeveloperProvidedBilling({ externalTransactionToken: token })
    IAP->>App: invoke listener(details)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #2999: Foundational OpenIAP Android integration — this PR extends that init/bridge with developer-provided billing hooks.
  • PR #3049: Modifies same HybridRnIap.kt init/wiring for alternative billing programs; overlaps with billing-program mapping and listener additions.
  • PR #2989: Changes to Android native bridge/types that intersect with the new developer-provided billing fields and Nitro type mappings.

Suggested labels

📖 documentation, 🧪 test, ❄️ types, dependencies

Poem

Hop-hop! A token leaps into view,
External payments — a fresh path to pursue.
Listeners tuned, bridges set to sing,
OpenIAP hums as the tokens spring. 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 pull request title accurately describes the main feature being added: Google Play Billing 8.3.0 External Payments support, which aligns with the primary changes across Kotlin, TypeScript, documentation, and example code.

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
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 integrates the latest Google Play Billing Library 8.3.0 features, specifically the External Payments program, into the system. This enables applications to present users in Japan with a direct choice between Google Play's billing and an external payment method. The changes involve introducing new listeners and configuration options for managing this payment flow, updating core dependencies, and providing clear documentation and tests for developers to adopt this new functionality.

Highlights

  • Google Play Billing 8.3.0 External Payments Support: This pull request introduces comprehensive support for Google Play Billing Library 8.3.0's External Payments program, which allows developers to offer a side-by-side payment choice between Google Play Billing and their own external payment system, currently available only in Japan.
  • New Listener for External Payments: A new listener, developerProvidedBillingListenerAndroid, has been added to handle events when a user selects the developer's external payment option during the purchase flow. This listener provides an externalTransactionToken that must be reported to Google Play within 24 hours.
  • Enhanced Initialization Configuration: The InitConnectionConfig now includes an enableBillingProgramAndroid option, allowing for a cleaner setup of billing programs, including the new 'external-payments' program, directly during the connection initialization.
  • New Types and API Parameters: New types such as DeveloperBillingOptionParamsAndroid and DeveloperBillingLaunchModeAndroid have been introduced to configure and manage the external payment option within purchase requests, defining how the external payment link is launched.
  • Updated OpenIAP Dependencies: The underlying openiap library versions have been updated, specifically openiap-google to 1.3.19 to support Google Play Billing 8.3.0, openiap-gql to 1.3.10 for new external payments types, and openiap-apple to 1.3.8 for connection management improvements.
  • Comprehensive Documentation and Testing: Extensive documentation has been added, including a new blog post, updates to the API listeners guide, and a dedicated section in the alternative billing guide, along with new test cases to ensure the correct functionality of the External Payments API.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

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.

Copy link
Contributor

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

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 support for Google Play Billing 8.3.0's External Payments program, which is a significant feature addition. The changes are comprehensive, covering native Android implementation, TypeScript definitions, tests, and extensive documentation. The code is well-structured and the documentation is clear. I've found one potential issue in the native Android code regarding a redundant call, which I've detailed in a specific comment. Overall, this is a great contribution.

@codecov
Copy link

codecov bot commented Dec 28, 2025

Codecov Report

❌ Patch coverage is 80.76923% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.93%. Comparing base (ba335f3) to head (842494a).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/index.ts 80.76% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3117      +/-   ##
==========================================
+ Coverage   64.68%   64.93%   +0.24%     
==========================================
  Files           9        9              
  Lines        1665     1691      +26     
  Branches      564      569       +5     
==========================================
+ Hits         1077     1098      +21     
- Misses        582      587       +5     
  Partials        6        6              
Flag Coverage Δ
library 64.93% <80.76%> (+0.24%) ⬆️

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

Files with missing lines Coverage Δ
src/index.ts 68.84% <80.76%> (+0.38%) ⬆️
🚀 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/index.ts (2)

121-129: developerBillingOption from requestPurchase is not forwarded to native on Android

Both RequestPurchaseAndroidProps and RequestSubscriptionAndroidProps (src/types.ts:977, 1051) include the developerBillingOption field for External Payments support (8.3.0+), but the requestPurchase function (lines 1224–1271) does not forward it to androidPayload. Similarly, NitroRequestPurchaseAndroid (src/specs/RnIap.nitro.ts:121) does not include this field.

This means callers can pass developerBillingOption as documented in the release notes and type definitions, but it will be silently dropped before reaching IAP.instance.requestPurchase.

To support External Payments as documented:

  1. Add developerBillingOption?: DeveloperBillingOptionParamsAndroid | null; to NitroRequestPurchaseAndroid in src/specs/RnIap.nitro.ts.
  2. Forward androidRequest.developerBillingOption to androidPayload.developerBillingOption in the Android branch for both subscription and in-app purchase flows.
  3. Verify the field name and type match the native/OpenIAP side expectations.

2350-2353: Exported BillingProgramAndroid union is missing 'external-payments'

The public BillingProgramAndroid type exported from src/index.ts (lines 2350–2353) only includes:

export type BillingProgramAndroid =
  | 'unspecified'
  | 'external-content-link'
  | 'external-offer';

However:

  • The canonical src/types.ts definition includes 'external-payments'.
  • Tests call enableBillingProgramAndroid('external-payments').
  • Docs recommend and demonstrate the same string literal.

This mismatch causes TypeScript consumers to get type errors when using the new program value, even though it's supported at the bridge level. Update the exported union to include:

export type BillingProgramAndroid =
  | 'unspecified'
  | 'external-content-link'
  | 'external-offer'
  | 'external-payments';

Longer-term, consider aliasing the generated type from src/types.ts instead of hardcoding the union to avoid similar omissions on future openiap updates.

src/types.ts (1)

1-4: Remove duplicate type definitions; import from src/types.ts instead.

DeveloperProvidedBillingDetailsAndroid is defined in three locations (src/types.ts, src/index.ts, and src/specs/RnIap.nitro.ts). Per the coding guidelines, types should be imported from src/types.ts rather than defined ad-hoc in other files. Since src/index.ts already exports everything from src/types.ts (line 65), the local definition at line 412 is redundant and should be removed. Similarly, src/specs/RnIap.nitro.ts should import this type from src/types.ts instead of defining it locally.

🧹 Nitpick comments (4)
docs/docs/api/methods/listeners.md (1)

496-580: Docs for developerProvidedBillingListenerAndroid match implementation

The new section correctly documents platform (Android), version (8.3.0+), Japan-only scope, the need to enable external-payments before initConnection, and the externalTransactionToken payload. Looks accurate and consistent with the code and tests; you might optionally add a link to the alternative billing guide’s External Payments section for deeper context.

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

48-60: Nitro spec additions align with External Payments, but duplicate a generated type

  • Extending BillingProgramAndroid with 'external-payments' and introducing DeveloperBillingLaunchModeAndroid/DeveloperProvidedBillingDetailsAndroid are consistent with the new External Payments flow and the public JS API.
  • However, DeveloperProvidedBillingDetailsAndroid now exists in three places: src/types.ts (generated), this Nitro spec, and src/index.ts. To avoid drift when openiap regenerates src/types.ts, consider aliasing the generated type here instead of re-declaring the interface.

For example (conceptually):

import type {
  DeveloperProvidedBillingDetailsAndroid as GeneratedDeveloperProvidedBillingDetailsAndroid,
} from '../types';

export interface DeveloperProvidedBillingDetailsAndroid
  extends GeneratedDeveloperProvidedBillingDetailsAndroid {}

Also, since this file changed, remember to regenerate Nitro bridge files (yarn specs) so the native bridge picks up the new listener and billing program values.

Also applies to: 331-342, 964-989

src/index.ts (1)

412-419: Avoid re-declaring DeveloperProvidedBillingDetailsAndroid here

DeveloperProvidedBillingDetailsAndroid is already defined in src/types.ts (and also in src/specs/RnIap.nitro.ts). Re-declaring it in src/index.ts introduces a third copy that can silently drift if the generated types add fields in future openiap updates.

Instead, import and re‑export the canonical type from ./types:

import type {DeveloperProvidedBillingDetailsAndroid} from './types';

// (re-export is already covered by `export * from './types';`)

and remove the local export interface DeveloperProvidedBillingDetailsAndroid { ... } block so there’s a single source of truth.

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

1578-1583: Remove unused function mapDeveloperBillingLaunchMode or add a TODO explaining its intended use.

This private function is defined but never called anywhere in the codebase. Either remove it, or if it's reserved for future implementation (e.g., handling developerBillingOption in the purchase flow), add a TODO comment explaining the planned usage to clarify intent.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af4e2c4 and 11d0d6c.

📒 Files selected for processing (9)
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • docs/blog/2025-12-28-release-14.6.4.md
  • docs/docs/api/methods/listeners.md
  • docs/docs/guides/alternative-billing.md
  • openiap-versions.json
  • src/__tests__/index.test.ts
  • src/index.ts
  • src/specs/RnIap.nitro.ts
  • src/types.ts
🧰 Additional context used
📓 Path-based instructions (6)
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
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/types.ts
  • src/specs/RnIap.nitro.ts
  • src/index.ts
  • src/__tests__/index.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/types.ts
  • src/specs/RnIap.nitro.ts
  • src/index.ts
  • src/__tests__/index.test.ts
{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}

📄 CodeRabbit inference engine (CLAUDE.md)

{src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}: Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.
Use Platform.OS checks for platform-specific logic in React Native code.

Files:

  • src/types.ts
  • src/specs/RnIap.nitro.ts
  • src/index.ts
  • src/__tests__/index.test.ts
**/*.nitro.ts

📄 CodeRabbit inference engine (CLAUDE.md)

After modifying any .nitro.ts interface files, regenerate Nitro bridge files (yarn specs).

Files:

  • src/specs/RnIap.nitro.ts
{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
🧠 Learnings (5)
📚 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/api/methods/listeners.md
  • src/types.ts
  • src/__tests__/index.test.ts
📚 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/api/methods/listeners.md
  • src/types.ts
📚 Learning: 2025-09-18T16:45:10.582Z
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.

Applied to files:

  • src/types.ts
  • src/specs/RnIap.nitro.ts
  • src/index.ts
  • openiap-versions.json
  • src/__tests__/index.test.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:

  • src/types.ts
  • src/specs/RnIap.nitro.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.

Applied to files:

  • src/types.ts
🧬 Code graph analysis (5)
src/types.ts (2)
src/index.ts (2)
  • BillingProgramAndroid (2350-2353)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
src/specs/RnIap.nitro.ts (3)
  • BillingProgramAndroid (49-53)
  • DeveloperBillingLaunchModeAndroid (56-59)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/specs/RnIap.nitro.ts (2)
src/index.ts (2)
  • BillingProgramAndroid (2350-2353)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
src/types.ts (3)
  • BillingProgramAndroid (72-72)
  • DeveloperBillingLaunchModeAndroid (112-112)
  • DeveloperProvidedBillingDetailsAndroid (133-140)
src/index.ts (3)
src/specs/RnIap.nitro.ts (2)
  • RnIap (542-1073)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/types.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (133-140)
src/utils/debug.ts (1)
  • RnIapConsole (18-49)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (3)
src/index.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
src/specs/RnIap.nitro.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/types.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (133-140)
src/__tests__/index.test.ts (2)
android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt (1)
  • warn (26-28)
ios/RnIapLog.swift (1)
  • warn (34-34)
🪛 GitHub Actions: CI - Library Build
src/types.ts

[error] 1-1: yarn generate:types failed. Exit code 1 during type generation (OpenIAP types update caused the step to fail). Command: 'yarn generate:types'.

⏰ 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-android
🔇 Additional comments (18)
openiap-versions.json (1)

2-4: Version bumps look consistent with release notes

The updated openiap versions for apple, google, and gql are straightforward and align with the documented 14.6.4 release; no code-level concerns.

docs/blog/2025-12-28-release-14.6.4.md (1)

1-118: Release notes accurately describe 14.6.4 feature surface

The blog post’s description of External Payments, new listener, enableBillingProgramAndroid, and openiap version bumps matches the code and test changes. The usage example is consistent with the public API shape exposed from src/index.ts and the Nitro spec.

docs/docs/guides/alternative-billing.md (1)

318-390: External Payments guide section is clear and consistent

The new External Payments section (8.3.0+, Japan only) clearly explains setup, listener usage, and the developerBillingOption shape, and its comparison table vs User Choice Billing matches the API surface. This will rely on requestPurchase correctly forwarding developerBillingOption to native (see src/index.ts comment), but the guide itself is coherent.

src/__tests__/index.test.ts (1)

1481-1525: Good coverage for new Android External Payments APIs

The new tests correctly cover:

  • Non-Android behavior for developerProvidedBillingListenerAndroid (warn + no-op unsubscribe).
  • Android path: listener registration, payload forwarding of externalTransactionToken, and proper removal.
  • enableBillingProgramAndroid('external-payments') forwarding the new program value to the Nitro layer.

This gives solid regression protection for the new listener and billing program support.

Also applies to: 1541-1547

src/index.ts (1)

374-464: New developerProvidedBillingListenerAndroid matches existing listener patterns

The implementation mirrors the patterns used for userChoiceBillingListenerAndroid and the purchase listeners:

  • Android-only guard with clear warning message.
  • WeakMap to keep track of wrapped Nitro listeners for safe removal.
  • Graceful handling of Nitro-not-installed errors with an inert listener and informative warning.

Functionally this looks correct and matches the behavior covered in the new tests.

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

41-42: LGTM!

New imports correctly aliased for OpenIAP Developer Billing types, following the same aliasing pattern used for other OpenIAP imports in this file.


83-83: LGTM!

Listener collection follows the same pattern as userChoiceBillingListenersAndroid on line 82, maintaining consistency.


174-186: LGTM!

The Developer Provided Billing listener registration follows the same pattern as the other listeners (purchase update, purchase error, user choice billing). The error handling with runCatching and onFailure is consistent with the existing implementation.


193-210: LGTM!

The billing program is correctly enabled before initConnection as required by the API. The config mapping properly passes enableBillingProgramAndroid to the OpenIAP initialization config.


1453-1470: LGTM!

The listener management methods follow the same thread-safe pattern as userChoiceBillingListenersAndroid:

  • synchronized blocks protect concurrent access
  • sendDeveloperProvidedBilling iterates and dispatches safely

Based on coding guidelines, the function ordering is correct (event listener methods section).


1574-1576: LGTM!

The new EXTERNAL_PAYMENTS case correctly maps to OpenIapBillingProgramAndroid.ExternalPayments, completing the billing program mapping.

src/types.ts (7)

68-72: New external-payments value aligns with Nitro spec.

The updated BillingProgramAndroid type now includes 'external-payments', which matches the definition in src/specs/RnIap.nitro.ts (lines 48-52). The documentation correctly notes the 8.3.0 version requirement.


107-140: New External Payments types are well-documented.

The new types DeveloperBillingLaunchModeAndroid, DeveloperBillingOptionParamsAndroid, and DeveloperProvidedBillingDetailsAndroid include clear JSDoc comments with version requirements (8.3.0+). The structure aligns with the Nitro spec definitions.


308-308: LGTM!

The new 'developer-provided-billing-android' event type is correctly added to the IapEvent union type.


324-334: LGTM!

The enableBillingProgramAndroid field is properly documented with available programs and version requirements. The nullable union type (BillingProgramAndroid | null) follows the existing pattern used for alternativeBillingModeAndroid.


975-980: LGTM!

The developerBillingOption field is consistently added to both RequestPurchaseAndroidProps and RequestSubscriptionAndroidProps with identical documentation, enabling the External Payments flow for both purchase types.

Also applies to: 1063-1068


1150-1157: LGTM!

The developerProvidedBillingAndroid subscription event is well-documented, explaining when it fires and what data it contains.


1448-1448: LGTM!

The subscription args map entry correctly uses never since the event doesn't require arguments, consistent with other subscription events.

…illing

- Add 'external-payments' option to Android billing mode selector
- Add developerProvidedBillingListenerAndroid setup for handling
  when user selects developer billing in Japan
- Add handleAndroidExternalPayments handler with developerBillingOption
- Add External Payments Token display section
- Update info card and status text for new mode
- Update documentation comments
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
example/screens/AlternativeBilling.tsx (1)

459-503: Consider using isUserCancelledError() for consistent error handling.

The error handling at line 494 uses direct string comparison (error.code !== 'user-cancelled'). Per coding guidelines, prefer using isUserCancelledError() with normalized ErrorCode when handling purchase errors.

This is consistent with the existing pattern in onPurchaseError (line 146), so if you update one, consider updating both for consistency.

🔎 Proposed fix

Import the helper:

 import {
   useIAP,
   requestPurchase,
   initConnection,
   endConnection,
   presentExternalPurchaseLinkIOS,
   isBillingProgramAvailableAndroid,
   createBillingProgramReportingDetailsAndroid,
   launchExternalLinkAndroid,
   developerProvidedBillingListenerAndroid,
+  isUserCancelledError,
   type Product,
   ...

Then update the error check:

-        if (error.code !== 'user-cancelled') {
+        if (!isUserCancelledError(error)) {
           setPurchaseResult(`❌ Error: ${error.message}`);
           Alert.alert('Error', error.message);
         } else {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11d0d6c and 8e582ff.

📒 Files selected for processing (3)
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
  • example/screens/AlternativeBilling.tsx
  • src/types.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • example/screens/AlternativeBilling.tsx
  • src/types.ts
{example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}

📄 CodeRabbit inference engine (CLAUDE.md)

{example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}}: In useIAP hook usage, do not expect returned data from methods that return Promise; consume state from the hook instead.
Do not call parseErrorStringToJsonObj() in app/user code; errors are already normalized by the library.

Files:

  • example/screens/AlternativeBilling.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/screens/AlternativeBilling.tsx
  • src/types.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
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/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:

  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
🧠 Learnings (6)
📓 Common learnings
Learnt from: hyochan
Repo: hyochan/react-native-iap PR: 3015
File: src/types.ts:137-166
Timestamp: 2025-09-18T16:45:10.582Z
Learning: The types in src/types.ts are auto-generated from openiap-gql1.0.6. Breaking changes in mutation return types (like acknowledgePurchaseAndroid returning Promise<boolean>, finishTransaction returning Promise<void>, etc.) originate from updates to this external GraphQL schema generator, not manual refactoring.
📚 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/AlternativeBilling.tsx
  • src/types.ts
📚 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/AlternativeBilling.tsx
  • 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/AlternativeBilling.tsx
  • src/types.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:

  • src/types.ts
📚 Learning: 2025-10-02T19:35:19.667Z
Learnt from: CR
Repo: hyochan/react-native-iap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T19:35:19.667Z
Learning: Applies to {src/**/*.{ts,tsx},example/**/*.{ts,tsx},example-expo/**/*.{ts,tsx}} : Prefer using isUserCancelledError() and getUserFriendlyErrorMessage() with normalized ErrorCode when handling purchase errors.

Applied to files:

  • src/types.ts
🧬 Code graph analysis (3)
example/screens/AlternativeBilling.tsx (3)
src/index.ts (2)
  • developerProvidedBillingListenerAndroid (421-464)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
src/types.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (143-150)
src/specs/RnIap.nitro.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/types.ts (2)
src/specs/RnIap.nitro.ts (3)
  • BillingProgramAndroid (49-53)
  • DeveloperBillingLaunchModeAndroid (56-59)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/index.ts (2)
  • BillingProgramAndroid (2350-2353)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (3)
src/types.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (143-150)
src/specs/RnIap.nitro.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (335-342)
src/index.ts (1)
  • DeveloperProvidedBillingDetailsAndroid (412-419)
⏰ 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-android
🔇 Additional comments (15)
example/screens/AlternativeBilling.tsx (4)

25-32: LGTM! Type-only imports used correctly.

The imports correctly use type-only imports for DeveloperProvidedBillingDetailsAndroid as per coding guidelines.


164-194: LGTM! Proper listener lifecycle management.

The developerProvidedBillingListenerAndroid setup is well-structured:

  • Platform check for Android-only execution
  • Proper cleanup via subscription.remove() in the return
  • Empty dependency array is appropriate since this listener should only be registered once

791-813: LGTM! External Payments token display section.

Good UX for displaying the external transaction token with appropriate warnings about the 24-hour reporting requirement and clear token functionality.


883-902: LGTM! External Payments mode option added to modal.

The mode selector option for External Payments is well-integrated with appropriate labeling indicating the Japan-specific availability and the side-by-side dialog behavior.

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

41-41: LGTM! Import added for OpenIapDeveloperProvidedBillingListener.

Required import for the new External Payments listener functionality.


82-82: LGTM! Listener list follows established pattern.

The developerProvidedBillingListenersAndroid list matches the structure of userChoiceBillingListenersAndroid, maintaining consistency.


173-185: LGTM! Developer Provided Billing listener wiring.

The listener setup correctly:

  • Follows the same error-handling pattern as other listeners (runCatching with onFailure logging)
  • Maps the OpenIAP details to the Nitro type
  • Dispatches via sendDeveloperProvidedBilling

193-204: LGTM! Billing program enablement via config.

The enableBillingProgramAndroid is correctly mapped from the config and passed to OpenIapInitConnectionConfig. This aligns with the documented "cleaner setup" approach and addresses the previous review feedback about redundant calls.


1447-1464: LGTM! Add/remove/send methods for developer provided billing.

The implementation correctly uses synchronized blocks for thread safety, consistent with the userChoiceBilling listener pattern. The method signatures match the spec interface.


1563-1569: LGTM! EXTERNAL_PAYMENTS mapping added.

The mapBillingProgram function correctly maps the new EXTERNAL_PAYMENTS enum value to OpenIapBillingProgramAndroid.ExternalPayments.

src/types.ts (5)

1-4: Auto-generated file — changes appear consistent with schema updates.

Per learnings, this file is auto-generated from openiap-gql. The new types (DeveloperProvidedBillingDetailsAndroid, DeveloperBillingLaunchModeAndroid, DeveloperBillingOptionParamsAndroid) and updated interfaces align with the External Payments feature additions.


117-150: LGTM! New External Payments types added.

The new types for Developer Billing support are well-documented:

  • DeveloperBillingLaunchModeAndroid - launch mode options
  • DeveloperBillingOptionParamsAndroid - parameters for purchase flow
  • DeveloperProvidedBillingDetailsAndroid - callback details with external transaction token

Version availability (8.3.0+) is clearly noted in the JSDoc comments.


334-340: LGTM! enableBillingProgramAndroid added to InitConnectionConfig.

The optional enableBillingProgramAndroid property allows cleaner initialization without a separate enableBillingProgramAndroid() call, as documented in the PR objectives.


971-986: LGTM! developerBillingOption added to purchase request props.

Both RequestPurchaseAndroidProps and RequestSubscriptionAndroidProps correctly include the optional developerBillingOption parameter for enabling the External Payments flow during purchase.

Also applies to: 1046-1074


1143-1163: LGTM! Subscription interface updated for developer provided billing.

The developerProvidedBillingAndroid subscription event is properly documented, noting the 8.3.0+ availability and the purpose (receiving externalTransactionToken when user selects developer billing).

The canonical type with 'external-payments' is in types.ts (auto-generated
from openiap-gql). Import it instead of redefining locally.
@hyochan hyochan merged commit 7da6c45 into main Dec 28, 2025
10 checks passed
@hyochan hyochan deleted the feat/android-billing-8.3.0 branch December 28, 2025 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖 android Related to android 🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant