Skip to main content
Version: v1.0.0-rc (Current)

Core Methods

Essential methods for implementing in-app purchases with kmp-iap, now 100% OpenIAP specification compliant. All methods support both iOS and Android platforms with unified APIs using Kotlin coroutines.

Version Information

Version Differences

This documentation covers both:

  • v1.0.0-rc (Current) - Simplified API without wrapper classes
  • v1.0.0-beta - Previous API with wrapper classes

⚠️ Platform Differences: While the API is unified, there are important differences between iOS and Android implementations. Each method documents platform-specific behavior.

Connection Management

initConnection()

Initializes the connection to the platform store.

suspend fun initConnection(): Boolean

Returns: true if connection successful, false otherwise

Description: Establishes connection with the App Store (iOS) or Google Play Store (Android). Must be called before any other IAP operations.

Platform Differences:

  • iOS: Connects to StoreKit 2 (iOS 15+)
  • Android: Connects to Google Play Billing Client v8+

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

val connected = kmpIapInstance.initConnection()
if (connected) {
println("IAP connection initialized successfully")
} else {
println("Failed to initialize IAP connection")
}

Throws: PurchaseError if connection fails

See Also: endConnection(), Connection Lifecycle


endConnection()

Ends the connection to the platform store and cleans up resources.

suspend fun endConnection()

Returns: Unit (void)

Description: Cleanly closes the store connection and frees resources. Should be called when IAP functionality is no longer needed.

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

// In your cleanup code
override fun onCleared() {
scope.launch {
kmpIapInstance.endConnection()
}
}

Product Loading

requestProducts()

Loads product information from the store.

v1.0.0-rc (Current) - DSL API

suspend fun requestProducts(
builder: ProductsRequestBuilder.() -> Unit
): List<Product>

Parameters:

  • builder - DSL builder for configuring the request

Returns: List of products with pricing and metadata

Example:

import io.github.hyochan.kmpiap.kmpIapInstance
import io.github.hyochan.kmpiap.ProductType

// Load in-app products using DSL
val products = kmpIapInstance.requestProducts {
skus = listOf("coins_100", "coins_500", "remove_ads")
type = ProductType.INAPP
}

// Load subscriptions using DSL
val subscriptions = kmpIapInstance.requestProducts {
skus = listOf("premium_monthly", "premium_yearly")
type = ProductType.SUBS
}

v1.0.0-beta

suspend fun requestProducts(params: ProductRequest): List<Product>

Example:

val products = kmpIapInstance.requestProducts(
ProductRequest(
skus = listOf("coins_100", "coins_500"),
type = ProductType.INAPP
)
)

Platform Differences:

  • iOS: Uses StoreKit 2 API for product requests
  • Android: Uses queryProductDetailsAsync() with automatic product type detection

Purchase Processing

requestPurchase()

Initiates a purchase using OpenIAP-compliant request structure.

v1.0.0-rc (Current) - DSL API

suspend fun requestPurchase(
builder: PurchaseRequestBuilder.() -> Unit
): Purchase

Parameters:

  • builder - DSL builder for configuring platform-specific purchase options

Returns: Purchase object implementing PurchaseCommon interface following OpenIAP specification

Example:

import io.github.hyochan.kmpiap.kmpIapInstance
import io.github.hyochan.kmpiap.*

// Cross-platform purchase using DSL
val purchase = kmpIapInstance.requestPurchase {
ios {
sku = "premium_upgrade"
quantity = 1
}
android {
skus = listOf("premium_upgrade")
}
}

// iOS-only purchase
val iosPurchase = kmpIapInstance.requestPurchase {
ios {
sku = "coins_100"
quantity = 5
appAccountToken = "token_456"
}
}

// Android-only purchase
val androidPurchase = kmpIapInstance.requestPurchase {
android {
skus = listOf("coins_100")
obfuscatedAccountIdAndroid = "user_123"
obfuscatedProfileIdAndroid = "profile_456"
}
}

v1.0.0-beta

suspend fun requestPurchase(request: RequestPurchaseProps): Purchase

Example:

val purchase = kmpIapInstance.requestPurchase(
RequestPurchaseProps(
ios = RequestPurchaseIosProps(
sku = "premium_upgrade",
quantity = 1
),
android = RequestPurchaseAndroidProps(
skus = listOf("premium_upgrade")
)
)
)

Platform Differences:

  • iOS: Supports App Account Token for fraud prevention
  • Android: Supports obfuscated user IDs and automatic Activity detection

Transaction Management

finishTransaction()

Completes a transaction after successful purchase processing.

suspend fun finishTransaction(
purchase: Purchase,
isConsumable: Boolean? = null
): Boolean

Parameters:

  • purchase - The purchase to finish
  • isConsumable - Whether the product is consumable (null for auto-detection)

Returns: true if transaction was successfully finished, false otherwise

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

// For consumable products
val success = kmpIapInstance.finishTransaction(purchase, isConsumable = true)
if (success) {
println("Transaction finished successfully")
}

// For subscriptions (acknowledge only, don't consume)
val acknowledged = kmpIapInstance.finishTransaction(purchase, isConsumable = false)
if (acknowledged) {
println("Subscription acknowledged")
}

Platform Behavior:

  • iOS: Calls finish() on the transaction
  • Android:
    • Consumables: Calls consumeAsync
    • Non-consumables/Subscriptions: Calls acknowledgePurchase

⚠️ Important: Subscriptions should NEVER be consumed. They should only be acknowledged once.

Purchase History

getAvailablePurchases()

Gets all available (unconsumed/active) purchases.

suspend fun getAvailablePurchases(options: PurchaseOptions? = null): List<Purchase>

Returns: List of available purchases

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

val purchases = kmpIapInstance.getAvailablePurchases()
purchases.forEach { purchase ->
println("Product: \${purchase.productId}")
println("Date: \${purchase.transactionDate}")

// Check acknowledgment status (Android)
if (purchase.acknowledgedAndroid == true) {
println("Already acknowledged")
}
}

Platform Differences:

  • iOS: Returns active subscriptions and non-consumed purchases
  • Android: Queries both INAPP and SUBS purchases separately

getPurchaseHistories()

Gets purchase history including consumed items.

suspend fun getPurchaseHistories(options: PurchaseOptions? = null): List<ProductPurchase>

Returns: List of historical purchases

Note: Android Billing Client v6+ no longer provides purchase history. This method returns an empty list on Android.

Event Flows

Purchase Event Flows

All purchase events are exposed via Kotlin Flow for reactive programming:

// Purchase updates
val purchaseUpdatedListener: Flow<Purchase>

// Purchase errors
val purchaseErrorListener: Flow<PurchaseError>

// Promoted products (iOS only)
val promotedProductListener: Flow<String?>

Example Usage:

import io.github.hyochan.kmpiap.kmpIapInstance
import kotlinx.coroutines.flow.collectLatest

// Listen for purchase updates
scope.launch {
kmpIapInstance.purchaseUpdatedListener.collectLatest { purchase ->
println("Purchase completed: \${purchase.productId}")
// Deliver content to user
deliverContent(purchase.productId)
// Finish transaction
kmpIapInstance.finishTransaction(purchase, isConsumable = true)
}
}

// Listen for errors
scope.launch {
kmpIapInstance.purchaseErrorListener.collectLatest { error ->
when (error.code) {
ErrorCode.E_USER_CANCELLED.name -> {
println("User cancelled purchase")
}
else -> {
println("Purchase error: \${error.message}")
}
}
}
}

Platform-Specific Methods

iOS-Specific Methods

clearTransactionIOS()

Clears pending transactions (iOS only).

suspend fun clearTransactionIOS()

clearProductsIOS()

Clears cached products (iOS only).

suspend fun clearProductsIOS()

getPromotedProductIOS()

Gets the currently promoted product (iOS only).

suspend fun getPromotedProductIOS(): String?

presentCodeRedemptionSheetIOS()

Presents the App Store code redemption sheet.

suspend fun presentCodeRedemptionSheetIOS()

getStorefrontIOS()

Gets the App Store storefront information.

suspend fun getStorefrontIOS(): String

Android-Specific Methods

acknowledgePurchaseAndroid()

Acknowledges a purchase (Android only).

suspend fun acknowledgePurchaseAndroid(purchaseToken: String)

Important: Subscriptions must be acknowledged within 3 days or they will be refunded.

consumePurchaseAndroid()

Consumes a purchase (Android only).

suspend fun consumePurchaseAndroid(purchaseToken: String)

Warning: Only use for consumable products. Never consume subscriptions.

deepLinkToSubscriptions()

Opens the Google Play subscription management page.

suspend fun deepLinkToSubscriptions(options: DeepLinkOptions)

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

kmpIapInstance.deepLinkToSubscriptions(
DeepLinkOptions(skuAndroid = "premium_monthly")
)

Subscription Management

getActiveSubscriptions()

Gets all active subscriptions with detailed information.

suspend fun getActiveSubscriptions(subscriptionIds: List<String>? = null): List<ActiveSubscription>

Parameters:

  • subscriptionIds - Optional list of subscription IDs to check. If null, returns all active subscriptions

Returns: List of active subscriptions with platform-specific details

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

// Get all active subscriptions
val allActiveSubscriptions = kmpIapInstance.getActiveSubscriptions()

// Get specific subscriptions
val premiumSubscriptions = kmpIapInstance.getActiveSubscriptions(
listOf("premium_monthly", "premium_yearly")
)

premiumSubscriptions.forEach { subscription ->
println("Product: \${subscription.productId}")
println("Active: \${subscription.isActive}")

// iOS-specific information
subscription.expirationDateIOS?.let { expDate ->
println("Expires: \${Instant.fromEpochMilliseconds(expDate)}")
}
subscription.environmentIOS?.let { env ->
println("Environment: $env") // "Sandbox" or "Production"
}
subscription.daysUntilExpirationIOS?.let { days ->
println("Days until expiration: $days")
}

// Android-specific information
subscription.autoRenewingAndroid?.let { autoRenew ->
println("Auto-renewing: $autoRenew")
}

// Cross-platform
if (subscription.willExpireSoon == true) {
println("⚠️ Subscription expires soon!")
}
}

Platform Differences:

  • iOS: Provides expirationDateIOS, environmentIOS, daysUntilExpirationIOS
  • Android: Provides autoRenewingAndroid status
  • Cross-platform: willExpireSoon (true if expiring within 7 days)

hasActiveSubscriptions()

Checks if the user has any active subscriptions.

suspend fun hasActiveSubscriptions(subscriptionIds: List<String>? = null): Boolean

Parameters:

  • subscriptionIds - Optional list of subscription IDs to check. If null, checks all subscriptions

Returns: true if the user has at least one active subscription, false otherwise

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

// Check if user has any active subscriptions
val hasAnySubscription = kmpIapInstance.hasActiveSubscriptions()
if (hasAnySubscription) {
println("User has active subscriptions")
showPremiumFeatures()
} else {
showSubscriptionOffer()
}

// Check specific subscription types
val hasPremium = kmpIapInstance.hasActiveSubscriptions(
listOf("premium_monthly", "premium_yearly")
)
if (hasPremium) {
enablePremiumFeatures()
}

Use Cases:

  • Feature gating based on subscription status
  • Showing/hiding subscription offers
  • Quick subscription status checks without detailed information

Validation

validateReceipt()

Validates a purchase receipt (server-side validation recommended).

suspend fun validateReceipt(options: ValidationOptions): ValidationResult

Parameters:

  • options - Validation options including receipt data

Returns: Validation result with status

Note: Client-side validation is not secure. Always validate receipts on your server.

isPurchaseValid()

Quick client-side purchase validation.

suspend fun isPurchaseValid(purchase: Purchase): Boolean

Returns: true if purchase appears valid

Example:

import io.github.hyochan.kmpiap.kmpIapInstance

val isValid = kmpIapInstance.isPurchaseValid(purchase)
if (isValid) {
// Proceed with server validation
validateOnServer(purchase)
}

Utility Methods

getStore()

Gets the current store type.

fun getStore(): Store

Returns: Store enum (APP_STORE, PLAY_STORE, AMAZON, or NONE)

canMakePayments()

Checks if the device can make payments.

suspend fun canMakePayments(): Boolean

Returns: true if payments are available

getVersion()

Gets the library version.

fun getVersion(): String

Returns: Version string (e.g., "KMP-IAP v1.0.0 (Android)")

Best Practices

  1. Always handle errors: Use try-catch blocks or collect error flows
  2. Finish transactions: Always call finishTransaction() after delivering content
  3. Check acknowledgment: For Android, check if subscriptions are already acknowledged before re-acknowledging
  4. Monitor connection: Check connection status before operations
  5. Use proper product types: Specify ProductType.INAPP or ProductType.SUBS correctly
  6. Never consume subscriptions: Only acknowledge subscriptions, never consume them
  7. Validate on server: Always perform receipt validation on your server

Error Handling

All methods can throw PurchaseError with specific error codes:

import io.github.hyochan.kmpiap.kmpIapInstance

try {
val purchase = kmpIapInstance.requestPurchase(request)
} catch (e: PurchaseError) {
when (e.code) {
ErrorCode.E_USER_CANCELLED.name -> handleCancellation()
ErrorCode.E_ITEM_UNAVAILABLE.name -> handleUnavailable()
ErrorCode.E_NETWORK_ERROR.name -> handleNetworkError()
else -> handleGenericError(e)
}
}

See Also