Skip to content

feat(google): update android billing client to 8.2.0#52

Merged
hyochan merged 8 commits intomainfrom
feat/android-billing-8.2.0
Dec 11, 2025
Merged

feat(google): update android billing client to 8.2.0#52
hyochan merged 8 commits intomainfrom
feat/android-billing-8.2.0

Conversation

@hyochan
Copy link
Member

@hyochan hyochan commented Dec 11, 2025

Summary by CodeRabbit

  • New Features

    • Android Billing Programs API (8.2.0+) for external content/offers, link launching, availability checks and reporting
    • Per-product subscription replacement params (8.1.0+) with new replacement modes including KEEP_EXISTING
    • Example apps/UI updated to enable/select billing programs and launch external links
  • Deprecations

    • Legacy alternative-billing APIs marked deprecated; migrate to Billing Programs API
  • Documentation

    • Expanded migration guides, flows and multi-language examples (TS/Kotlin/Dart/Swift)

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

- Add SubscriptionProductReplacementParams API (8.1.0+)
- Add Billing Programs APIs: enableBillingProgram, isBillingProgramAvailable,
  createBillingProgramReportingDetails, launchExternalLink (8.2.0+)
- Deprecate old External Offer APIs in favor of Billing Programs
- Fix ReplacementMode constants in Example app
- Update documentation with new APIs and examples
- Generate types for all platforms (TypeScript, Kotlin, Swift, Dart)
… Example

- Add Billing Programs API section to external-purchase.tsx docs
- Add deprecated warnings for legacy alternative billing APIs
- Add API migration guide table
- Update Example AlternativeBillingScreen with new 8.2.0+ Billing Programs mode
- Add enableBillingProgram() method to OpenIapStore
- Support ExternalOffer and ExternalContentLink program types
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 23 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 d372e4d and 1a774d4.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (11 hunks)
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (13 hunks)

Walkthrough

Adds Android Billing Programs (8.2.0+) and per-product subscription replacement params (8.1.0+) across types, protocol/store APIs, Play/Horizon implementations, examples, GraphQL, and docs; deprecates legacy alternative-billing APIs and threads new parameters through purchase request surfaces.

Changes

Cohort / File(s) Summary
Type Definitions
packages/apple/Sources/Models/Types.swift, packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
Added Android enums (BillingProgramAndroid, ExternalLinkLaunchModeAndroid, ExternalLinkTypeAndroid, SubscriptionReplacementModeAndroid) and data types (BillingProgramAvailabilityResultAndroid, BillingProgramReportingDetailsAndroid, LaunchExternalLinkParamsAndroid, SubscriptionProductReplacementParamsAndroid, deprecated ExternalOffer* types). Extended RequestSubscriptionAndroidProps with subscriptionProductReplacementParams and deprecated replacementModeAndroid.
GraphQL Schema
packages/gql/src/type-android.graphql
Added inputs/enums/types for billing programs and per-product replacement (SubscriptionProductReplacementParamsAndroid, SubscriptionReplacementModeAndroid, BillingProgramAndroid, ExternalLinkLaunchModeAndroid, ExternalLinkTypeAndroid, LaunchExternalLinkParamsAndroid, BillingProgramReportingDetailsAndroid, BillingProgramAvailabilityResultAndroid). Marked legacy ExternalOffer* types deprecated.
Protocol & Store APIs
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt, packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
Added APIs: isBillingProgramAvailable, createBillingProgramReportingDetails, launchExternalLink; deprecated checkAlternativeBillingAvailability, showAlternativeBillingInformationDialog, createAlternativeBillingReportingToken.
Horizon Implementation
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt, packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt
Added stub Horizon implementations for Billing Programs APIs (returning unavailable / throwing feature-not-supported where applicable) and deprecated legacy methods; propagated subscriptionProductReplacementParams in shared helpers.
Play Billing Implementation
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt, packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt, packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt
Implemented Billing Programs integration (reflection for Billing Library 8.2.0+): enableBillingProgram, isBillingProgramAvailable, launchExternalLink, createBillingProgramReportingDetails; added applySubscriptionProductReplacementParams helper and threaded per-product replacement params; guarded extraction of preorderDetails/rentalDetails with runCatching.
Example App: Alternative Billing
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
Refactored to support three billing modes (ALTERNATIVE_ONLY, USER_CHOICE, BILLING_PROGRAMS), added Billing Programs UI and flow using isBillingProgramAvailable / launchExternalLink / reporting details.
Example App: Subscription Flow
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
Switched from global replacementMode to per-product SubscriptionProductReplacementParamsAndroid; updated replacement-mode constants (including KEEP_EXISTING, DEFERRED, UNKNOWN_REPLACEMENT_MODE).
Docs: External Purchase
packages/docs/src/pages/docs/features/external-purchase.tsx
Reworked docs to introduce Billing Programs API (8.2.0+), added migration notes for legacy APIs, updated platform support table, and added multi-language examples and flows.
Docs: Subscription Upgrade/Downgrade
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx
Added “Per-Product Replacement Params” (8.1.0+) guidance and examples; removed numeric prefixes from replacement-mode labels; documented KEEP_EXISTING availability.
Changelog / Release Notes
CHANGELOG.md, packages/docs/src/pages/docs/updates/notes.tsx
Added release notes documenting new Billing Programs and per-product replacement features, migration guidance, and deprecated legacy APIs.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant App as Client App
    participant OpenIap as OpenIap Module
    participant Billing as Play BillingClient
    participant Backend as Backend Server

    User->>App: Request external purchase / enable program
    App->>OpenIap: enableBillingProgram(program)
    OpenIap->>Billing: enableBillingProgram() (reflective call, 8.2.0+)
    App->>OpenIap: isBillingProgramAvailable(program)
    OpenIap->>App: BillingProgramAvailabilityResultAndroid{isAvailable}
    alt available
        App->>OpenIap: launchExternalLink(LaunchExternalLinkParamsAndroid)
        OpenIap->>Billing: launch external link (open browser/app)
        Billing->>User: External purchase UI
        User->>Backend: Complete external payment
        Backend->>App: Payment success / token
        App->>OpenIap: createBillingProgramReportingDetails(program)
        OpenIap->>Backend: report token / return reporting details
        OpenIap->>App: BillingProgramReportingDetailsAndroid
    else unavailable
        OpenIap->>App: isAvailable = false
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt — reflection usage, Billing Client 8.2.0+ integration, fallbacks and error handling
    • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt — new enums/data classes and JSON (de)serialization correctness
    • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt — runCatching changes and compatibility comments
    • Example screens — UI state transitions and new flows for billing-programs mode
    • GraphQL schema changes — ensure generated artifacts and clients are updated

Possibly related PRs

Poem

🐰 Hop, hop — a rabbit's cheer!

New programs sprout, per-product bright,
I hop tokens through day and night.
Legacy paths, softly retired,
I nibble changes, code inspired. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.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 title accurately summarizes the main change: updating the Android Billing Client library to version 8.2.0, which aligns with the extensive additions of new Billing Programs API support across multiple files.

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.

@hyochan hyochan added 🎯 feature New feature 🚽 migration Activities due to changes in framework 🤖 android Related to android labels Dec 11, 2025
Copy link
Contributor

@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: 4

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

978-998: Consider clarifying the relationship between labels and numeric values.

The updated labels (removing numeric prefixes) improve readability. However, code examples throughout the document still use numeric values (e.g., replacementMode: 1). Consider either:

  1. Adding a note that these string names map to numeric values in the API
  2. Updating all code examples to use enum/string values instead of numbers

This would prevent confusion about whether to use 'WITH_TIME_PRORATION' or 1 in actual code.

packages/apple/Sources/Models/Types.swift (1)

1295-1311: Acknowledge SwiftLint warning on type name length.

SwiftLint flags SubscriptionProductReplacementParamsAndroid for exceeding 40 characters. Since this is an auto-generated file and the name must match the GraphQL schema for cross-platform consistency, this warning can be suppressed or acknowledged. Consider adding a SwiftLint disable comment in the generator template if this pattern continues.

packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1)

65-79: Unnecessary safe call operator after runCatching.

The runCatching { ... } function always returns a Result object (never null), so the safe call operator ?. before getOrNull() is redundant. While this doesn't cause a bug (it still works), it's misleading and suggests potential nullability that doesn't exist.

-        // Extract preorder details if available (Billing Library 8.1.0+)
-        val preorder = runCatching { preorderDetails }?.getOrNull()?.let { details ->
+        // Extract preorder details if available (Billing Library 8.1.0+)
+        val preorder = runCatching { preorderDetails }.getOrNull()?.let { details ->
             PreorderDetailsAndroid(
                 preorderPresaleEndTimeMillis = details.preorderPresaleEndTimeMillis.toString(),
                 preorderReleaseTimeMillis = details.preorderReleaseTimeMillis.toString()
             )
         }

-        // Extract rental details if available (Billing Library 7.0+)
-        val rental = runCatching { rentalDetails }?.getOrNull()?.let { details ->
+        // Extract rental details if available (Billing Library 7.0+)
+        val rental = runCatching { rentalDetails }.getOrNull()?.let { details ->
             RentalDetailsAndroid(
                 rentalPeriod = details.rentalPeriod,
                 rentalExpirationPeriod = runCatching { details.rentalExpirationPeriod }.getOrNull()
             )
         }
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (1)

1002-1032: Inconsistent API usage: Play Store path uses deprecated replacementModeAndroid.

The Play Store-specific code path at line 1025 still uses the deprecated replacementModeAndroid: Int parameter instead of the new subscriptionProductReplacementParams API that's demonstrated in the Horizon path (lines 793-796). For consistency and to showcase the recommended 8.1.0+ API, consider updating this path as well.

                                                             android = RequestSubscriptionAndroidProps(
                                                                 isOfferPersonalized = null,
                                                                 obfuscatedAccountIdAndroid = null,
                                                                 obfuscatedProfileIdAndroid = null,
                                                                 purchaseTokenAndroid = purchaseToken,
-                                                                replacementModeAndroid = replacementMode,
+                                                                // New 8.1.0+ API: per-product replacement params
+                                                                subscriptionProductReplacementParams = SubscriptionProductReplacementParamsAndroid(
+                                                                    oldProductId = PREMIUM_SUBSCRIPTION_PRODUCT_ID,
+                                                                    replacementMode = SubscriptionReplacementModeAndroid.ChargeFullPrice
+                                                                ),
                                                                 skus = listOf(PREMIUM_SUBSCRIPTION_PRODUCT_ID),
                                                                 subscriptionOffers = offerInputs
                                                             )
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)

472-484: Reflection usage is appropriate; consider including exception details in log.

The reflection approach is correct since enableBillingProgram only exists in the Play flavor. However, the static analysis tool flagged that NoSuchMethodException is swallowed without preserving exception details.

Consider including the exception message for debugging:

         } catch (e: NoSuchMethodException) {
-            OpenIapLog.w("enableBillingProgram not available (Horizon flavor or older library)", "OpenIapStore")
+            OpenIapLog.w("enableBillingProgram not available (Horizon flavor or older library): ${e.message}", "OpenIapStore")
         } catch (e: Exception) {
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)

857-865: Clarify the condition for applying per-product replacement params.

The condition replacementParams.oldProductId == productDetails.productId compares the old product ID to the current (new) product ID. This would only match if upgrading to the same product, which is atypical. Was this intended to check if the current product is the target of the upgrade rather than the source?

For single-product scenarios (androidArgs.skus.size == 1), the fallthrough makes sense. But for multi-product requests, the first condition seems inverted.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4f1ba6 and ca306c7.

⛔ Files ignored due to path filters (4)
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (14)
  • packages/apple/Sources/Models/Types.swift (10 hunks)
  • packages/docs/src/pages/docs/features/external-purchase.tsx (3 hunks)
  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (3 hunks)
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (11 hunks)
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (4 hunks)
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (4 hunks)
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt (4 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt (1 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (12 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (2 hunks)
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (11 hunks)
  • packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt (4 hunks)
  • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1 hunks)
  • packages/gql/src/type-android.graphql (2 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: iOS-specific functions must end with IOS suffix (e.g., clearTransactionIOS, getAppTransactionIOS)
Android-specific functions must end with Android suffix (e.g., acknowledgePurchaseAndroid, consumePurchaseAndroid)
Use action prefixes for function naming: get for retrieval, request for async operations, clear for removal, is/has for boolean checks, show/present for UI display, begin/finish/end for process control

Prefer interface for defining object shapes in TypeScript

Files:

  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx
  • packages/docs/src/pages/docs/features/external-purchase.tsx
packages/docs/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/docs/**/*.{ts,tsx}: Use kebab-case for search modal IDs (e.g., id: 'request-products')
Modal state should be defined once at the app root level using Preact Signals (signal from '@preact/signals-react'), not instantiated multiple times
ALL components must fit within parent boundaries and never overflow outside parent containers; use overflow-hidden, break-words, and whitespace-nowrap as needed
Delete unused components, functions, and imports immediately; do not keep commented-out code or unused variables
ANY function that returns a Promise must be wrapped with void operator when used where a void return is expected (e.g., event handlers)

Files:

  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx
  • packages/docs/src/pages/docs/features/external-purchase.tsx
packages/docs/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

packages/docs/**/*: Before committing, run npx prettier --write, npm run lint, bun run tsc or npm run typecheck, and npm run build to verify formatting, linting, types, and build success
Use conventional commit format with lowercase type prefix and lowercase description (e.g., feat: add user authentication)

Files:

  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx
  • packages/docs/src/pages/docs/features/external-purchase.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{ts,tsx,js,jsx}: Use camelCase for variable and function names
Use PascalCase for class and component names
Always use async/await for handling promises instead of .then() chains
Add JSDoc comments for public functions and exported APIs
Use const by default, let if reassignment is needed, avoid var

Files:

  • packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx
  • packages/docs/src/pages/docs/features/external-purchase.tsx
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
packages/gql/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run generate to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the GraphQL schema

Files:

  • packages/gql/src/type-android.graphql
packages/apple/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

packages/apple/**/*.swift: iOS-specific functions MUST have IOS suffix (e.g., presentCodeRedemptionSheetIOS(), showManageSubscriptionsIOS())
Use Pascal case for acronyms at the beginning or middle of names (e.g., IapManager, IapPurchase), but ALL CAPS only when appearing as a suffix (e.g., ProductIAP, OpenIAP)

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/Types.swift

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT edit Types.swift in Sources/Models/ as it is auto-generated from the OpenIAP GraphQL schema; regenerate using ./scripts/generate-types.sh

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

OpenIAP official types in Sources/Models/ must match types defined at openiap.dev/docs/types

Files:

  • packages/apple/Sources/Models/Types.swift
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)

Applied to files:

  • packages/docs/src/pages/docs/features/external-purchase.tsx
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (8)
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)
  • code (599-604)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
  • code (1763-1785)
  • code (2378-2397)
packages/gql/src/generated/Types.kt (2)
  • code (1835-1857)
  • code (2450-2469)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/gql/src/generated/types.ts (1)
  • BillingProgramAvailabilityResultAndroid (78-83)
packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1)
packages/gql/src/generated/types.ts (1)
  • PreorderDetailsAndroid (438-449)
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (1)
packages/gql/src/generated/types.ts (1)
  • SubscriptionProductReplacementParamsAndroid (1050-1055)
packages/docs/src/pages/docs/features/external-purchase.tsx (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)
  • code (599-604)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
  • code (1763-1785)
  • code (2378-2397)
packages/gql/src/generated/Types.kt (2)
  • code (1835-1857)
  • code (2450-2469)
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (1)
packages/gql/src/generated/types.ts (5)
  • InitConnectionConfig (283-289)
  • LaunchExternalLinkParamsAndroid (296-305)
  • RequestPurchaseProps (904-918)
  • RequestPurchasePropsByPlatforms (920-929)
  • RequestPurchaseAndroidProps (880-889)
packages/apple/Sources/Models/Types.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (8)
  • billingProgram (833-858)
  • billingProgram (865-891)
  • billingProgram (2554-2589)
  • isAvailable (1070-1089)
  • externalTransactionToken (1096-1115)
  • externalTransactionToken (2212-2237)
  • skus (2591-2608)
  • oldProductId (3009-3032)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt (1)
  • skus (53-64)
packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt (1)
  • skus (95-106)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/gql/src/generated/types.ts (2)
  • BillingProgramAvailabilityResultAndroid (78-83)
  • BillingProgramReportingDetailsAndroid (90-98)
🪛 detekt (1.23.8)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt

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

(detekt.exceptions.SwallowedException)

🪛 SwiftLint (0.57.0)
packages/apple/Sources/Models/Types.swift

[Warning] 1298-1298: Type name 'SubscriptionProductReplacementParamsAndroid' should be between 3 and 40 characters long

(type_name)

⏰ 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: Test Android
  • GitHub Check: Test iOS
🔇 Additional comments (30)
packages/docs/src/pages/docs/features/external-purchase.tsx (6)

60-62: LGTM: Clear version guidance.

The table effectively communicates the legacy and recommended approaches for Android billing.


1000-1230: LGTM: Comprehensive Billing Programs API documentation.

The new section provides clear guidance on the recommended 8.2.0+ API with:

  • Program type definitions
  • Multi-language code examples
  • Step-by-step flow documentation

All Android-specific function names correctly follow the naming convention with the "Android" suffix.


1232-1258: LGTM: Clear migration mapping.

The side-by-side comparison table makes it easy for developers to understand which new APIs replace the legacy ones.


1260-1273: LGTM: Clear version requirements.

The requirements section appropriately labels the legacy and recommended approaches, guiding developers toward the 8.2.0+ Billing Programs API.


1462-1531: LGTM: Well-structured implementation flow guidance.

The side-by-side presentation of the recommended Billing Programs API flow (8.2.0+) and the legacy flow (6.2+) helps developers understand both approaches and choose the appropriate one. The explicit "Step 0" for enabling the billing program before initConnection is a helpful detail.


988-998: Incorrect deprecation reference. These three APIs (checkAlternativeBillingAvailability, showAlternativeBillingInformationDialog, createAlternativeBillingReportingToken) were deprecated in Google Play Billing Library 7.1.1, not 8.2.0+. The recommended replacement is the User Choice Billing API (via enableUserChoiceBilling, UserChoiceBillingListener, UserChoiceDetails), not the Billing Programs API. The warning should be updated to reference the correct version and replacement APIs.

Likely an incorrect or invalid review comment.

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

1008-1039: LGTM: Clear documentation of the new per-product API.

The Accordion effectively introduces the Billing Library 8.1.0+ per-product replacement params feature, explaining the fields and use cases. The note about KEEP_EXISTING being exclusive to this API is helpful.


1305-1397: LGTM: Comprehensive multi-language examples for the new API.

The code examples clearly demonstrate how to use subscriptionProductReplacementParams across TypeScript, Kotlin, and Dart. The examples consistently show:

  • The new per-product replacement structure
  • Proper type usage for each platform
  • Comments indicating KEEP_EXISTING availability (8.1.0+)
packages/gql/src/type-android.graphql (3)

295-307: LGTM - Well-structured deprecation and new API field.

The deprecation annotation on replacementModeAndroid with clear migration guidance to the new subscriptionProductReplacementParams field follows good API evolution practices. The version note (8.1.0+) is helpful for consumers.


309-360: LGTM - Comprehensive replacement mode enum and input type.

The SubscriptionReplacementModeAndroid enum correctly maps to Google Play Billing Library's ReplacementMode constants, including the new KEEP_EXISTING mode added in 8.1.0. The SubscriptionProductReplacementParamsAndroid input type provides a clean structure for per-product replacement configuration.


440-590: LGTM - Billing Programs API surface for 8.2.0+.

The new types for external content links and external offers (BillingProgramAndroid, ExternalLinkLaunchModeAndroid, ExternalLinkTypeAndroid, etc.) are well-documented with version availability notes. The deprecated ExternalOfferReportingDetailsAndroid and ExternalOfferAvailabilityResultAndroid types appropriately reference the new replacement APIs.

packages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.kt (2)

53-64: LGTM - AndroidPurchaseArgs correctly extended.

The new subscriptionProductReplacementParams field is properly added to the data class, maintaining consistency with the Play Store variant shown in the relevant code snippets.


69-109: LGTM - Proper null handling for purchase vs subscription paths.

The extension function correctly sets subscriptionProductReplacementParams to null for one-time purchases (line 82) while propagating the actual value for subscription requests (line 103). This matches the semantic intent where replacement params only apply to subscription changes.

packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (2)

46-47: LGTM - Updated imports and ReplacementMode constants.

The new imports and updated ReplacementMode object constants align with Google Play Billing Library 8.x API values.

Also applies to: 62-71


777-803: Good usage of new per-product replacement params API.

The Horizon/Play unified code path correctly demonstrates the new SubscriptionProductReplacementParamsAndroid API introduced in Billing Library 8.1.0+.

packages/apple/Sources/Models/Types.swift (1)

1-7: File is properly auto-generated from the GraphQL schema.

This file is auto-generated and should not be edited directly. The file header correctly instructs to run npm run generate from the repository root after updating the GraphQL schema. The regeneration process is properly configured and the file has been recently regenerated (source and target files match at 1996 lines).

packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt (1)

103-103: LGTM! Clean addition of per-product subscription replacement params.

The new subscriptionProductReplacementParams field is correctly:

  • Added as nullable to AndroidPurchaseArgs
  • Set to null for in-app purchases (Line 121)
  • Propagated from input for subscriptions (Line 142)

This aligns with the 8.1.0+ per-product replacement params feature.

Also applies to: 121-121, 142-142

packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (1)

621-697: Well-structured Billing Programs demo flow.

The implementation correctly demonstrates the 8.2.0+ API flow:

  1. Check availability with isBillingProgramAvailable
  2. Launch external link with launchExternalLink
  3. Create reporting details with createBillingProgramReportingDetails

Clear UI feedback and proper error handling. Good example code for developers.

packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)

432-464: Clean addition of Billing Programs APIs.

The new methods (isBillingProgramAvailable, createBillingProgramReportingDetails, launchExternalLink) follow the established delegation pattern and include good KDoc documentation explaining the 8.2.0+ migration path.

packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2)

1017-1035: Billing Programs stubs correctly indicate Horizon doesn't support these APIs.

The implementation is appropriate:

  • isBillingProgramAvailable returns isAvailable = false (graceful degradation)
  • createBillingProgramReportingDetails throws FeatureNotSupported (prevents misuse)
  • launchExternalLink returns false (graceful degradation)

This allows callers to check availability before attempting operations that would fail.


904-904: Deprecation annotations provide clear migration guidance.

The deprecation messages correctly point users to the new 8.2.0+ APIs:

  • checkAlternativeBillingAvailabilityisBillingProgramAvailable
  • showAlternativeBillingInformationDialoglaunchExternalLink
  • createAlternativeBillingReportingTokencreateBillingProgramReportingDetails

Also applies to: 935-935, 973-973

packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt (2)

44-49: Deprecation annotations provide clear migration paths.

The legacy alternative billing methods are properly deprecated with messages guiding developers to the new 8.2.0+ APIs.


54-81: Well-designed Billing Programs protocol extension.

The new interface methods:

  • Follow suspend function pattern for async operations
  • Include comprehensive KDoc explaining purpose and migration from legacy APIs
  • Correctly avoid Android suffix in function names per coding guidelines

This provides a clean contract for both Play and Horizon implementations.

packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (6)

110-112: LGTM on the billing programs tracking set.

Good approach to track enabled billing programs for deferred application during client initialization.


548-642: Well-structured implementation of launchExternalLink.

Good use of reflection with proper error handling and fallback behavior. The parameter building and listener setup follow the established pattern correctly.


1447-1509: Clean implementation of per-product replacement params helper.

The reflection-based approach with graceful degradation for older billing library versions is well done. Error handling covers NoSuchMethodException, ClassNotFoundException, and general exceptions appropriately.


1376-1396: Billing programs initialization logic is correct.

The iteration over enabledBillingPrograms with per-program reflection calls and error handling follows the established pattern. The continue for Unspecified is appropriate defensive coding.


644-655: [rewritten comment]
[classification tag]


420-425: Hardcoded billing program constants are correct.

The constants 1 for EXTERNAL_CONTENT_LINK and 3 for EXTERNAL_OFFER match the actual BillingClient.BillingProgram values in Play Billing Library 8.2.0. No changes needed.

packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)

1-4: Auto-generated file - no manual edits required.

Based on learnings, this file is generated from the GraphQL schema via ./scripts/generate-types.sh. The new types (BillingProgramAndroid, ExternalLinkLaunchModeAndroid, ExternalLinkTypeAndroid, SubscriptionReplacementModeAndroid, LaunchExternalLinkParamsAndroid, SubscriptionProductReplacementParamsAndroid, etc.) are consistent with the implementations in OpenIapModule.kt and the interfaces defined in packages/gql/src/generated/types.ts.

- Add error logging and status messages to empty catch blocks in AlternativeBillingScreen.kt
- Fix exception propagation in OpenIapModule.kt Proxy handler using resumeWithException()
- Add null-safe activity handling to prevent potential NPE

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@hyochan hyochan force-pushed the feat/android-billing-8.2.0 branch from 371b24a to 51e420f Compare December 11, 2025 04:03
…3.12

- Document Billing Programs API (8.2.0+) with new methods
- Add new types (BillingProgramAndroid, LaunchExternalLinkParamsAndroid, etc.)
- Document Google Play Billing 8.1.0 support
- Include code example and deprecated APIs section
Copy link
Contributor

@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: 2

🧹 Nitpick comments (2)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (2)

408-547: Reflection-based 8.2.0 Billing Programs APIs look sound; consider guarding against constant drift

The implementations of isBillingProgramAvailable, createBillingProgramReportingDetails, and launchExternalLink correctly:

  • Guard on billingClient readiness and log failures.
  • Use reflection for 8.2.0+ types/listeners with graceful fallbacks when methods are missing (returning false or throwing a specific OpenIapError).
  • Resume or resumeWithException on the continuation in all callback/error paths, avoiding the proxy-exception issue from the earlier review.

Two small follow-ups to consider:

  • The hard‑coded integer values for program / launch mode / link type / replacement modes (e.g., 1, 3, etc.) will be brittle if Google ever changes constants. If feasible with your min billing-lib version, consider sourcing these via the official constants (directly or via reflection) instead of literals, and please double-check they match the 8.2.0 values.
  • These coroutines rely entirely on the Play callback being invoked; if that ever fails, they would hang. A future enhancement could be a timeout or at least a debug log when the coroutine is cancelled to aid diagnosis.

Also applies to: 557-643


859-867: Per-product subscription replacement params are well-encapsulated; consider making failures more explicit

The applySubscriptionProductReplacementParams helper cleanly isolates the 8.1.0+ per‑product replacement logic and is invoked only for subscription products, with sensible matching on oldProductId / single-SKU upgrades. That’s a good shape.

Two optional improvements:

  • You currently catch NoSuchMethodException / ClassNotFoundException / generic Exception, log, and then silently proceed. Given this feeds into upgrade flows, consider returning a Boolean from this helper (or logging at a higher level) so callers know when per-product replacement didn’t apply and can reason about behavior alongside the legacy SubscriptionUpdateParams path.
  • The integer mapping from SubscriptionReplacementModeAndroid to the underlying billing constants is hand-coded. To avoid drift with future billing-library changes, it may be safer to obtain these values via the official constants (directly or via reflection), and to re-check that the current literals match the 8.1/8.2 definitions.

Also applies to: 1448-1510

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca306c7 and 51e420f.

📒 Files selected for processing (2)
  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (11 hunks)
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (12 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
🧬 Code graph analysis (2)
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (1)
packages/gql/src/generated/types.ts (5)
  • InitConnectionConfig (283-289)
  • LaunchExternalLinkParamsAndroid (296-305)
  • RequestPurchaseProps (904-918)
  • RequestPurchasePropsByPlatforms (920-929)
  • RequestPurchaseAndroidProps (880-889)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/gql/src/generated/types.ts (2)
  • BillingProgramAvailabilityResultAndroid (78-83)
  • BillingProgramReportingDetailsAndroid (90-98)
🪛 detekt (1.23.8)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt

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

(detekt.exceptions.SwallowedException)


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

(detekt.exceptions.SwallowedException)


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

(detekt.exceptions.SwallowedException)

🔇 Additional comments (7)
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (6)

45-50: Clean encapsulation of billing modes.

The private enum cleanly organizes the three billing approaches with helpful version comments. This makes the mode-switching logic throughout the file more maintainable.


147-152: Correct initialization flow for Billing Programs API.

Enabling the billing program before initConnection() with a null config aligns with the 8.2.0+ API requirements. The key on selectedBillingProgram ensures proper reconnection when switching program types.


621-706: Well-structured Billing Programs flow with proper null safety.

The null check for activity at lines 641-648 properly addresses the previous review concern. The 4-step flow (availability check → launch external link → payment → reporting details) correctly demonstrates the 8.2.0+ API usage with comprehensive error handling.


781-829: User Choice flow correctly delegates to Google's selection dialog.

The implementation properly creates purchase props and calls requestPurchase(), letting Google handle the billing choice dialog. Error handling now includes logging (addressing the past review concern). The comment at lines 804-805 helpfully explains the callback paths.


356-380: Good UX for program type selection.

The conditional FilterChip row for selecting between ExternalOffer and ExternalContentLink provides a clear, intuitive UI when BILLING_PROGRAMS mode is selected. The visual separation with Spacer and section title keeps the form organized.


418-461: Comprehensive in-UI documentation for each billing mode.

The mode-specific descriptions clearly explain the flow, limitations, and deprecation status of each approach. The "⚠️ Deprecated in 8.2.0+" warning for legacy APIs helps guide developers toward the recommended Billing Programs API.

packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)

270-308: Legacy alternative-billing deprecations and usage look consistent

The new @Deprecated annotations and KDoc on the three alternative-billing helpers, plus the explicit @Suppress("DEPRECATION") usage in the ALTERNATIVE_ONLY flow, give a clear migration path to the billing-program APIs while preserving backward compatibility. No changes needed here.

Also applies to: 310-357, 359-406, 658-733

- Add null check for activity in ALTERNATIVE_ONLY mode to prevent NPE
- Remove init block to defer BillingClient construction for enableBillingProgram
- v1.3.0: ios/android to apple/google rename, verifyPurchaseWithProvider, IapStore enum
- v1.3.1: One-time purchase discount offers, Billing 8.1.0 (PreorderDetails, isSuspended)
@hyochan hyochan merged commit 3ae957a into main Dec 11, 2025
5 checks passed
@hyochan hyochan deleted the feat/android-billing-8.2.0 branch December 11, 2025 04:46
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 🚽 migration Activities due to changes in framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant