feat(google): update android billing client to 8.2.0#52
Conversation
- 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
|
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 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. 📒 Files selected for processing (3)
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this comment.
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:
- Adding a note that these string names map to numeric values in the API
- Updating all code examples to use enum/string values instead of numbers
This would prevent confusion about whether to use
'WITH_TIME_PRORATION'or1in actual code.packages/apple/Sources/Models/Types.swift (1)
1295-1311: Acknowledge SwiftLint warning on type name length.SwiftLint flags
SubscriptionProductReplacementParamsAndroidfor 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 afterrunCatching.The
runCatching { ... }function always returns aResultobject (never null), so the safe call operator?.beforegetOrNull()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 deprecatedreplacementModeAndroid.The Play Store-specific code path at line 1025 still uses the deprecated
replacementModeAndroid: Intparameter instead of the newsubscriptionProductReplacementParamsAPI 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
enableBillingProgramonly exists in the Play flavor. However, the static analysis tool flagged thatNoSuchMethodExceptionis 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.productIdcompares 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
⛔ Files ignored due to path filters (4)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/Types.swiftis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**packages/gql/src/generated/types.tsis 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 withIOSsuffix (e.g.,clearTransactionIOS,getAppTransactionIOS)
Android-specific functions must end withAndroidsuffix (e.g.,acknowledgePurchaseAndroid,consumePurchaseAndroid)
Use action prefixes for function naming:getfor retrieval,requestfor async operations,clearfor removal,is/hasfor boolean checks,show/presentfor UI display,begin/finish/endfor process controlPrefer interface for defining object shapes in TypeScript
Files:
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/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; useoverflow-hidden,break-words, andwhitespace-nowrapas 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 withvoidoperator when used where a void return is expected (e.g., event handlers)
Files:
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/external-purchase.tsx
packages/docs/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*: Before committing, runnpx prettier --write,npm run lint,bun run tscornpm run typecheck, andnpm run buildto 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.tsxpackages/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.tsxpackages/docs/src/pages/docs/features/external-purchase.tsx
packages/google/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
DO NOT add
Androidsuffix to function names in the Android-only package, even for Android-specific APIs (e.g., useacknowledgePurchase()notacknowledgePurchaseAndroid())
Files:
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
packages/gql/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Run
bun run generateto 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 haveIOSsuffix (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.swiftinSources/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.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/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.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/src/type-android.graphqlpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/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.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/helpers/SharedHelpers.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/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.ktpackages/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.ktpackages/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
initConnectionis 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 (viaenableUserChoiceBilling,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_EXISTINGbeing 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
subscriptionProductReplacementParamsacross 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
replacementModeAndroidwith clear migration guidance to the newsubscriptionProductReplacementParamsfield 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
SubscriptionReplacementModeAndroidenum correctly maps to Google Play Billing Library'sReplacementModeconstants, including the newKEEP_EXISTINGmode added in 8.1.0. TheSubscriptionProductReplacementParamsAndroidinput 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 deprecatedExternalOfferReportingDetailsAndroidandExternalOfferAvailabilityResultAndroidtypes 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
subscriptionProductReplacementParamsfield 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
subscriptionProductReplacementParamstonullfor 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
ReplacementModeobject 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
SubscriptionProductReplacementParamsAndroidAPI 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 generatefrom 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
subscriptionProductReplacementParamsfield is correctly:
- Added as nullable to
AndroidPurchaseArgs- Set to
nullfor 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:
- Check availability with
isBillingProgramAvailable- Launch external link with
launchExternalLink- Create reporting details with
createBillingProgramReportingDetailsClear 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:
isBillingProgramAvailablereturnsisAvailable = false(graceful degradation)createBillingProgramReportingDetailsthrowsFeatureNotSupported(prevents misuse)launchExternalLinkreturnsfalse(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:
checkAlternativeBillingAvailability→isBillingProgramAvailableshowAlternativeBillingInformationDialog→launchExternalLinkcreateAlternativeBillingReportingToken→createBillingProgramReportingDetailsAlso 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
Androidsuffix in function names per coding guidelinesThis 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 oflaunchExternalLink.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
enabledBillingProgramswith per-program reflection calls and error handling follows the established pattern. ThecontinueforUnspecifiedis appropriate defensive coding.
644-655: [rewritten comment]
[classification tag]
420-425: Hardcoded billing program constants are correct.The constants
1forEXTERNAL_CONTENT_LINKand3forEXTERNAL_OFFERmatch the actualBillingClient.BillingProgramvalues 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 inOpenIapModule.ktand the interfaces defined inpackages/gql/src/generated/types.ts.
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
Show resolved
Hide resolved
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
Show resolved
Hide resolved
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
Show resolved
Hide resolved
- 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)
371b24a to
51e420f
Compare
…3.12 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…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
There was a problem hiding this comment.
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 driftThe implementations of
isBillingProgramAvailable,createBillingProgramReportingDetails, andlaunchExternalLinkcorrectly:
- Guard on
billingClientreadiness and log failures.- Use reflection for 8.2.0+ types/listeners with graceful fallbacks when methods are missing (returning
falseor throwing a specificOpenIapError).- 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 explicitThe
applySubscriptionProductReplacementParamshelper cleanly isolates the 8.1.0+ per‑product replacement logic and is invoked only for subscription products, with sensible matching onoldProductId/ single-SKU upgrades. That’s a good shape.Two optional improvements:
- You currently catch
NoSuchMethodException/ClassNotFoundException/ genericException, log, and then silently proceed. Given this feeds into upgrade flows, consider returning aBooleanfrom 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 legacySubscriptionUpdateParamspath.- The integer mapping from
SubscriptionReplacementModeAndroidto 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
📒 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
Androidsuffix to function names in the Android-only package, even for Android-specific APIs (e.g., useacknowledgePurchase()notacknowledgePurchaseAndroid())
Files:
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/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 onselectedBillingProgramensures proper reconnection when switching program types.
621-706: Well-structured Billing Programs flow with proper null safety.The null check for
activityat 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
FilterChiprow for selecting betweenExternalOfferandExternalContentLinkprovides a clear, intuitive UI whenBILLING_PROGRAMSmode is selected. The visual separation withSpacerand 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 consistentThe new
@Deprecatedannotations and KDoc on the three alternative-billing helpers, plus the explicit@Suppress("DEPRECATION")usage in theALTERNATIVE_ONLYflow, 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
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt
Show resolved
Hide resolved
- 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)
Summary by CodeRabbit
New Features
Deprecations
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.