This release syncs with OpenIAP gql v1.3.14 / google v1.3.25 / apple v1.3.13.
New Features
Android Product Status Codes (Billing 8.0+)
Products now include status codes explaining fetch results:
val products = kmpIapInstance.fetchProducts {
skus = listOf("premium_monthly")
type = ProductQueryType.Subs
}
products.forEach { product ->
when ((product as? ProductAndroid)?.productStatusAndroid) {
ProductStatusAndroid.Ok -> println("Product fetched successfully")
ProductStatusAndroid.NotFound -> println("SKU doesn't exist")
ProductStatusAndroid.NoOffersAvailable -> println("User not eligible")
ProductStatusAndroid.Unknown -> println("Unknown status")
null -> {} // iOS or no status
}
}
New Types & DSL Updates
iOS Subscription Options (Types Only)
The following new iOS subscription options are now available in Kotlin types and DSL builders for forward compatibility:
introductoryOfferEligibility- Override introductory offer eligibility (iOS 15+, WWDC 2025)promotionalOfferJWS- JWS promotional offer (iOS 15+, WWDC 2025)winBackOffer- Win-back offer for iOS 18+
// DSL syntax available
kmpIapInstance.requestPurchase {
type = ProductType.Subs
ios {
sku = "premium_monthly"
winBackOffer = WinBackOfferInputIOS(offerId = "winback_50_off")
promotionalOfferJWS = PromotionalOfferJWSInputIOS(offerId = "promo", jws = "...")
introductoryOfferEligibility = true
}
}
Note: These options require OpenIAP's ObjC bridge update to function. Currently, kmp-iap's cinterop calls the ObjC bridge
requestSubscriptionWithSku(sku, offer)which doesn't accept these new parameters. The legacywithOffer(DiscountOfferInputIOS) continues to work.
Android Suspended Subscriptions Option (Billing 8.1+)
New includeSuspendedAndroid option added to PurchaseOptions for fetching suspended subscriptions:
// Fetch available purchases including suspended subscriptions
val purchases = kmpIapInstance.getAvailablePurchases(
PurchaseOptions(includeSuspendedAndroid = true)
)
purchases.forEach { purchase ->
println("Product: ${purchase.productId}, State: ${purchase.purchaseState}")
}
This option uses reflection for backward compatibility with older Billing Library versions. When the underlying library doesn't support this feature, it gracefully degrades to the default behavior.
Breaking Changes
iOS Subscription-Only Props Cleanup
Removed subscription-specific fields from RequestPurchaseIosProps. These fields now only exist in RequestSubscriptionIosProps:
introductoryOfferEligibilitypromotionalOfferJWSwinBackOffer
Migration: If using these fields for purchases, switch to requestSubscription() API.
Bug Fixes
Android displayPrice Fix for Free Trials
Fixed an issue where displayPrice returned "Free" or "$0.00" for subscription products with free trials. Now returns the actual base/recurring price.
// Before (bug)
product.displayPrice // "Free" or "$0.00"
product.price // 0.0
// After (fixed)
product.displayPrice // "$9.99" (base recurring price)
product.price // 9.99
// Free trial info is still available in subscriptionOffers
product.subscriptionOffers[0].displayPrice // "$0.00"
product.subscriptionOffers[0].paymentMode // FreeTrial
New Types
| Type | Platform | Description |
|---|---|---|
WinBackOfferInputIOS | iOS | Win-back offer input for iOS 18+ |
PromotionalOfferJWSInputIOS | iOS | JWS promotional offer for iOS 15+ |
ProductStatusAndroid | Android | Product fetch status codes |
SubResponseCodeAndroid | Android | Sub-response codes for errors |
BillingResultAndroid | Android | Extended billing result with sub-response |
OpenIAP Versions
| Package | Version |
|---|---|
| openiap-gql | 1.3.14 |
| openiap-google | 1.3.25 |
| openiap-apple | 1.3.13 |
Installation
implementation("io.github.hyochan:kmp-iap:1.3.2")
