Billing Program Configuration
Android Billing Programs#
Configure Google Play billing programs through InitConnectionConfig.enableBillingProgramAndroid.
These options are Android only. See the current Google Play requirements in the Google alternative billing documentation.
Native references: Google · Alternative billing · Google · User Choice Billing
InitConnectionConfig#
Configuration options for initConnection():
| Name | Summary |
|---|---|
enableBillingProgramAndroid | (Recommended) Enable a specific billing program during connection. Use USER_CHOICE_BILLING for user choice, EXTERNAL_OFFER for alternative only, or EXTERNAL_PAYMENTS for Japan external payments (8.3.0+). Use BILLING_CHOICE for Billing Choice (OpenIAP Spec 2.1.0 / openiap-google 2.3.0; Play Billing 9.1.0+). |
billingChoiceScreenTypeAndroid | Billing Choice renderer in OpenIAP Spec 2.1.0 / openiap-google 2.3.0 (Play Billing 9.1.0+). Defaults to GOOGLE_RENDERED. Set DEVELOPER_RENDERED when your app renders the choice screen; this controls whether OpenIAP registers Play's developer-provided billing listener. |
Basic Usage (Recommended)#
// iOS uses standard StoreKit billing
// Alternative billing is Android-only
let connected = try await OpenIapModule.shared.initConnection()
precondition(connected, "Store connection failed")User Choice Billing Complete Example#
With User Choice Billing (7.0+), users see a dialog to choose between Google Play or your alternative payment. Handle both paths:
import dev.hyo.openiap.*
import dev.hyo.openiap.listener.OpenIapUserChoiceBillingListener
import dev.hyo.openiap.store.OpenIapStore
val iapStore = OpenIapStore(context)
// Step 1: Set up listener for when user selects alternative billing
val userChoiceListener = object : OpenIapUserChoiceBillingListener {
override fun onUserChoiceBilling(details: UserChoiceBillingDetails) {
Log.d("IAP", "User chose alternative billing")
for (productId in details.products) {
Log.d("IAP", "Product: $productId")
}
Log.d("IAP", "External transaction token received; send it to your backend without logging it.")
// Process payment with your backend using the token
lifecycleScope.launch {
try {
val paymentResult = yourBackend.processPayment(
products = details.products,
token = details.externalTransactionToken
)
if (paymentResult.success) {
grantUserAccess()
}
} catch (e: Exception) {
Log.e("IAP", "Alternative billing failed: ${e.message}")
}
}
}
}
iapStore.addUserChoiceBillingListener(userChoiceListener)
// Step 2: Initialize with user choice billing (recommended)
val connected = iapStore.initConnection(
InitConnectionConfig(
enableBillingProgramAndroid = BillingProgramAndroid.UserChoiceBilling
)
)
check(connected) { "Store connection failed" }
// Step 3: Fetch products and purchase as normal
val products = iapStore.fetchProducts(
ProductRequest(
skus = listOf("premium_subscription"),
type = ProductQueryType.Subs
)
)
// Step 4: Request purchase - dialog will show both options
iapStore.setActivity(activity)
iapStore.requestPurchase(
RequestPurchaseProps(
request = RequestPurchaseProps.Request.Subscription(
RequestSubscriptionPropsByPlatforms(
google = RequestSubscriptionAndroidProps(
skus = listOf("premium_subscription")
)
)
),
type = ProductQueryType.Subs
)
)
// If user selects Google Play → onPurchaseSuccess fires
// If user selects alternative → OpenIapUserChoiceBillingListener fires
// Call when the owning lifecycle ends.
fun disposeUserChoiceListener() {
iapStore.removeUserChoiceBillingListener(userChoiceListener)
}Alternative Billing Only Complete Example#
With External Offer mode (replaces Alternative Only), all purchases go through your alternative payment system. Use the Billing Programs API available with Play Billing 8.2.1+:
import dev.hyo.openiap.*
import dev.hyo.openiap.store.OpenIapStore
val iapStore = OpenIapStore(context)
// Step 1: Initialize with external offer (recommended)
val connected = iapStore.initConnection(
InitConnectionConfig(
enableBillingProgramAndroid = BillingProgramAndroid.ExternalOffer
)
)
check(connected) { "Store connection failed" }
// Step 2: Check whether External Offer is available for this user.
val availability = iapStore.isBillingProgramAvailable(
BillingProgramAndroid.ExternalOffer
)
if (!availability.isAvailable) {
Log.w("IAP", "External Offer is not available in this region")
return
}
// Step 3: Fetch products (still needed to show prices)
val products = iapStore.fetchProducts(
ProductRequest(
skus = listOf("premium_subscription"),
type = ProductQueryType.Subs
)
)
val product = (products as? FetchProductsResultSubscriptions)
?.value
?.firstOrNull()
?: return
// Step 4: Create reporting details immediately before redirecting.
val reportingDetails = iapStore.createBillingProgramReportingDetails(
BillingProgramAndroid.ExternalOffer
)
// Step 5: Play shows the disclosure and launches the checkout URL.
val launched = iapStore.launchExternalLink(
activity,
LaunchExternalLinkParamsAndroid(
billingProgram = BillingProgramAndroid.ExternalOffer,
launchMode = ExternalLinkLaunchModeAndroid.LaunchInExternalBrowserOrApp,
linkType = ExternalLinkTypeAndroid.LinkToDigitalContentOffer,
linkUri = "https://your-payment-site.com/checkout"
)
)
if (!launched) return
// Step 6: This helper must wait for and verify checkout completion; a true
// launch result alone is not proof of purchase. Then report from your backend.
lifecycleScope.launch {
val paymentResult = yourBackend.completeAndReportExternalPurchase(
productId = product.id,
externalTransactionToken = reportingDetails.externalTransactionToken,
userId = currentUserId
)
if (paymentResult.success) {
grantUserAccess()
}
}For both User Choice and Alternative Only modes, you must report completed transactions to Google Play within 24 hours using the Google Play Developer API. Failure to report may result in account suspension.