Integrating In-App Purchases on iOS and Android

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

Introduction

Monetizing your mobile app through in-app purchases (IAP) is a powerful way to generate sustainable revenue, offer premium features, and enhance user engagement. Whether you’re unlocking new content, offering subscriptions, or selling consumable items, implementing IAP correctly on both iOS and Android ensures a seamless user experience and maximizes conversion. In this comprehensive guide, you’ll learn how to set up in-app purchases from scratch: configuring your products in App Store Connect and Google Play Console, integrating StoreKit (iOS) and BillingClient (Android), handling transactions securely, and following best practices for testing and compliance. By the end, you’ll be equipped to deploy robust IAP flows that delight users and stand up to App Store and Play Store review.

Main Body

Understanding In-App Purchase Types

Before writing any code, it’s crucial to know the four primary purchase types each platform offers:

  • Consumable: Items that users buy, use, and can repurchase (e.g., coins, hints).
  • Non-consumable: One-time purchases that remain available permanently (e.g., premium upgrade, ad removal).
  • Auto-renewable Subscriptions: Recurring charges granting ongoing access (e.g., monthly magazine).
  • Non-renewing Subscriptions: Time-limited access without automatic renewal (e.g., 6-month access).

Analogy: Think of consumables like arcade tokens you spend and need to buy again, non-consumables like unlocking the “full game,” and subscriptions like your monthly streaming service.

iOS Integration with StoreKit

Configuring Products in App Store Connect

  1. Log in to App Store Connect and select your app.
  2. Go to Features ▶️ In-App Purchases and click “+” to add a new product.
  3. Choose the Type (consumable, non-consumable, subscription, etc.).
  4. Enter the Reference Name, Product ID (e.g., com.yourapp.premium), and Pricing.
  5. Save and Submit for review.

Pro Tip: Use a consistent naming convention for Product IDs (reverse-domain style) to avoid collisions.

Implementing StoreKit (Swift)

Importing and Requesting Products

swiftCopyEditimport StoreKit

class IAPManager: NSObject {
  static let shared = IAPManager()
  private var products: [SKProduct] = []
  
  func fetchProducts() {
    let ids: Set<String> = ["com.yourapp.premium", "com.yourapp.coins100"]
    let request = SKProductsRequest(productIdentifiers: ids)
    request.delegate = self
    request.start()
  }
}

extension IAPManager: SKProductsRequestDelegate {
  func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    products = response.products
    // Notify UI to display available products
  }
}

Purchasing a Product

swiftCopyEditfunc purchase(product: SKProduct) {
  guard SKPaymentQueue.canMakePayments() else { return }
  let payment = SKPayment(product: product)
  SKPaymentQueue.default().add(self)
  SKPaymentQueue.default().add(payment)
}

extension IAPManager: SKPaymentTransactionObserver {
  func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for tx in transactions {
      switch tx.transactionState {
      case .purchased:
        // Unlock content
        SKPaymentQueue.default().finishTransaction(tx)
      case .failed:
        // Handle error
        SKPaymentQueue.default().finishTransaction(tx)
      case .restored:
        // Restore purchases
        SKPaymentQueue.default().finishTransaction(tx)
      default: break
      }
    }
  }
}

Expert Insight: Always call finishTransaction(_:) to clear transactions and avoid duplicate callbacks.

Testing Your iOS IAP Flow

  • Sandbox Tester Accounts: Create testers in App Store Connect under Users and Access ▶️ Sandbox Testers.
  • Reset Transactions: In the sandbox environment, use Settings ▶️ Developer ▶️ Reset All Transactions.
  • Use StoreKit Testing in Xcode: Define a local JSON file to simulate IAP responses without hitting Apple servers.

Android Integration with Google Play Billing

Configuring Products in Google Play Console

  1. Open Google Play Console, select your app.
  2. Navigate to Monetize ▶️ Products and choose In-app products or Subscriptions.
  3. Click Create product, select Managed product or Subscription, and fill out Product ID, Title, Description, and Price.
  4. Activate the product.

Pro Tip: Activate your products only when you’re ready to test or release; inactive products won’t appear in the test flow.

Implementing BillingClient (Kotlin)

Adding Dependencies

groovyCopyEditdependencies {
  implementation "com.android.billingclient:billing:6.0.1"
}

Setting Up the BillingClient

kotlinCopyEditclass BillingManager(context: Context) : PurchasesUpdatedListener {
  private val billingClient = BillingClient.newBuilder(context)
    .setListener(this)
    .enablePendingPurchases()
    .build()

  fun startConnection() {
    billingClient.startConnection(object : BillingClientStateListener {
      override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
          queryAvailableProducts()
        }
      }
      override fun onBillingServiceDisconnected() { /* Retry logic */ }
    })
  }
}

Querying and Purchasing Products

kotlinCopyEditfun queryAvailableProducts() {
  val params = QueryProductDetailsParams.newBuilder()
    .setProductList(listOf(
      QueryProductDetailsParams.Product.newBuilder()
        .setProductId("premium")
        .setProductType(BillingClient.ProductType.INAPP)
        .build()
    ))
    .build()

  billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList ->
    // Display products in UI
  }
}

fun purchase(activity: Activity, productDetails: ProductDetails) {
  val billingFlowParams = BillingFlowParams.ProductDetailsParams.newBuilder()
    .setProductDetails(productDetails)
    .build()
  billingClient.launchBillingFlow(activity, billingFlowParams)
}

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList<Purchase>?) {
  if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
    for (purchase in purchases) handlePurchase(purchase)
  }
}

Expert Insight: Use enablePendingPurchases() to comply with Google’s new billing requirements for pending transactions.

Handling and Acknowledging Purchases

kotlinCopyEditfun handlePurchase(purchase: Purchase) {
  if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
    // Grant entitlement
    val acknowledgeParams = AcknowledgePurchaseParams.newBuilder()
      .setPurchaseToken(purchase.purchaseToken)
      .build()
    billingClient.acknowledgePurchase(acknowledgeParams) { result ->
      // Check result.responseCode
    }
  }
}
  • Acknowledge every purchase within 3 days to avoid automatic refunds.
  • Restore purchases by querying existing ones on startup:
kotlinCopyEditbillingClient.queryPurchasesAsync(
  QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build()
) { _, purchasesList -> purchasesList.forEach(::handlePurchase) }

Best Practices & Expert Tips

  • Secure Your Backend: Never rely solely on client-side verification; validate receipts/tokens on your server against Apple’s or Google’s servers.
  • Graceful Fallbacks: Detect IAP availability (SKPaymentQueue.canMakePayments() / BillingClient ready) and hide purchase UI if unavailable.
  • UX Considerations: Clearly label prices and benefits, handle failed transactions with user-friendly messages, and allow easy restoration of purchases.
  • Analytics & Monitoring: Track purchase events with your analytics platform (e.g., Firebase, Amplitude) to measure conversion funnels.
  • Compliance: Follow each store’s guidelines on subscriptions, free trials, and refund policies to avoid rejections.

Conclusion

Implementing in-app purchases on iOS and Android involves configuring products in your respective consoles, integrating StoreKit and BillingClient into your app, handling transactions securely, and rigorously testing the flow. By following the steps outlined—defining product types, writing purchase and restoration code, and adhering to best practices for security, UX, and analytics—you’ll deliver a polished, reliable IAP experience that drives revenue and satisfies both App Store and Play Store requirements. With this foundation, you’re ready to offer consumables, premium features, and subscriptions that keep users engaged and coming back for more.

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts