Skip to content

Fix incorrect Android replacement mode#93

Closed
nero-angela wants to merge 1 commit into
hyodotdev:mainfrom
nero-angela:dev
Closed

Fix incorrect Android replacement mode#93
nero-angela wants to merge 1 commit into
hyodotdev:mainfrom
nero-angela:dev

Conversation

@nero-angela

@nero-angela nero-angela commented Apr 14, 2026

Copy link
Copy Markdown

fix #92

Summary by CodeRabbit

  • Bug Fixes
    • Corrected enum value mappings for Android in-app purchase replacement modes to ensure accurate handling of purchase replacement operations.

@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Fixed incorrect integer mappings for Android replacement modes in the AndroidReplacementModeValue extension. The chargeFullPrice mode now correctly maps to 4 and deferred maps to 5, aligning with Android's official BillingClient API specifications.

Changes

Cohort / File(s) Summary
Android Replacement Mode Enum Mapping
libraries/flutter_inapp_purchase/lib/enums.dart
Corrected integer values for AndroidReplacementMode.chargeFullPrice (now 4) and AndroidReplacementMode.deferred (now 5) to match Android's official documentation.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Suggested labels

🛠 bugfix, 🤖 android

Poem

🐰 A rabbit hops through enum fields so green,
Finding values that were mixed, now seen!
Four and five swap places in the code,
Android's way, the rightful road! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing incorrect Android replacement mode enum values to match Android's official API.
Linked Issues check ✅ Passed The changes directly address issue #92 by correcting the integer mappings for deferred and chargeFullPrice enum cases to align with Android's ReplacementMode values.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the Android replacement mode enum values as specified in issue #92; no unrelated modifications were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@libraries/flutter_inapp_purchase/lib/enums.dart`:
- Around line 170-173: The enum-to-integer mapping for AndroidReplacementMode
was swapped (AndroidReplacementMode.chargeFullPrice now returns 4 and
AndroidReplacementMode.deferred returns 5); find all dependent tests, examples,
and any serialization/deserialization code that reference the old numeric values
(search for literal usages of 4 and 5 tied to AndroidReplacementMode, usages of
AndroidReplacementMode.chargeFullPrice or AndroidReplacementMode.deferred in
tests/examples) and update them to match the new mapping so chargeFullPrice maps
to 4 and deferred maps to 5, ensuring assertions, fixtures, and sample data are
adjusted accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bc5c1d1-5878-4f3d-a2c0-9138785d5753

📥 Commits

Reviewing files that changed from the base of the PR and between d1c1253 and 280f2e5.

📒 Files selected for processing (1)
  • libraries/flutter_inapp_purchase/lib/enums.dart

Comment on lines 170 to 173
case AndroidReplacementMode.chargeFullPrice:
return 4;
case AndroidReplacementMode.deferred:
return 5;

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.

⚠️ Potential issue | 🟠 Major

Sync dependent tests/examples with this new mapping to prevent drift.

Lines 170-173 switch chargeFullPrice/deferred values, but existing references still encode the previous mapping (chargeFullPrice=5, deferred=4). Please update those usages in the same PR to avoid failing tests and contradictory sample behavior.

Suggested follow-up updates
--- a/libraries/flutter_inapp_purchase/test/enums_unit_test.dart
+++ b/libraries/flutter_inapp_purchase/test/enums_unit_test.dart
@@
-    expect(AndroidReplacementMode.chargeFullPrice.value, 5);
+    expect(AndroidReplacementMode.chargeFullPrice.value, 4);
+    expect(AndroidReplacementMode.deferred.value, 5);
--- a/libraries/flutter_inapp_purchase/example/lib/src/screens/subscription_flow_screen.dart
+++ b/libraries/flutter_inapp_purchase/example/lib/src/screens/subscription_flow_screen.dart
@@
-    'Deferred': 4,
-    'Immediate and Charge Full Price': 5,
+    'Immediate and Charge Full Price': 4,
+    'Deferred': 5,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libraries/flutter_inapp_purchase/lib/enums.dart` around lines 170 - 173, The
enum-to-integer mapping for AndroidReplacementMode was swapped
(AndroidReplacementMode.chargeFullPrice now returns 4 and
AndroidReplacementMode.deferred returns 5); find all dependent tests, examples,
and any serialization/deserialization code that reference the old numeric values
(search for literal usages of 4 and 5 tied to AndroidReplacementMode, usages of
AndroidReplacementMode.chargeFullPrice or AndroidReplacementMode.deferred in
tests/examples) and update them to match the new mapping so chargeFullPrice maps
to 4 and deferred maps to 5, ensuring assertions, fixtures, and sample data are
adjusted accordingly.

@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 modifies the AndroidReplacementModeValue extension in libraries/flutter_inapp_purchase/lib/enums.dart to swap the integer return values for AndroidReplacementMode.chargeFullPrice and AndroidReplacementMode.deferred. I have no feedback to provide as there were no review comments to evaluate.

@hyochan

hyochan commented Apr 14, 2026

Copy link
Copy Markdown
Member

Thanks for the report and PR! I looked into this carefully and the proposed values are not correct for this code path. Details below.

Why the PR is wrong

The Flutter AndroidReplacementMode.value int is passed as-is through the channel and ends up being fed to the legacy SubscriptionUpdateParams.Builder.setSubscriptionReplacementMode(int) in packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt:1006:

val replacementMode = androidArgs.replacementMode ?: 5 // Default to CHARGE_FULL_PRICE
@Suppress("DEPRECATION")
updateParamsBuilder.setSubscriptionReplacementMode(replacementMode)

That legacy setter consumes values from BillingFlowParams.SubscriptionUpdateParams.ReplacementMode, not the newer SubscriptionProductReplacementParams.ReplacementMode.

I verified this against the shipped Billing Library 8.3.0 bytecode:

Constant Legacy SubscriptionUpdateParams.ReplacementMode New SubscriptionProductReplacementParams.ReplacementMode (8.1.0+)
UNKNOWN_REPLACEMENT_MODE 0 0
WITH_TIME_PRORATION 1 1
CHARGE_PRORATED_PRICE 2 2
WITHOUT_PRORATION 3 3
CHARGE_FULL_PRICE 5 4
DEFERRED 6 5
KEEP_EXISTING 6

What the PR changes (before → after)

Enum Current This PR Legacy (required here)
chargeFullPrice 5 ✅ 4 ❌ 5
deferred 4 ❌ 5 ❌ 6

So this PR breaks the previously-correct chargeFullPrice = 5 and still does not land on the correct deferred = 6.

It also breaks the existing unit test at libraries/flutter_inapp_purchase/test/enums_unit_test.dart:19:

expect(AndroidReplacementMode.chargeFullPrice.value, 5);

Correct fix

Only deferred needs to change — from 4 to 6:

case AndroidReplacementMode.deferred:
  return 6;
case AndroidReplacementMode.chargeFullPrice:
  return 5;

Please also extend enums_unit_test.dart to cover deferred (and ideally the remaining values) so this can't regress.

Side note: the Kotlin SubscriptionReplacementModeAndroidExt.toReplacementModeConstant() uses the new API values (4/5/6) because it feeds SubscriptionProductReplacementParams.Builder.setReplacementMode() via reflection (see OpenIapModule.kt:1629) — that is a separate code path keyed by the enum name (charge-full-price, etc.), not by this int. The two paths use different numeric conventions, which is what made this confusing.

Could you update the PR accordingly? Happy to help review once pushed.

@hyochan

hyochan commented Apr 14, 2026

Copy link
Copy Markdown
Member

Superseded by #96, which lands the corrected legacy values (deferred = 6, chargeFullPrice = 5) along with full test coverage and a Kotlin-side change that sources the values from native Billing Library constants so this can't drift again. Thanks for surfacing the bug — see the issue thread for the detailed analysis.

@hyochan

hyochan commented Apr 14, 2026

Copy link
Copy Markdown
Member

Superseded by #96 (merged).

@hyochan hyochan closed this Apr 14, 2026
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.

Incorrect android replacement mode parameter value on Flutter.

2 participants