feat(google): add External Payments (8.3.0+) support for 🇯🇵#57
Conversation
- Add DeveloperProvidedBillingListener for External Payments callbacks - Add setDeveloperProvidedBillingListener to OpenIapProtocol and OpenIapStore - Update AlternativeBillingScreen example with EXTERNAL_PAYMENTS mode - Throw UnsupportedOperationException for unsupported Horizon methods
📝 WalkthroughWalkthroughAdds Android External Payments (Google Play Billing 8.3.0+) support: new billing program and launch-mode enums, developer-provided billing event/listener/types, propagation of a developerBillingOption through purchase/subscription requests, Play/Horizon wiring, docs, and example updates (Japan-only external payments). Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant App
participant OpenIapModule
participant GooglePlay as GooglePlayBilling
participant DevListener as DeveloperListener
participant Backend
User->>App: Tap "Buy (External Payments)"
App->>OpenIapModule: launchBillingFlow(developerBillingOption)
OpenIapModule->>GooglePlay: start billing flow (includes DeveloperBillingOption)
alt User selects developer billing
GooglePlay->>OpenIapModule: onUserSelectedDeveloperBilling(token)
OpenIapModule->>App: emit IapEvent.DeveloperProvidedBillingAndroid(details)
OpenIapModule->>DevListener: onDeveloperProvidedBilling(details)
DevListener->>Backend: report externalTransactionToken (within 24h)
Backend->>GooglePlay: report token usage
DevListener->>App: confirm success
else User selects Google Play payment
GooglePlay->>OpenIapModule: regular purchase callback
OpenIapModule->>App: deliver purchase result
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (4)**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
packages/docs/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
packages/docs/**/*📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (GEMINI.md)
Files:
🧠 Learnings (5)📚 Learning: 2025-12-06T20:15:59.233ZApplied to files:
📚 Learning: 2025-12-06T20:15:59.233ZApplied to files:
📚 Learning: 2025-12-06T20:15:59.233ZApplied to files:
📚 Learning: 2025-12-06T20:15:59.233ZApplied to files:
📚 Learning: 2025-12-06T20:15:59.233ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (1)
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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)
366-383: IncludeskuForStatusinrequestPurchase
requestPurchasecurrently derivesskuForStatusonly from the deprecated.androidprops:val skuForStatus = when (val request = props.request) { is RequestPurchaseProps.Request.Purchase -> request.value.android?.skus?.firstOrNull() is RequestPurchaseProps.Request.Subscription -> request.value.android?.skus?.firstOrNull() }Now that newer flows (including the External Payments example) use the recommended
google = RequestPurchaseAndroidProps(...)field,skuForStatuswill benullfor those calls. That means:
pendingRequestProductIdis never set for those purchases.- The
purchasingloading state isn’t toggled.- Any errors during the flow won’t be associated with the correct product ID (or with any product), which degrades the example UI behavior.
Consider preferring
androidfor backward compatibility, e.g.:Suggested update to derive `skuForStatus`
- val skuForStatus = when (val request = props.request) { - is RequestPurchaseProps.Request.Purchase -> request.value.android?.skus?.firstOrNull() - is RequestPurchaseProps.Request.Subscription -> request.value.android?.skus?.firstOrNull() - } + val skuForStatus = when (val request = props.request) { + is RequestPurchaseProps.Request.Purchase -> { + val platforms = request.value + platforms.google?.skus?.firstOrNull() + ?: platforms.android?.skus?.firstOrNull() + } + is RequestPurchaseProps.Request.Subscription -> { + val platforms = request.value + platforms.google?.skus?.firstOrNull() + ?: platforms.android?.skus?.firstOrNull() + } + }This keeps existing behavior for callers still using
.androidwhile fixing status tracking for the new
🧹 Nitpick comments (5)
packages/docs/src/pages/docs/events.tsx (1)
38-39: Verify TypeScript API name for Developer Provided Billing listenerThe new docs introduce
DeveloperProvidedBillingAndroidinIapEventand adeveloperProvidedBillingListener(...)helper in the TS examples, while the existing User Choice event exposesuserChoiceBillingListenerAndroid(...).Given the guidelines that Android-specific TS functions should carry an
Androidsuffix and the existing User Choice API already follows that pattern, please double-check:
- What the actual exported function name is (
developerProvidedBillingListenervsdeveloperProvidedBillingListenerAndroid).- Whether this listener is Android-only (it appears to be) or intentionally cross-platform.
If it is Android-only and exported as
developerProvidedBillingListenerAndroid, the examples here should be updated to use that name for consistency and to avoid confusing consumers. If the API is intentionally named without the suffix, it would be good to bring the rest of the docs and naming guidelines into alignment.Also applies to: 784-895
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
107-109: Remove unused Horizon listener sets or wire them when support is addedOn the Horizon module you’ve:
- Added
userChoiceBillingListeners/developerProvidedBillingListenersfields, but- All related methods (
setUserChoiceBillingListener,setDeveloperProvidedBillingListener,add*/remove*UserChoiceBillingListener,add*/remove*DeveloperProvidedBillingListener) immediately throwUnsupportedOperationException.Given External Payments and these listener-based APIs are not supported on Meta Horizon, the explicit exceptions are fine and match the PR objective. However, the new
mutableSetOffields are currently never used and will trigger “unused” warnings without adding behavior.You can either:
- Remove the unused sets for now (simplest, less noise), or
- Keep them but add a brief comment that they are placeholders for a future Horizon implementation so readers don’t assume they’re wired up.
The functional behavior (throwing for unsupported features) looks correct; this is just a small cleanup / clarity point.
Also applies to: 1020-1045
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (3)
108-112: Consider thread-safe collections for listener sets.The listener collections (
purchaseUpdateListeners,purchaseErrorListeners,userChoiceBillingListeners,developerProvidedBillingListeners) usemutableSetOfwhich is not thread-safe. If listeners are added/removed from different threads while callbacks are being dispatched, this could causeConcurrentModificationException.🔎 Consider using thread-safe collections
import java.util.Collections private val purchaseUpdateListeners = Collections.synchronizedSet(mutableSetOf<OpenIapPurchaseUpdateListener>()) private val purchaseErrorListeners = Collections.synchronizedSet(mutableSetOf<OpenIapPurchaseErrorListener>()) private val userChoiceBillingListeners = Collections.synchronizedSet(mutableSetOf<OpenIapUserChoiceBillingListener>()) private val developerProvidedBillingListeners = Collections.synchronizedSet(mutableSetOf<OpenIapDeveloperProvidedBillingListener>())Alternatively, use
CopyOnWriteArraySetif reads are more frequent than writes.
1694-1700: Include exception details in warning logs for better diagnostics.The
NoSuchMethodExceptionandClassNotFoundExceptioncatch blocks log warnings but don't include the exception details, making debugging harder when these occur. The finalExceptioncatch properly includes the exception.🔎 Proposed fix to include exception details
} catch (e: NoSuchMethodException) { - OpenIapLog.w("DeveloperBillingOption not found. Requires Billing Library 8.3.0+", TAG) + OpenIapLog.w("DeveloperBillingOption not found. Requires Billing Library 8.3.0+: ${e.message}", TAG) } catch (e: ClassNotFoundException) { - OpenIapLog.w("DeveloperBillingOptionParams class not found. Requires Billing Library 8.3.0+", TAG) + OpenIapLog.w("DeveloperBillingOptionParams class not found. Requires Billing Library 8.3.0+: ${e.message}", TAG) } catch (e: Exception) { OpenIapLog.e("Failed to apply DeveloperBillingOption: ${e.message}", e, TAG) }
1408-1428: Include exception details in warning logs for billing program initialization.Similar to the
applyDeveloperBillingOptionmethod, the exception catch blocks here don't include the exception details in the log messages. While these are expected when running on older Billing Library versions, includinge.messagehelps with debugging unexpected failures.🔎 Proposed fix to include exception details
} catch (e: NoSuchMethodException) { - OpenIapLog.w("✗ EXTERNAL_PAYMENTS not found. Requires Billing Library 8.3.0+", TAG) + OpenIapLog.w("✗ EXTERNAL_PAYMENTS not found. Requires Billing Library 8.3.0+: ${e.message}", TAG) } catch (e: Exception) { OpenIapLog.w("✗ Failed to enable EXTERNAL_PAYMENTS: ${e.message}", TAG) } } else { // For other programs, use the simpler enableBillingProgram method try { val method = builder.javaClass.getMethod("enableBillingProgram", Int::class.javaPrimitiveType) method.invoke(builder, programConstant) OpenIapLog.d("✓ Billing program enabled: $program (constant=$programConstant)", TAG) } catch (e: NoSuchMethodException) { - OpenIapLog.w("✗ enableBillingProgram not found. Requires Billing Library 8.2.0+", TAG) + OpenIapLog.w("✗ enableBillingProgram not found. Requires Billing Library 8.2.0+: ${e.message}", TAG) } catch (e: Exception) { OpenIapLog.w("✗ Failed to enable billing program $program: ${e.message}", TAG) }
📜 Review details
Configuration used: defaults
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 (18)
packages/apple/Sources/Models/Types.swiftpackages/docs/src/components/MenuDropdown.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/styles/documentation.csspackages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/DeveloperProvidedBillingListener.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/OpenIapListener.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/gql/src/type-android.graphqlpackages/gql/src/type.graphql
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{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/components/MenuDropdown.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/docs/src/pages/docs/events.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/components/MenuDropdown.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/features/external-purchase.tsx
packages/docs/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Place reusable components in
src/components/directory; co-locate single-use components with their parent component. For components with sub-components used only within them, create a folder withindex.tsxfor the main component
Files:
packages/docs/src/components/MenuDropdown.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/components/MenuDropdown.tsxpackages/docs/src/styles/documentation.csspackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/docs/src/pages/docs/events.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/components/MenuDropdown.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/docs/src/pages/docs/events.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/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/DeveloperProvidedBillingListener.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/OpenIapListener.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/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.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.graphqlpackages/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)
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/helpers/Helpers.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/DeveloperProvidedBillingListener.ktpackages/docs/src/pages/docs/events.tsxpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/helpers/Helpers.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/helpers/Helpers.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/listener/DeveloperProvidedBillingListener.ktpackages/gql/src/type.graphqlpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/src/type-android.graphqlpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/gql/src/type.graphqlpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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.graphqlpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
Learning: Applies to packages/apple/Sources/Helpers/**/*.swift : Place internal helper classes in `Sources/Helpers/` (e.g., `ProductManager.swift` for thread-safe caching, `IapStatus.swift` for UI status); these are NOT official OpenIAP types
Applied to files:
packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/events.tsxpackages/docs/src/pages/docs/features/external-purchase.tsx
🧬 Code graph analysis (3)
packages/docs/src/pages/docs/updates/notes.tsx (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
code(1843-1865)code(2489-2508)packages/gql/src/generated/Types.kt (2)
code(1916-1938)code(2562-2581)
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (1)
packages/gql/src/generated/types.ts (4)
RequestPurchaseProps(1002-1016)RequestPurchasePropsByPlatforms(1026-1035)RequestPurchaseAndroidProps(965-980)DeveloperBillingOptionParamsAndroid(129-136)
packages/docs/src/pages/docs/features/external-purchase.tsx (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
code(1843-1865)code(2489-2508)packages/gql/src/generated/Types.kt (2)
code(1916-1938)code(2562-2581)
🪛 detekt (1.23.8)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
[warning] 1412-1412: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1423-1423: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1694-1694: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1696-1696: 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] 934-934: Type name 'BillingProgramReportingDetailsParamsAndroid' 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 Android
🔇 Additional comments (30)
packages/docs/src/components/MenuDropdown.tsx (1)
66-66: Consistent styling alignment with CSS updates.The inline padding
0.4rem 0matches the updated.docs-nav apadding indocumentation.css, maintaining visual consistency across the navigation.packages/docs/src/styles/documentation.css (1)
57-71: Navigation spacing refinements look good.The coordinated reduction in margins and padding creates a more compact navigation layout. The changes are consistent across both CSS rules and the inline styles in
MenuDropdown.tsx.packages/google/openiap/src/main/java/dev/hyo/openiap/listener/OpenIapListener.kt (1)
42-53: New listener interface follows existing patterns.The
OpenIapDeveloperProvidedBillingListenercorrectly usesfun interfacefor SAM conversion (consistent with other listeners), includes appropriate KDoc documentation, and follows the established naming convention without theAndroidsuffix per coding guidelines.packages/docs/src/pages/docs/updates/notes.tsx (1)
26-163: Well-structured release note for External Payments feature.The new note entry provides comprehensive documentation including:
- New APIs and event types
- Comparison table distinguishing from User Choice Billing
- Proper external links with security attributes (
rel="noopener noreferrer")- Internal documentation cross-references
The structure follows existing patterns and maintains chronological ordering.
packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt (1)
96-108: NewdeveloperBillingOptionfield correctly integrated.The nullable field type is appropriate since developer billing is optional. The field follows the existing pattern and is consistently propagated in both the Purchase and Subscription branches of
toAndroidPurchaseArgs().packages/docs/src/pages/docs/features/external-purchase.tsx (5)
1260-1277: Comprehensive External Payments section with clear availability scope.The documentation clearly establishes the Japan-only availability and 8.3.0+ requirement upfront. The info alert appropriately guides developers to check availability before relying on the feature.
1299-1380: TypeScript example is well-structured.The example demonstrates the complete flow including listener setup, availability check, and token reporting. The comments clearly explain each step and the callback behavior for both Google Play and developer billing paths.
1382-1470: Kotlin example correctly uses library APIs.The Kotlin example correctly uses
addDeveloperProvidedBillingListener(withoutAndroidsuffix per coding guidelines for the Android-only package) and properly demonstratesRequestPurchasePropsconstruction with the newdeveloperBillingOptionparameter.
1544-1612: Clear flow diagram and critical token reporting warning.The flow diagram table provides an excellent step-by-step reference. The warning about the 24-hour token reporting requirement is appropriately prominent and mentions the specific API endpoint needed.
62-62: Platform support table updated.The table now correctly reflects the progression of billing library versions: legacy (6.2+), recommended (8.2.0+), and External Payments (8.3.0+).
packages/google/openiap/src/main/java/dev/hyo/openiap/listener/DeveloperProvidedBillingListener.kt (1)
1-28: Not a duplication — these listeners serve different purposes with distinct architectural roles.
DeveloperProvidedBillingListeneris the new public-facing listener interface (set viasetDeveloperProvidedBillingListener()), whileOpenIapDeveloperProvidedBillingListeneris the internal multi-listener collection (managed viaadd/removemethods in OpenIapModule). They use different data types intentionally:DeveloperProvidedBillingDetails(simpler) vsDeveloperProvidedBillingDetailsAndroid(with Android platform details). This separation is intentional and not a consolidation concern.packages/gql/src/type.graphql (1)
31-35: New enum value correctly follows naming conventions and types are regenerated.The
DeveloperProvidedBillingAndroidenum value follows the established pattern for platform-specific events in the IapEvent enum (matchingPromotedProductIOSandUserChoiceBillingAndroid), includes helpful documentation about availability (8.3.0+), and has been properly regenerated in all type files (packages/gql/src/generated/Types.kt and packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt).packages/gql/src/type-android.graphql (1)
270-276: External Payments GraphQL schema looks consistent; remember to regenerate typesThe schema extensions for External Payments and Developer Provided Billing look well-structured:
developerBillingOptionadded to bothRequestPurchaseAndroidPropsandRequestSubscriptionAndroidPropsas an optionalDeveloperBillingOptionParamsAndroid, matching the intendedbillingProgram/linkUri/launchModeshape.BillingProgramAndroidgainsEXTERNAL_PAYMENTSwith clear 8.3.0+ / Japan-only documentation.- New definitions for
DeveloperBillingLaunchModeAndroid,DeveloperBillingOptionParamsAndroid,DeveloperProvidedBillingDetailsAndroid, andBillingProgramReportingDetailsParamsAndroidalign with the Android BillingClient API.Since this is a schema change in
packages/gql, please ensure you regenerate and commit the corresponding generated types for all platforms (TS, Swift, Kotlin, Dart), e.g. via your existing tooling (bun run generateor./scripts/generate-types.sh), and confirm that the generatedDeveloperBillingOptionParamsAndroid/DeveloperProvidedBillingDetailsAndroidtypes match the Kotlin and TS usage introduced in this PR.Also applies to: 313-318, 517-542, 640-709
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt (1)
4-6: All implementations of OpenIapProtocol have been properly updated with the new Developer Provided Billing methodsVerification confirms all protocol implementors now define the three required methods:
- Horizon OpenIapModule: Methods implemented with
UnsupportedOperationException(appropriate since External Payments is Play-only)- Play OpenIapModule: Methods fully implemented with real functionality
- OpenIapStore: Convenience methods delegate to module implementations
No test fakes or mocks implementing
OpenIapProtocolwere found, so no additional updates are needed. The API is consistent with the existing User Choice listener pattern.packages/apple/Sources/Models/Types.swift (3)
1-6: Verify this file was properly regenerated.Per coding guidelines,
Types.swiftinSources/Models/is auto-generated from the OpenIAP GraphQL schema and should not be edited directly. Please confirm these changes were made by running./scripts/generate-types.shafter updating the GraphQL schema, rather than manual edits.Based on learnings, this file should be regenerated using
./scripts/generate-types.shand not edited directly.
23-53: LGTM - New enums for External Payments.The
BillingProgramAndroid.externalPaymentscase andDeveloperBillingLaunchModeAndroidenum follow the established patterns. Documentation properly indicates the 8.3.0+ availability requirement.
931-943: LGTM - Request props and structs properly updated.The new structs (
BillingProgramReportingDetailsParamsAndroid,DeveloperBillingOptionParamsAndroid,DeveloperProvidedBillingDetailsAndroid) and thedeveloperBillingOptionadditions toRequestPurchaseAndroidPropsandRequestSubscriptionAndroidPropsfollow the established patterns. The initializers correctly include the new optional parameters.Note: The SwiftLint warning about
BillingProgramReportingDetailsParamsAndroidexceeding 40 characters is acceptable here since this is auto-generated from the GraphQL schema and the name should match the API.Also applies to: 960-980, 1102-1128, 1264-1309
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (4)
1196-1202: LGTM - Listener management methods follow existing patterns.The
addDeveloperProvidedBillingListenerandremoveDeveloperProvidedBillingListenermethods are consistent with the existing listener management pattern used for other listener types.
1553-1633: LGTM - External Payments initialization with proper reflection approach.The
enableExternalPaymentsProgrammethod correctly uses reflection to support optional Billing Library 8.3.0+ APIs. The proxy-basedDeveloperProvidedBillingListenerimplementation properly extracts the external transaction token and notifies registered listeners.
1482-1488: LGTM - Setter method follows existing patterns.The
setDeveloperProvidedBillingListenermethod follows the same pattern assetUserChoiceBillingListener.
935-938: LGTM - Developer billing option integration in purchase flow.The
developerBillingOptionis properly applied to theBillingFlowParamsbuilder when provided, enabling the side-by-side choice for external payments flow.packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (9)
1-4: Auto-generated file - verified proper structure.This file is auto-generated from the GraphQL schema. The External Payments types have been properly generated with consistent patterns for enums, data classes, and JSON serialization methods.
Based on learnings, ensure any future changes are made by updating the GraphQL schema and running
./scripts/generate-types.shto regenerate this file.
47-88: LGTM! External Payments enum properly added.The
ExternalPaymentsenum value is well-documented, noting it's Japan-only and requires Google Play Billing Library 8.3.0+. The JSON parsing correctly handles both kebab-case and PascalCase variants, consistent with other enum values.
90-124: LGTM! New launch mode enum properly implemented.The
DeveloperBillingLaunchModeAndroidenum is well-structured with clear documentation for each launch mode. The implementation follows the established pattern for enums in this file.
342-371: LGTM! Developer billing event properly added.The new
DeveloperProvidedBillingAndroidevent is well-documented and follows the established pattern for IAP events in this file. The documentation clearly indicates when this event fires and the version requirement.
945-971: LGTM! Billing details class properly implemented.The
DeveloperProvidedBillingDetailsAndroiddata class follows the established pattern with proper JSON serialization. The documentation importantly highlights the 24-hour reporting deadline for the transaction token.
2569-2591: LGTM! Reporting params class properly structured.The
BillingProgramReportingDetailsParamsAndroidinput class is well-documented and follows the standard pattern for parameter objects. The default toUnspecifiedinfromJsonis appropriate.
2618-2652: LGTM! Developer billing params properly implemented.The
DeveloperBillingOptionParamsAndroidclass is well-structured with comprehensive documentation. All three required parameters are properly typed and the JSON conversion handles appropriate defaults.
2808-2851: LGTM! Purchase props properly extended for external payments.The
developerBillingOptionfield is correctly added as optional toRequestPurchaseAndroidProps. The documentation clearly explains the external payments flow behavior when this parameter is provided.
3003-3072: LGTM! Subscription props properly extended for external payments.The
developerBillingOptionfield is correctly added toRequestSubscriptionAndroidProps, maintaining consistency with the purchase props. The implementation properly supports the external payments flow for subscriptions.
- Fix setLinkUri to use android.net.Uri instead of String - Fix listener add/remove mismatch with remembered listeners - Add Horizon guard for Play-only listeners - Rename userChoiceBillingListener to userChoiceBillingListenerAndroid in docs
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
1674-1676: Past issue resolved:setLinkUrinow correctly usesandroid.net.Uri.The previous review comment about using
Stringinstead ofandroid.net.Urihas been addressed. Line 1675 now correctly reflects the method withandroid.net.Uri::class.java, and line 1676 properly parses the string to a Uri before invocation.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/types/alternative.tsxpackages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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/types/alternative.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/types/alternative.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/types/alternative.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/types/alternative.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/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
🧠 Learnings (5)
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/types/alternative.tsx
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
Learning: Applies to **/*.{ts,tsx} : iOS-specific functions must end with `IOS` suffix (e.g., `clearTransactionIOS`, `getAppTransactionIOS`)
Applied to files:
packages/docs/src/pages/docs/types/alternative.tsx
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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/docs/src/pages/docs/types/alternative.tsxpackages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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.233Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.233Z
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
🧬 Code graph analysis (2)
packages/docs/src/pages/docs/types/alternative.tsx (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)
code(611-616)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
code(1843-1865)code(2489-2508)packages/gql/src/generated/Types.kt (2)
code(1916-1938)code(2562-2581)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/gql/src/generated/types.ts (1)
DeveloperProvidedBillingDetailsAndroid(143-150)
🪛 detekt (1.23.8)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
[warning] 1412-1412: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1423-1423: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1694-1694: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 1696-1696: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (15)
packages/docs/src/pages/docs/types/alternative.tsx (1)
195-532: Excellent comprehensive examples with proper listener naming.The User Choice Billing and Alternative Billing Only examples are well-structured and comprehensive. The use of
userChoiceBillingListenerAndroid(lines 207, 214, 253) correctly follows the Android-specific naming convention, addressing the previous review feedback.The warning box about the 24-hour reporting requirement (lines 524-532) is appropriately placed and highlights a critical compliance point.
packages/docs/src/pages/docs/features/external-purchase.tsx (3)
780-847: User Choice Billing listener naming correctly updated.All references to
userChoiceBillingListenerAndroid(lines 784, 795, 839) now correctly include theAndroidsuffix, addressing the previous review feedback.
1260-1640: Excellent comprehensive External Payments documentation.The External Payments (8.3.0+) section is well-structured with:
- Clear availability information (Japan only)
- Comprehensive key differences explanation (lines 1279-1297)
- Detailed flow diagram (lines 1544-1600)
- Appropriate warning about 24-hour token reporting requirement (lines 1602-1612)
- Clear launch mode options documentation (lines 1614-1639)
The multi-language code examples (TypeScript, Kotlin, Dart) provide excellent coverage for developers.
62-62: Platform support table correctly updated.The Android platform entry now accurately reflects support for Google Play Billing 8.3.0+ with External Payments.
packages/google/Example/src/main/java/dev/hyo/martie/screens/AlternativeBillingScreen.kt (6)
42-45: LGTM! External Payments imports added correctly.The new imports for External Payments (8.3.0+) support are properly added and necessary for the feature implementation.
54-54: ✅ Enum addition and remembered listeners fix past review issues.The addition of
EXTERNAL_PAYMENTSenum value and the creation of remembered listener instances properly address the listener lifecycle issues flagged in the previous review. Usingrememberensures a single listener instance is created and can be properly added/removed without leaks or duplicates.Based on learnings, this follows the correct pattern for listener management in this Android-only package.
Also applies to: 82-122
124-144: ✅ Past review issues resolved: Listener lifecycle and Horizon guards fixed.Both critical issues from the previous review have been properly addressed:
Listener add/remove pairing fixed: Now correctly uses
addUserChoiceBillingListener/removeUserChoiceBillingListenerandaddDeveloperProvidedBillingListener/removeDeveloperProvidedBillingListenerwith the same remembered listener instance, preventing leaks and duplicate callbacks.Horizon guards added: Both
LaunchedEffectblocks now checkif (isHorizon) return@LaunchedEffectat the start, preventingUnsupportedOperationExceptionon Horizon builds.
198-203: LGTM! External Payments connection config follows correct pattern.The initialization logic for External Payments properly enables
BillingProgramAndroid.ExternalPaymentsbefore connection, matching the pattern used for Billing Programs mode. This aligns with Google Play Billing 8.3.0+ requirements.
329-329: LGTM! Clear and informative UI updates for External Payments.The UI additions properly communicate:
- Japan-only availability (mentioned multiple times for clarity)
- Side-by-side purchase choice mechanism
- Detailed 7-step flow explanation
- Differences from User Choice Billing
The use of the Language icon (🌐) and consistent "8.3.0+ Japan" labeling helps users understand the feature's scope and restrictions.
Also applies to: 406-425, 474-493, 539-555, 598-598
939-1016: Fix deprecated parameter: useandroidon line 902.The code uses the correct, non-deprecated parameter name
androidparameter. Both should useLine 902 should be:
RequestPurchasePropsByPlatforms( google = RequestPurchaseAndroidProps( skus = listOf(selectedProduct!!.id) ) )The
androidparameter is marked as deprecated across all generated types (Kotlin, TypeScript, Dart, Swift) in favor of⛔ Skipped due to learnings
Learnt from: CR Repo: hyodotdev/openiap PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-12-06T20:15:59.233Z 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()`)Learnt from: CR Repo: hyodotdev/openiap PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-12-06T20:15:59.233Z Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (5)
1560-1633: Well-structured External Payments initialization.The
enableExternalPaymentsProgrammethod correctly:
- Creates the DeveloperProvidedBillingListener proxy via reflection
- Extracts and propagates the external transaction token
- Uses appropriate exception handling for version compatibility (8.3.0+)
- Provides comprehensive logging for debugging
1642-1701: Correct implementation of DeveloperBillingOption.The
applyDeveloperBillingOptionmethod properly:
- Converts enums to billing program constants
- Builds DeveloperBillingOptionParams via reflection
- Correctly uses
android.net.Urifor the link URI parameter- Applies the params to the billing flow builder
- Handles version compatibility gracefully
1396-1431: Billing programs initialization handles External Payments correctly.The initialization logic properly:
- Distinguishes EXTERNAL_PAYMENTS (8.3.0+) from other billing programs (8.2.0+)
- Routes EXTERNAL_PAYMENTS to the specialized
enableExternalPaymentsProgramwith listener support- Uses the simpler
enableBillingProgramAPI for other programs- Handles version compatibility with appropriate exception catching and logging
1196-1202: Listener management methods implemented correctly.The
addDeveloperProvidedBillingListenerandremoveDeveloperProvidedBillingListenermethods properly manage the listener collection, following the established pattern for other listener types.
935-938: Developer billing option correctly integrated into purchase flow.The conditional application of
developerBillingOptionis properly positioned in the billing flow setup, applying the external payments configuration only when specified in the purchase arguments.
- Rename developerProvidedBillingListener to developerProvidedBillingListenerAndroid in docs - Notify legacy-style listeners (set via constructor or setter) in addition to collection listeners
Changed from com.android.billingclient:billing to io.github.hyochan.openiap:openiap-google
Reference: https://developer.android.com/google/play/billing/release-notes#8-3-0
Summary by CodeRabbit
New Features
Documentation
Style
✏️ Tip: You can customize this high-level summary in your review settings.