Skip to content

fix(google): correct SubscriptionProductReplacementParams Replacement…#72

Merged
hyochan merged 4 commits into
mainfrom
fix/replacement-mode-constants-71
Jan 20, 2026
Merged

fix(google): correct SubscriptionProductReplacementParams Replacement…#72
hyochan merged 4 commits into
mainfrom
fix/replacement-mode-constants-71

Conversation

@hyochan

@hyochan hyochan commented Jan 20, 2026

Copy link
Copy Markdown
Member

Summary

Fix incorrect replacementModeConstant mapping in applySubscriptionProductReplacementParams(). The code was using values from the legacy SubscriptionUpdateParams.ReplacementMode API instead of the new SubscriptionProductReplacementParams.ReplacementMode API (Billing 8.1.0+).

Affected modes:

Mode Before (Wrong) After (Correct)
CHARGE_FULL_PRICE 5 4
DEFERRED 6 5
KEEP_EXISTING 7 6

Changes

References

Closes #71

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Corrected Android subscription replacement mode mappings and resolved value inconsistencies.
  • Documentation

    • Replaced enumerated lists with platform-specific tables for upgrade/downgrade modes.
    • Added cross-API notes clarifying differences between legacy and new APIs and removed inline numeric identifiers.
    • Added a dated release note entry describing the Android mapping fix.
  • Tests

    • Added unit tests validating replacement-mode mappings and JSON serialization/deserialization.

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

…Mode constants

Fix incorrect replacementModeConstant mapping in applySubscriptionProductReplacementParams().
The code was using values from the legacy SubscriptionUpdateParams.ReplacementMode API
instead of the new SubscriptionProductReplacementParams.ReplacementMode API (Billing 8.1.0+).

Affected modes:
- CHARGE_FULL_PRICE: 5 → 4
- DEFERRED: 6 → 5
- KEEP_EXISTING: 7 → 6

Also:
- Add SubscriptionReplacementModeTest.kt with comprehensive test coverage
- Update subscription-upgrade-downgrade docs with correct API values table
- Add v1.3.27 release notes

Closes #71

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@hyochan hyochan added the 🐛 bug Something isn't working label Jan 20, 2026
@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 resolves a critical bug in the Google Play Billing integration where the applySubscriptionProductReplacementParams() function was using incorrect integer constants for subscription replacement modes, leading to potential issues with subscription management on Android Billing Library 8.1.0+ versions. The fix ensures that the system now correctly maps CHARGE_FULL_PRICE, DEFERRED, and KEEP_EXISTING to their proper values. The changes are validated with new, comprehensive unit tests, and the public documentation has been updated to provide clear guidance on the correct API values for both legacy and modern billing implementations.

Highlights

  • Google Play Billing API Fix: Corrected the replacementModeConstant mapping for CHARGE_FULL_PRICE, DEFERRED, and KEEP_EXISTING within the applySubscriptionProductReplacementParams() function to align with the Google Play Billing Library 8.1.0+ SubscriptionProductReplacementParams.ReplacementMode API.
  • Comprehensive Test Coverage: Added a new test file, SubscriptionReplacementModeTest.kt, which provides extensive unit tests to verify the correct integer mappings for all SubscriptionReplacementModeAndroid enum values against the new Billing Library 8.1.0+ constants, and highlights differences from the legacy API.
  • Documentation Update: Updated the subscription-upgrade-downgrade documentation to clearly present the integer values for subscription replacement modes, differentiating between the legacy SubscriptionUpdateParams.ReplacementMode and the new SubscriptionProductReplacementParams.ReplacementMode APIs in a tabular format.
  • Release Notes: Included release notes for v1.3.27, detailing the fix for the incorrect SubscriptionProductReplacementParams.ReplacementMode mapping.

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

@coderabbitai

coderabbitai Bot commented Jan 20, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 1 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 5c99380 and f6bbfad.

📒 Files selected for processing (1)
  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx

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

Corrects Android subscription replacement-mode constant mapping in OpenIapModule, adds a mapping extension and tests verifying legacy vs. Billing Library 8.1.0+ constants and JSON (de)serialization, and updates docs and release notes to show tabular replacement-mode mappings and explanatory notes.

Changes

Cohort / File(s) Summary
Documentation
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx, packages/docs/src/pages/docs/updates/notes.tsx
Replaced enumerated lists with per-platform tables showing Mode, Legacy API, 8.1.0+ API, and descriptions; added cross-API notes and a new release-note entry documenting Android ReplacementMode mapping changes.
Android mapping code
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt, packages/google/openiap/src/main/java/dev/hyo/openiap/SubscriptionReplacementModeAndroidExt.kt
Centralized conversion by adding SubscriptionReplacementModeAndroid.toReplacementModeConstant() and updated OpenIapModule to use it instead of an inline integer map (aligns values with Billing Library 8.1.0+ ReplacementMode constants).
Tests
packages/google/openiap/src/test/java/dev/hyo/openiap/SubscriptionReplacementModeTest.kt
New comprehensive tests asserting exact numeric mappings for new and legacy APIs, JSON serialization/deserialization behavior, and coverage of all enum values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #50: Changes related to Android subscription replacement-mode handling for Google Play Billing v8.1.0; overlaps in mappings and tests.
  • #25: Modifies replacement-mode handling and documentation; touches same docs and mapping surface.
  • #52: Updates SubscriptionReplacementModeAndroid mappings and tests for 8.1.0+ compatibility; directly related changes.

Suggested labels

📖 documentation

Poem

🐰 I hopped through code and fixed the map tonight,

Numbers lined up, now billing's tight,
Tests did a dance, the docs showed two,
Old and new side-by-side in view,
🌟 A rabbit's patch — neatly done, delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly indicates this is a fix for incorrect SubscriptionProductReplacementParams mapping, directly addressing the main change in the PR.
Linked Issues check ✅ Passed The PR successfully addresses all key requirements: corrects the replacementModeConstant mapping to use the correct API (SubscriptionProductReplacementParams), implements correct numeric values (CHARGE_FULL_PRICE=4, DEFERRED=5, KEEP_EXISTING=6), adds comprehensive test coverage, and updates documentation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the issue: core mapping fix in OpenIapModule, new extension function for the mapping logic, comprehensive tests, documentation updates for the API differences, and release notes. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 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 correctly fixes a critical bug in the Google Play Billing integration, where incorrect constants were used for subscription replacement modes. The changes are well-executed, including necessary updates to documentation and the addition of a comprehensive test suite to cover the new logic. My review includes one suggestion to refactor the test implementation to avoid duplicating production logic, which will improve long-term maintainability.

@hyochan hyochan added ⌚️ regression Things that worked previously 🛠 bugfix All kinds of bug fixes 🤖 android Related to android and removed 🐛 bug Something isn't working labels Jan 20, 2026

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx`:
- Around line 1085-1138: Wrap the existing table element in a container div with
CSS to prevent overflow (e.g., class or inline style setting overflow: 'hidden'
and width: '100%'), and add fixed layout and word-wrapping to the table cells by
applying tableLayout: 'fixed' to the <table> and overflowWrap/wordBreak (e.g.,
overflowWrap: 'break-word' and wordBreak: 'break-word') to <th> and <td> styles;
update the table rendering in subscription-upgrade-downgrade.tsx (the <table>
and its <th>/<td>) to include these styles so the replacement-mode table cannot
overflow on narrow viewports.

In
`@packages/google/openiap/src/test/java/dev/hyo/openiap/SubscriptionReplacementModeTest.kt`:
- Around line 143-165: Replace the Kotlin built-in assert(...) calls in the
tests with JUnit assertions so the checks always run; specifically change the
two assertions that verify legacyValue != newValue to use
org.junit.jupiter.api.Assertions.assertNotEquals(legacyValue, newValue,
"message") for the tests that call
getSubscriptionProductReplacementModeConstant(SubscriptionReplacementModeAndroid.ChargeFullPrice)
and
getSubscriptionProductReplacementModeConstant(SubscriptionReplacementModeAndroid.Deferred),
preserving the existing failure messages.
🧹 Nitpick comments (2)
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (1)

1338-1341: Clarify which API the numeric DEFERRED value applies to in examples.

Line 1340 lists both legacy and 8.1.0+ constants, but the code examples below still use replacementMode: 6. Consider a short note that requestPurchase() uses legacy SubscriptionUpdateParams values, while per‑product replacements use subscriptionProductReplacementParams (DEFERRED = 5).

Suggested clarification
                 <ol>
                   <li>
                     <strong>Use DEFERRED replacement mode</strong> (Legacy API: 6, 8.1.0+ API: 5)
                   </li>
                   <li>No immediate charge to the user</li>
                   <li>User keeps premium access until current period ends</li>
                   <li>Basic subscription starts at next billing date</li>
                 </ol>
+                <p style={{ fontSize: '0.875rem', color: 'var(--text-secondary)' }}>
+                  Note: <code>replacementMode</code> in <code>requestPurchase()</code> uses legacy
+                  <code>SubscriptionUpdateParams</code> values (DEFERRED = 6). For Billing 8.1.0+
+                  per‑product replacement, use <code>subscriptionProductReplacementParams</code> with
+                  DEFERRED = 5.
+                </p>
packages/docs/src/pages/docs/updates/notes.tsx (1)

28-69: Keep the new note table from overflowing small screens.

The table can exceed the container width on narrow devices; wrap it in an overflow-hidden container and add fixed layout/word breaking.

Proposed fix
-          <table style={{ width: '100%', borderCollapse: 'collapse', marginTop: '0.5rem', marginBottom: '0.5rem' }}>
+          <div className="overflow-hidden">
+            <table
+              style={{
+                width: '100%',
+                borderCollapse: 'collapse',
+                marginTop: '0.5rem',
+                marginBottom: '0.5rem',
+                tableLayout: 'fixed',
+                wordBreak: 'break-word',
+              }}
+            >
               ...
-          </table>
+            </table>
+          </div>

As per coding guidelines, ensure tables never overflow their containers.

Comment thread packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx Outdated
hyochan and others added 2 commits January 21, 2026 06:12
- Wrap replacement mode table in scrollable container for narrow screens
- Replace Kotlin assert() with JUnit assertNotEquals() for reliable test execution

Co-Authored-By: Claude <noreply@anthropic.com>
- Create SubscriptionReplacementModeAndroidExt.kt with toReplacementModeConstant()
- Update OpenIapModule.kt to use the shared extension function
- Simplify test file by removing duplicated mapping logic

This ensures tests verify actual production logic instead of a reimplementation.

Co-Authored-By: Claude <noreply@anthropic.com>

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx`:
- Line 1350: Several downgrade examples hardcode the DEFERRED replacement mode
value as 6 even though the note at "Use DEFERRED replacement mode" states
DEFERRED = 6 for Legacy API and DEFERRED = 5 for 8.1.0+; update each example
that currently uses the literal 6 to either (a) use 5 when the example targets
8.1.0+ or (b) keep 6 but add an inline annotation (e.g., "// Legacy API" or "//
8.1.0+") next to the hardcoded value so readers know which API version the
example is for; search for occurrences of the literal "6" in the
subscription-upgrade-downgrade examples that mention DEFERRED and add the
appropriate value or comment consistently.
♻️ Duplicate comments (2)
packages/google/openiap/src/test/java/dev/hyo/openiap/SubscriptionReplacementModeTest.kt (1)

32-68: Avoid duplicating the mapping logic in tests.
Consider extracting the enum→constant mapping into a shared helper used by production and tests so the suite doesn’t re‑implement the logic and drift over time.

packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (1)

1085-1148: Prevent replacement‑mode table overflow on narrow screens.
The wrapper currently allows horizontal scrolling and the table lacks fixed layout/word breaking, so long content can still exceed the container. Consider switching to an overflow‑hidden wrapper and fixed layout with word breaking so the table always fits within the doc column.

Suggested tweak
-                  <div style={{ overflowX: 'auto' }}>
+                  <div className="overflow-hidden">
                     <table
                       style={{
                         width: '100%',
                         borderCollapse: 'collapse',
                         marginTop: '0.5rem',
                         marginBottom: '0.5rem',
                         fontSize: '0.875rem',
+                        tableLayout: 'fixed',
+                        wordBreak: 'break-word',
                       }}
                     >

As per coding guidelines, tables should not overflow their containers.

🧹 Nitpick comments (1)
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (1)

1368-1370: Add a reference or version scope for the “empty purchase list” behavior.
Line 1368–1370 is a strong behavioral claim; consider citing the Billing docs or noting the BillingClient version(s) you validated so readers can trust when it applies.

…sion

Add "(Legacy API value)" annotation to all code examples that use
DEFERRED = 6 to clarify this is for the legacy SubscriptionUpdateParams API,
not the new SubscriptionProductReplacementParams API (8.1.0+) where DEFERRED = 5.

Co-Authored-By: Claude <noreply@anthropic.com>
@hyochan hyochan merged commit 2540524 into main Jan 20, 2026
7 checks passed
@hyochan hyochan deleted the fix/replacement-mode-constants-71 branch January 20, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖 android Related to android 🛠 bugfix All kinds of bug fixes ⌚️ regression Things that worked previously

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix incorrect replacementModeConstant mapping in OpenIapModule

1 participant