KMP-IAP 1.2.0 brings Google Play Billing Library 8.2.0 features including the new Billing Programs API for external billing and one-time product discount support from Billing Library 7.0+.
New Features
1. Billing Programs API (Android 8.2.0+)
New methods for handling external billing programs:
isBillingProgramAvailable(program)- Check if a billing program is available for the usercreateBillingProgramReportingDetails(program)- Get external transaction token for reportinglaunchExternalLink(params)- Launch external link for billing programs
Supported Programs:
ExternalOffer- External offer programsExternalContentLink- External content link programs
import io.github.hyochan.kmpiap.kmpIapInstance
import io.github.hyochan.kmpiap.openiap.*
// Step 1: Check availability
val result = kmpIapInstance.isBillingProgramAvailable(BillingProgram.ExternalOffer)
if (!result.isAvailable) return
// Step 2: Launch external link
kmpIapInstance.launchExternalLink(
LaunchExternalLinkParams(
billingProgram = BillingProgram.ExternalOffer,
launchMode = ExternalLinkLaunchMode.LaunchInExternalBrowserOrApp,
linkType = ExternalLinkType.LinkToDigitalContentOffer,
linkUri = "https://your-payment-site.com"
)
)
// Step 3: Get reporting token
val details = kmpIapInstance.createBillingProgramReportingDetails(BillingProgram.ExternalOffer)
// Report token to Google Play within 24 hours
The Billing Programs API methods currently return FeatureNotSupported error as the underlying Google Play Billing Library 8.2.0 APIs are not yet available in the billing-ktx dependency. The types and methods are provided for future compatibility.
2. One-Time Product Discounts (Android 7.0+)
oneTimePurchaseOfferDetailsAndroid is now an array to support multiple offers with discounts.
New Fields:
offerId- Unique offer identifierofferTags- Tags for categorizing offersofferToken- Token for purchasing specific offerdiscountDisplayInfo- Percentage and amount discount infolimitedQuantityInfo- Purchase quantity limitsvalidTimeWindow- Offer validity periodpreorderDetails- Preorder release info (8.1.0+)rentalDetails- Rental period info
3. Purchase Suspension Status (Android 8.1.0+)
- Added
isSuspendedAndroidfield toPurchaseAndroidtype - Indicates when a subscription is suspended due to payment issues
- Users should be directed to fix payment method when suspended
if (purchase.isSuspendedAndroid == true) {
// Show UI to direct user to fix payment method
showPaymentIssueDialog()
}
Breaking Changes
1. oneTimePurchaseOfferDetailsAndroid Type Change
Before (1.0.0):
val price = product.oneTimePurchaseOfferDetailsAndroid?.formattedPrice
After (1.2.0):
val price = product.oneTimePurchaseOfferDetailsAndroid?.firstOrNull()?.formattedPrice
Migration: Update code that accesses this field to handle arrays:
// Access first offer (most common case)
val offers = product.oneTimePurchaseOfferDetailsAndroid
val price = offers?.firstOrNull()?.formattedPrice
// Or iterate through all offers
offers?.forEach { offer ->
println("Offer: ${offer.offerId}, Price: ${offer.formattedPrice}")
offer.discountDisplayInfo?.let { discount ->
println("Discount: ${discount.percentageDiscount}%")
}
}
2. VerifyPurchaseProps API Change
VerifyPurchaseProps now uses platform-specific nested options instead of a root sku field.
Before (1.0.0):
val result = kmpIapInstance.verifyPurchase(
VerifyPurchaseProps(
sku = "premium_upgrade",
androidOptions = VerifyPurchaseAndroidOptions(
accessToken = token,
packageName = "com.yourapp.id",
productToken = purchaseToken,
isSub = false
)
)
)
After (1.2.0):
// iOS verification
val iosResult = kmpIapInstance.verifyPurchase(
VerifyPurchaseProps(
apple = VerifyPurchaseAppleOptions(sku = "premium_upgrade")
)
)
// Android verification
val androidResult = kmpIapInstance.verifyPurchase(
VerifyPurchaseProps(
google = VerifyPurchaseGoogleOptions(
sku = "premium_upgrade",
accessToken = backendProvidedToken, // Get from your secure backend
packageName = "com.yourapp.id",
purchaseToken = purchase.purchaseToken ?: "",
isSub = false
)
)
)
// Meta Quest (Horizon) verification
val horizonResult = kmpIapInstance.verifyPurchase(
VerifyPurchaseProps(
horizon = VerifyPurchaseHorizonOptions(
sku = "premium_upgrade",
userId = userId,
accessToken = backendProvidedToken
)
)
)
The accessToken for Google and Horizon verification must be obtained from your secure backend. Never hardcode or store API credentials in your app.
New Types
Billing Programs API Types
// Billing program type
enum class BillingProgram {
Unspecified,
ExternalContentLink,
ExternalOffer
}
// Launch mode for external links
enum class ExternalLinkLaunchMode {
Unspecified,
LaunchInExternalBrowserOrApp,
CallerWillLaunchLink
}
// Link type for external links
enum class ExternalLinkType {
Unspecified,
LinkToDigitalContentOffer,
LinkToAppDownload
}
// Parameters for launching external links
data class LaunchExternalLinkParams(
val billingProgram: BillingProgram,
val launchMode: ExternalLinkLaunchMode,
val linkType: ExternalLinkType,
val linkUri: String
)
// Result types
data class BillingProgramAvailabilityResult(
val billingProgram: BillingProgram,
val isAvailable: Boolean
)
data class BillingProgramReportingDetails(
val billingProgram: BillingProgram,
val externalTransactionToken: String
)
One-Time Product Discount Types
// Discount display information
data class DiscountDisplayInfoAndroid(
val percentageDiscount: Int? = null,
val discountAmount: DiscountAmountAndroid? = null
)
// Discount amount details
data class DiscountAmountAndroid(
val discountAmountMicros: String,
val formattedDiscountAmount: String
)
// Limited quantity information
data class LimitedQuantityInfoAndroid(
val maximumQuantity: Int,
val remainingQuantity: Int
)
// Offer validity period
data class ValidTimeWindowAndroid(
val startTimeMillis: String,
val endTimeMillis: String
)
// Pre-order details (8.1.0+)
data class PreorderDetailsAndroid(
val preorderReleaseTimeMillis: String,
val preorderPresaleEndTimeMillis: String
)
// Rental details
data class RentalDetailsAndroid(
val rentalPeriod: String,
val rentalExpirationPeriod: String? = null
)
Purchase Verification Types
// Platform-specific verification options (NEW in 1.2.0)
data class VerifyPurchaseProps(
val apple: VerifyPurchaseAppleOptions? = null,
val google: VerifyPurchaseGoogleOptions? = null,
val horizon: VerifyPurchaseHorizonOptions? = null
)
data class VerifyPurchaseAppleOptions(
val sku: String
)
data class VerifyPurchaseGoogleOptions(
val sku: String,
val accessToken: String, // Obtain from your backend
val packageName: String,
val purchaseToken: String,
val isSub: Boolean? = null
)
data class VerifyPurchaseHorizonOptions(
val sku: String,
val userId: String,
val accessToken: String // Obtain from your backend
)
// Deprecated (use VerifyPurchaseGoogleOptions instead)
@Deprecated("Use VerifyPurchaseGoogleOptions instead")
typealias VerifyPurchaseAndroidOptions = VerifyPurchaseGoogleOptions
OpenIAP Updates
Updated OpenIAP versions:
openiap-apple: 1.3.0 -> 1.3.5openiap-google: 1.3.10 -> 1.3.14openiap-gql: 1.3.0 -> 1.3.5
Installation
Gradle (Kotlin DSL)
// shared/build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.hyochan:kmp-iap:1.2.0")
}
}
}
Version Catalog (libs.versions.toml)
[versions]
kmp-iap = "1.2.0"
[libraries]
kmp-iap = { module = "io.github.hyochan:kmp-iap", version.ref = "kmp-iap" }
References
- Google Play Billing Library 8.2.0 Release Notes
- Google Play Billing Programs
- Google Play One-Time Product Discounts
- API Reference - Core Methods
- API Reference - Types
Questions or feedback? Reach out via GitHub Issues or join our Slack community.
