Skip to content

fix(android): prevent product data overwrite in fetchProducts with type 'all'#3044

Closed
CacaoRick wants to merge 1 commit intohyochan:mainfrom
CacaoRick:fix/android-fetchproducts-overwrite-issue
Closed

fix(android): prevent product data overwrite in fetchProducts with type 'all'#3044
CacaoRick wants to merge 1 commit intohyochan:mainfrom
CacaoRick:fix/android-fetchproducts-overwrite-issue

Conversation

@CacaoRick
Copy link
Contributor

@CacaoRick CacaoRick commented Oct 2, 2025

Problem

When using fetchProducts with type: 'all' to query mixed product types (e.g., both InApp and Subscription SKUs), InApp products lose their price information.

Reproduction Steps

  1. Call fetchProducts(['inapp_sku', 'subs_sku'], 'all')
  2. Check the returned InApp product
  3. Observe that price field is null and displayPrice, currency fields are ""

Root Cause

The original implementation handled ProductQueryType.All by:

  1. First querying InApp type → finds inapp_sku with complete price information
  2. Then querying Subs type → queries with the same SKU, Google Play returns empty/incomplete data
  3. Using map[sku] = product to unconditionally overwrite → valid InApp product data gets overwritten
// Original implementation
fetched.forEach { collected[it.id] = it }  // ❌ Unconditional overwrite

Solution

OpenIAP's fetchProducts method already correctly implements ProductQueryType.All logic:
Uses processedIds to track already-processed product IDs
Prevents duplicate additions of the same product
Correctly handles mixed-type queries

Reference: OpenIAP fetchProducts implementation

This PR removes the redundant manual implementation and directly uses OpenIAP's functionality.

Testing

Tested in my app with the following scenarios:

  • Mixed InApp + Subs query (type: 'all') → Both product types now return complete information with correct pricing
  • InApp only query (type: 'inapp') → Works correctly
  • Subs only query (type: 'subs') → Works correctly

Before this fix, InApp products would return { displayPrice:"", currency:"", price:null } when queried together with subscription products using type 'all'. After this fix, all products maintain their complete data.

Summary by CodeRabbit

  • New Features

    • None
  • Refactor

    • Streamlined in-app purchase product fetching into a single, consistent flow across product types.
  • Performance

    • Reduced overhead by eliminating redundant aggregation, improving product load times.
  • Stability

    • More consistent and reliable product retrieval for all product types.
  • Observability

    • Simplified and consolidated logging for clearer diagnostics during product fetch operations.

…pe 'all'

Previously, when fetching products with type 'all', the code manually
queried InApp and Subs separately, then merged results using a map.
This caused valid InApp products to be overwritten by empty/incomplete
data from the Subs query when the same SKU was queried for both types.

OpenIAP's fetchProducts already handles ProductQueryType.All correctly
with duplicate prevention using processedIds. This commit removes the
redundant manual implementation and directly uses OpenIAP's logic.

Fixes issue where InApp products lose price information when mixed
with subscription products in a single 'all' type query.
@coderabbitai
Copy link

coderabbitai bot commented Oct 2, 2025

Walkthrough

Simplifies fetchProducts in HybridRnIap.kt to a single-path flow using the computed queryType. Removes the previous All-case branching and aggregation, consolidates logging to a single payload and result, and retains updating productTypeBySku and returning converted NitroProduct lists.

Changes

Cohort / File(s) Summary
IAP fetchProducts simplification
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
Replaced multi-branch All-case aggregation with a single fetch based on computed queryType; consolidated logging; maintained productTypeBySku updates and NitroProduct conversion; no public API changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant JS as RN Caller
  participant IAP as HybridRnIap.fetchProducts
  participant BC as BillingClient (query)
  participant Map as productTypeBySku

  JS->>IAP: fetchProducts(skus, queryType)
  IAP->>IAP: compute effective queryType
  IAP->>BC: queryProducts(queryType, skus)
  BC-->>IAP: products[]
  IAP->>Map: update per SKU type
  IAP-->>JS: NitroProduct[]
  note over IAP: Single-path flow replaces prior All-case branching
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

🤖 android, 🛠 bugfix

Suggested reviewers

  • hyochan

Poem

Hoppity hop through code I go,
Trimming branches, streamlining flow.
One path now, no needless splits—
Fewer hops, the logic fits.
Carrots logged, results in sight,
Products fetched in one clean bite. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary purpose of the change by indicating the Android-specific fix for preventing product data overwrite in the fetchProducts method when using the 'all' type, which directly reflects the core issue and its resolution described in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1991e74 and 8accbd9.

📒 Files selected for processing (1)
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
android/src/main/java/com/margelo/nitro/iap/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

android/src/main/java/com/margelo/nitro/iap/**/*.kt: Follow the native class function ordering in Kotlin: properties/init; public cross-platform methods; platform-specific public methods; event listener methods; private helpers.
On Android, rely on automatic service reconnection; only check that the BillingClient exists, not connection state.

Files:

  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
🧬 Code graph analysis (1)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)
src/types.ts (1)
  • ProductRequest (271-274)
⏰ 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 (1)
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt (1)

197-205: LGTM! Simplification correctly fixes the overwrite bug.

The refactored implementation properly addresses the root cause by delegating to OpenIAP's fetchProducts, which uses processedIds to prevent duplicate processing when querying mixed product types (type: 'all'). This eliminates the manual aggregation that was overwriting InApp product data with incomplete information from the subsequent Subs query.

The logging consolidation maintains observability while side effects (productTypeBySku updates, line 207) are preserved.


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 @CacaoRick, 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 resolves a critical bug in the Android fetchProducts method that caused InApp product pricing data to be lost when querying for 'all' product types (both InApp and Subscriptions). The change simplifies the product fetching logic by removing a problematic custom implementation and instead leveraging the OpenIAP library's robust handling for mixed product queries, thereby ensuring accurate and complete product information is consistently returned.

Highlights

  • Problem Addressed: When using fetchProducts with type: 'all' to query mixed product types (e.g., InApp and Subscription SKUs), InApp products were losing their price information, appearing as null or empty.
  • Root Cause Identified: The issue stemmed from a redundant manual implementation for ProductQueryType.All where InApp products were queried first, then Subscription products. The subsequent query for Subscription products with the same SKUs would cause an unconditional overwrite of already fetched InApp product data, leading to incomplete information.
  • Solution Implemented: The custom, bug-prone logic for handling ProductQueryType.All was removed. The fetchProducts method now directly utilizes the OpenIAP library's built-in functionality, which correctly handles mixed-type queries and prevents data overwrites.
  • Impact: This fix ensures that all product types, including InApp products, retain their complete and correct pricing information when queried together using type: 'all'.
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 effectively addresses the issue of product data being overwritten when fetching mixed product types. By removing the manual implementation for handling ProductQueryType.All and instead relying on the underlying OpenIapModule's fetchProducts method, the code is now much simpler and more robust. The change correctly fixes the bug where in-app product information was lost during mixed-type queries. This simplification is a great improvement, and the changes look solid.

@CacaoRick
Copy link
Contributor Author

CacaoRick commented Oct 2, 2025

Wait, I noticed that there is a bug in the cache of OpenIapModule's fetchProducts method for ProductQueryType.All. It may need to be handled differently.

The first fetchProducts will get the correct result, but after the second time, the type of subs also becomes in-app.

fetchProducts payload: {"skus":["subs_sku","inapp_sku"],"type":"all"}
initConnection payload: null
initConnection result: true
fetchProducts.native payload: {"skus":["subs_sku","inapp_sku"],"type":"all"}
fetchProducts.native result: [{"id":"subs_sku","type":"subs"},{"id":"inapp_sku","type":"in-app"}]
fetchProducts result: [{"id":"subs_sku","type":"subs"},{"id":"inapp_sku","type":"in-app"}]

fetchProducts payload: {"skus":["subs_sku","inapp_sku"],"type":"all"}
initConnection payload: null
initConnection result: true
fetchProducts.native payload: {"skus":["subs_sku","inapp_sku"],"type":"all"}
fetchProducts.native result: [{"id":"subs_sku","type":"in-app"},{"id":"inapp_sku","type":"in-app"}]
fetchProducts result: [{"id":"subs_sku","type":"in-app"},{"id":"inapp_sku","type":"in-app"}]

final line {"id":"subs_sku","type":"in-app"} type is wrong

@CacaoRick
Copy link
Contributor Author

CacaoRick commented Oct 2, 2025

Closing This PR

After further investigation, I've discovered that this issue stems from bugs in the underlying OpenIAP library that cannot be reliably worked around at the react-native-iap level.

Root Cause Analysis

Issue 1: OpenIAP ProductQueryType.All Cache Bug

OpenIAP's fetchProducts with ProductQueryType.All appears to have a caching issue that causes incorrect results on subsequent queries.

Issue 2: Type Mismatch Returns Incorrect Product Types

When querying with ProductQueryType.Subs or ProductQueryType.InApp, OpenIAP returns all provided SKUs marked with the queried type, even if they don't actually match that type:

// Query with Subs type but include an InApp SKU
fetchProducts(["subs_sku", "inapp_sku"], ProductQueryType.Subs)

// OpenIAP returns BOTH marked as "subs"
Result: [
  {"id": "subs_sku", "type": "subs"},     // ✅ Correct
  {"id": "inapp_sku", "type": "subs"}     // ❌ Wrong! Actually an in-app product
]

This PR will remain closed until

  • OpenIAP fixes the ProductQueryType.All caching bug
  • OpenIAP properly handles type validation (only returning products that match the requested type)

Once these issues are resolved in OpenIAP, this PR approach (or a similar fix) can be reconsidered.

Workaround for Users
Until OpenIAP is fixed, users experiencing this issue should query product types separately and ensure each SKU is queried with its correct type:

❌ Don't use:

await fetchProducts(['inapp_sku', 'subs_sku'], 'all')

✅ Instead, query with correct types:

const inappProducts = await fetchProducts(['inapp_sku'], 'inapp')
const subsProducts = await fetchProducts(['subs_sku'], 'subs')
const allProducts = [...inappProducts, ...subsProducts]

Related Issues
This issue should be reported to the OpenIAP repository:

  • OpenIAP ProductQueryType.All caching issue
  • Type mismatch returning incorrect product types

Closing this PR. Can be reopened once OpenIAP resolves the underlying issues.

@CacaoRick CacaoRick closed this Oct 2, 2025
@hyochan
Copy link
Owner

hyochan commented Oct 2, 2025

@CacaoRick Hey good catch! I'll manage to fix this here hyodotdev/openiap-apple#16 and release to 1.2.6. Hope this works.

@hyochan
Copy link
Owner

hyochan commented Oct 2, 2025

@CacaoRick Released 14.4.10

@CacaoRick
Copy link
Contributor Author

Thank you for your maintenance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants