Implement In-App Purchases
Implement in-app purchases and billing — Mobile App Development.
Focus: implement in-app purchases and billing
Your app is finally polished, feature-complete, and users love it. But you're losing money every day because you haven't implemented in-app purchases (IAP) and billing. Without a reliable way to unlock premium features, subscriptions, or digital goods, your app is leaving revenue on the table — and worse, any rushed payment code can lead to chargebacks, security breaches, and angry users. In this lesson, you'll learn how to implement in-app purchases and billing the right way, from understanding the mental model to building a hands-on example that you can adapt to your own platform.
The Problem This Lesson Solves
Building an app is only half the battle. Once you've got an audience, you need to monetize it sustainably — and that means implementing in-app purchases and billing properly. The pain is real: developers who string together custom payment solutions or ignore platform billing rules often face:
- Security risks: Handling credit card data directly (without PCI compliance) turns your app into a liability.
- App store rejection: Apple's App Store and Google Play have strict policies — if you try to bypass their billing systems (like linking to external payment URLs or accepting payments outside the app), your app will be rejected or removed.
- Frustrating user experiences: Broken payment flows, unclear pricing, or no receipt validation can lead to refund requests and one-star reviews.
- Missed revenue: Subscriptions and consumables are a massive revenue stream — but only if you implement them correctly.
Without a solid grasp of IAP and billing, you're gambling with your app's future. This lesson gives you a trustworthy, step-by-step approach to monetization that keeps your app compliant and your users happy.
Core Concept / Mental Model
Think of in-app purchases as a transaction between three parties: you (the merchant), the app store (the intermediary), and the user. The app store doesn't just process payments — it acts as a trust broker. It verifies the user's identity, handles payment methods, and gives you a receipt that proves a purchase occurred. Your app's job is to unlock content only after you've verified that receipt.
A helpful analogy: IAP is like a toll booth on a highway. The toll booth (app store) checks the car's payment (user's funds), issues a ticket (receipt), and lets the car through. Your job is to check the ticket before opening the gate — if you open the gate without verifying, you're giving away free rides.
Key terms you'll encounter:
- Consumable: A one-time purchase that can be bought repeatedly (e.g., game currency, extra lives). Once consumed, it's gone.
- Non-consumable: A permanent purchase (e.g., unlocking a pro feature). It persists across devices.
- Subscription: Recurring billing (e.g., monthly premium access). The app store manages renewals.
- Receipt: A cryptographically signed proof of a purchase. Your app must validate it with the store's server to prevent fraud.
Here's the mental model for the purchase flow:
- User taps "Buy" → your app asks the store to start a purchase.
- Store shows its own payment sheet (you never handle credit cards).
- Store processes payment → returns a transaction with a receipt.
- Your app sends the receipt to your server (or to the store's verification endpoint).
- After validation, your server tells the app to unlock the content.
This separation — your app vs. the store's billing system — is what makes IAP secure and compliant.
How It Works Step by Step
Here's the single flow you'll implement across platforms (you'll see code in the next section):
- Initialize the billing client — connect to the app store's billing service (e.g., BillingClient on Android, StoreKit on iOS, or a cross-platform library like RevenueCat).
- Query products — get the list of products (consumable, non-consumable, subscriptions) with their prices from the store. Never hardcode prices — they can change dynamically.
- Launch a purchase flow — when a user taps a product, start the purchase. The store handles payment UI.
- Listen for purchase events — get notified when the purchase completes (or fails).
- Validate the receipt — send the receipt to a trusted server (yours or the store's) to verify its authenticity and check for expiry (for subscriptions).
- Unlock content — only after successful validation, grant the user access. For consumables, you must acknowledge the purchase after consuming it, or the store will auto-refund it.
- Persist entitlement — store the user's entitlements (e.g., in a database or local storage) so they regain access on app relaunch.
Let's break down the critical points:
- Never trust the client: A determined user can modify your app's memory or intercept network calls. Always validate receipts on a server.
- Handle pending purchases: Some purchases (especially in China) are deferred — the user pays later. Don't unlock content until you actually get a valid receipt.
- Acknowledge consumables: On Google Play, if you don't acknowledge a consumable within 3 days, it's automatically refunded. On iOS, you must call
finishTransaction.
Hands-On Walkthrough
Let's implement a simple subscription purchase using purchases (RevenueCat's Python client) — a popular cross-platform solution that handles store-specific complexities. We'll use Python because this track focuses on Python-based mobile development (e.g., Kivy or BeeWare), but the logic translates to any language.
First, install the library:
pip install purchases
Now, let's set up the client and fetch products:
from purchases import Purchases, PurchasesError
# Initialize the client with your public API key
purchases = Purchases(public_api_key="your_public_key")
# Fetch the product you've configured in your RevenueCat dashboard
product_id = "premium_subscription"
try:
product = purchases.get_products(product_identifier=product_id)[0]
print(f"Product: {product.title} | Price: {product.price_string} | Period: {product.subscription_period}")
except PurchasesError as e:
print(f"Failed to fetch product: {e}")
Expected output (on a device or emulator with test products):
Product: Premium Subscription | Price: $9.99/month | Period: P1M
Next, let's purchase the product. You'll need a listener for purchase events (simplified here):
from purchases import PruchaseResult
def on_purchase_completed(purchase_result):
if purchase_result.success:
# The purchase succeeded, now validate the receipt server-side
receipt = purchase_result.receipt
entitlements = verify_receipt_with_our_server(receipt) # implement this
if entitlements and "premium" in entitlements:
unlock_premium_features()
else:
print("Purchase failed or cancelled")
# Assuming user taps a button in your UI:
purchases.purchase_product(product_id, listener=on_purchase_completed)
Pro Tip: Always listen for purchase result in a handler that's registered early — on Android, if your activity is destroyed during the purchase flow (e.g., rotation), you'll get the result in the
onResumecallback. Use a framework that handles this for you.
Now, the most critical part — validating the receipt. Never just trust the client's success flag. Here's a minimal server-side validation for Google Play using RevenueCat (which proxies to Google's API):
from purchases import Purchases
# Server-side: use the secret API key (never put this in the app)
purchases = Purchases(secret_api_key="secret_key")
# The app sends you the app_user_id and product_id after purchase
app_user_id = "user_123"
product_id = "premium_subscription"
# Verify entitlement
customer_info = purchases.get_customer_info(app_user_id)
active_entitlements = customer_info.entitlements.active
if "premium" in active_entitlements:
print("User is entitled. Grant access.")
else:
print("No valid entitlement. Deny access.")
For a truly self-contained approach, you could use platform-specific APIs — Python isn't standard for Android/iOS native, but with BeeWare's Toga you'd call into native SDKs. However, RevenueCat (or purchases Flutter plugin) is the fastest path to production.
What success looks like: When you run this on a real device with a test account, the store's payment sheet appears, you tap "Confirm", and your code prints "Purchase succeeded" and then unlocks a premium screen.
Compare Options / When to Choose What
You have several ways to implement IAP. Here's a comparison:
| Approach | Pros | Cons | Best for |
|---|---|---|---|
| Platform-native APIs (StoreKit / Billing Library) | Direct control, no third-party dependency, full platform features | Platform-specific code, complex, need to learn both Apple/Google APIs | Apps with highly custom needs or strict compliance requirements |
| RevenueCat / purchases SDK | Cross-platform, handles validation, subscription management, analytics, easy webhooks | Adds a dependency, small monthly fee as you scale, may abstract away platform quirks | Most startups, MVPs, apps with subscriptions — it you want to move fast |
| Third-party payment processors (Stripe, PayPal) | Not allowed for in-app digital goods (violates store policy) | Risk of app rejection; only for physical goods or services outside the app | Selling physical products, or services used off-device (e.g., classes) |
| Self-hosted billing with StoreKit 2 / Google Play Billing (server-side validation) | Full control, no lock-in | Easiest to get wrong; you must implement secure receipt validation and renewal webhooks | Advanced teams that need complete ownership |
Decision guidance: - If you're building a cross-platform app (Flutter, React Native, or Python with Kivy), use RevenueCat to avoid doubling your billing code. - If you only target one platform and need deep customization, go native. - Always avoid non-store payment methods for digital content — it's against platform terms and will get you kicked out.
Troubleshooting & Edge Cases
- Purchase succeeded but receipt validation fails → Your server's signing key or certificate might be wrong. Check that you're using the correct store account (e.g., sandbox vs. production) and that your server time is synced. On iOS, sandbox receipts have different verify endpoints.
- User doesn't get content after purchase → Check if you're awaiting
on_purchase_completeto unlock. On Android, the purchase may be pending — handle thePENDINGstate and poll for updates. Also, verify your entitlement check is server-side, not just local. - Consumable auto-refunded → You forgot to
acknowledgethe purchase. For Google Play, callpurchases.acknowledge_purchaseafter consuming. On iOS, callfinishTransactionin the transaction observer. - Subscriptions not renewing → Ensure you've configured the product as a subscription in your store console and that you're handling renewal webhooks (e.g., RevenueCat webhooks) to update your server.
- App rejected during review → Either you included a hidden purchase method (like a URL to your website), or you don't use the sandbox/test account correctly. Always follow the review guidelines: use StoreKit's sandbox / Google Play's test track.
- Currency or price differences → Never hardcode prices; fetch from the store dynamically. On Apple, you must support regional pricing.
What You Learned & What's Next
In this lesson, you learned:
- The secure, compliant pattern for IAP: app → store → receipt validation → entitlement.
- How to implement IAP using a cross-platform SDK (RevenueCat) with Python.
- Why server-side receipt validation is non-negotiable for security and fraud prevention.
- The differences between consumables, non-consumables, and subscriptions, and how to handle each.
- How to troubleshoot common issues like pending purchases, failed validation, and auto-refunds.
The core idea is that app stores act as trusted intermediaries, and your job is to verify their receipts before granting access. You can now implement IAP in your own app, but billing is only one part of the monetization puzzle. The next lesson in this track — Store and manage user data — will teach you how to securely persist user entitlements and other sensitive information, so you can scale your monetization without leaking data or losing track of your users' preferences.
Practice recap
Now it's your turn: set up a RevenueCat test project, configure one consumable product, and implement a simple purchase flow that verifies a receipt on your server. Try to simulate a pending purchase by using a test account in a sandbox and observe your app's behavior. Next lesson will show you how to store that entitlement data securely — you'll need this foundation first.
Common mistakes
- Trusting client-side success callbacks without server validation — always verify the receipt server-side to prevent piracy and refund fraud.
- Forgetting to acknowledge consumable purchases on Google Play (within 3 days) or not calling finishTransaction on iOS, causing auto-refunds and lost revenue.
- Hardcoding product prices or IDs — they change across regions and test/production environments; always fetch products from the store dynamically.
- Handling pending purchases as failures — on Android, deferred payments are a valid state; poll or listen for actual completion
- Using non-store payment processors for digital goods, which violates Apple and Google policies and gets your app rejected.
Variations
- Use platform-native APIs directly (StoreKit 2 for iOS, Google Play Billing Library for Android) instead of RevenueCat, if you want full control and minimal dependencies.
- Implement server-side validation yourself (without third-party SDKs) using store webhooks and Apple's App Store Server API / Google's Play Developer API — more work but removes dependency.
- Adopt a subscription management service like RevenueCat or glassfy that handles pricing localization and analytics automatically, saving weeks of development.
Real-world use cases
- Fitness app selling monthly workout plan subscriptions with auto-renewal and trials, using revenuecat for cross-platform consistency.
- Gaming app offering 'gems' as consumable items with instant on-device usage, validated via googles' billing client to prevent hacks.
- News app with a premium tier: non-consumable lifetime upgrade purchased once, restoring on new devices via receipt validation.
Key takeaways
- Understand the IAP trust flow: app store issues receipts, and you must validate them on a server before unlocking content.
- Use consumables, non-consumables, and subscriptions appropriately — each has distinct lifecycle rules (e.g., acknowledgment).
- Cross-platform SDKs like RevenueCat dramatically simplify billing implementation and compliance.
- Never hardcode prices; always fetch live product details from the store.
- Handle pending purchases and test environments separately — don't treat them as successes or failures upfront.
- Implement server-side validation and maintain an entitlement database to support restores and multi-device access.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.