Skip to main content

1.0.0 - Built-in Purchase Verification (aka Receipt Validation)

ยท 4 min read
Hyo

IAPKit Integration

KMP-IAP 1.0.0 brings built-in purchase verification (aka receipt validation) powered by IAPKit. Now you can verify purchases with enterprise-grade backend validation using a single API call-no server setup required.

Why IAPKit?โ€‹

Purchase verification is critical for any production IAP implementation. Without proper server-side validation, your app is vulnerable to receipt tampering, replay attacks, and fraudulent transactions. IAPKit solves this with a battle-tested backend infrastructure. Sign up at iapkit.com to get your API key and start verifying purchases in minutes.

Key Benefitsโ€‹

BenefitDescription
๐Ÿ”’Security FirstServer-side validation that prevents fraud, receipt tampering, and token reuse. Far more secure than client-only verification.
๐Ÿ”—Unified APISingle endpoint for both Apple App Store and Google Play. No separate validation logic needed.
โšกZero InfrastructureNo server setup required. IAPKit handles Apple and Google API complexity for you.
๐ŸญProduction ReadyEnterprise-grade reliability with comprehensive error handling and detailed responses.

Getting Startedโ€‹

Use the new verifyPurchaseWithProvider API to verify purchases through IAPKit:

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

val result = kmpIapInstance.verifyPurchaseWithProvider(
VerifyPurchaseWithProviderProps(
provider = PurchaseVerificationProvider.Iapkit,
iapkit = RequestVerifyPurchaseWithIapkitProps(
apiKey = "your-iapkit-api-key",
apple = RequestVerifyPurchaseWithIapkitAppleProps(jws = purchase.jws),
google = RequestVerifyPurchaseWithIapkitGoogleProps(
purchaseToken = purchase.purchaseToken
)
)
)
)

if (result.iapkit?.isValid == true) {
// Purchase verified - grant entitlement
println("Purchase state: ${result.iapkit?.state}")
println("Store: ${result.iapkit?.store}")
}

The API returns a unified VerifyPurchaseWithProviderResult with:

  • isValid - Whether the purchase passed verification
  • state - Detailed purchase state (entitled, pending, expired, canceled, etc.)
  • store - The store where the purchase was made (apple, google, horizon)

Highlightsโ€‹

  • IAPKit Integration - Built-in purchase verification (aka receipt validation) with enterprise-grade backend
  • OpenIAP 1.3.0 - Updated to openiap-apple v1.3.0, openiap-google v1.3.10, and openiap-gql v1.3.0
  • New IapStore Type - Unified store identification: Unknown, Apple, Google, Horizon
  • Enhanced Purchase Types - New store field on Purchase (replaces deprecated platform)

Type System Updatesโ€‹

This release brings important type refinements aligned with the OpenIAP 1.3.0 specification:

New IapStore Enumโ€‹

enum class IapStore(val rawValue: String) {
Unknown("unknown"),
Apple("apple"),
Google("google"),
Horizon("horizon")
}

Updated Purchase Typesโ€‹

All purchase types now include a store field for unified store identification:

val purchase = PurchaseIOS(
productId = "premium",
transactionId = "12345",
store = IapStore.Apple, // NEW - unified store identification
// ...other fields
)

Request Parametersโ€‹

Request parameters now support apple/google keys alongside the legacy ios/android:

// New (recommended)
kmpIapInstance.requestPurchase(
RequestPurchaseProps(
apple = RequestPurchasePropsApple(sku = productId),
google = RequestPurchasePropsGoogle(skus = listOf(productId))
)
)

// Legacy (still supported but deprecated)
kmpIapInstance.requestPurchase(
RequestPurchaseProps(
ios = RequestPurchasePropsIos(sku = productId),
android = RequestPurchasePropsAndroid(skus = listOf(productId))
)
)

Migration from 1.0.0-rcโ€‹

Store Fieldโ€‹

If you're checking platform-specific purchases, consider using the new store field:

// Before (1.0.0-rc)
if (purchase.platform == IapPlatform.Ios) {
// Handle iOS purchase
}

// After (1.0.0)
if (purchase.store == IapStore.Apple) {
// Handle Apple purchase
}

Verification Integrationโ€‹

Add purchase verification to your existing purchase flow:

import io.github.hyochan.kmpiap.kmpIapInstance
import io.github.hyochan.kmpiap.openiap.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

// After successful purchase
scope.launch {
kmpIapInstance.purchaseUpdatedListener.collect { purchase ->
// Verify the purchase
val verification = kmpIapInstance.verifyPurchaseWithProvider(
VerifyPurchaseWithProviderProps(
provider = PurchaseVerificationProvider.Iapkit,
iapkit = RequestVerifyPurchaseWithIapkitProps(
apiKey = "your-api-key",
apple = RequestVerifyPurchaseWithIapkitAppleProps(
jws = (purchase as? PurchaseIOS)?.jwsRepresentationIOS
),
google = RequestVerifyPurchaseWithIapkitGoogleProps(
purchaseToken = purchase.purchaseToken
)
)
)
)

if (verification.iapkit?.isValid == true) {
// Grant entitlement
grantPurchase(purchase.productId)

// Finish the transaction
kmpIapInstance.finishTransaction(
purchase = purchase,
isConsumable = true
)
}
}
}

IAPKit Purchase Statesโ€‹

The IapkitPurchaseState enum provides detailed purchase status:

StateDescription
EntitledPurchase is valid and user has access
PendingAcknowledgmentPurchase needs acknowledgment (Android)
PendingPurchase is being processed
CanceledPurchase was canceled
ExpiredSubscription has expired
ReadyToConsumeConsumable ready to be consumed
ConsumedConsumable has been consumed
InauthenticPurchase failed verification (potential fraud)
UnknownUnknown state

Installationโ€‹

Gradle (Kotlin DSL)โ€‹

// shared/build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.hyochan:kmp-iap:1.0.0")
}
}
}

Gradle (Groovy)โ€‹

// shared/build.gradle
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'io.github.hyochan:kmp-iap:1.0.0'
}
}
}
}

Version Catalog (libs.versions.toml)โ€‹

[versions]
kmp-iap = "1.0.0"

[libraries]
kmp-iap = { module = "io.github.hyochan:kmp-iap", version.ref = "kmp-iap" }

Then sync your Gradle project.

Referencesโ€‹

Questions or feedback? Reach out via GitHub Issues or join our Slack community.