In-App Purchases via RevenueCat — a 30-Minute Intro

A practical 2026 intro to setting up in-app purchases for an indie iOS app using RevenueCat — what it gives you, what it costs, and when to use it instead of raw StoreKit.

RevenueCat is the easiest way to ship in-app purchases on iOS without spending a week on receipt validation, restore flows, and subscription edge cases. Here is when to use it, when not to, and the 30-minute setup path.

Background

Apple’s StoreKit handles the technical purchase flow, but everything around it — receipt validation, server-side subscription state, restore purchases, cross-platform user identity, analytics — is on you. RevenueCat is a hosted service that handles all of this for one fee structure, and it works for solo indie devs. The trade-off: you depend on a third party. For most indie apps in 2026, that trade is worth it; for very high-revenue apps, going direct to StoreKit eventually pays off.

How to tell

  • You are adding subscriptions or non-consumable purchases to an indie app.
  • You do not want to write your own receipt validation server.
  • You expect to support multiple platforms eventually (iOS + Android + web).
  • Your revenue is below RevenueCat’s free tier ($2,500/month) — or you can absorb the fee.

Quick verdict

Use RevenueCat when you want to ship IAP this week and not own the infrastructure. Skip it only if you have specific compliance needs, very high revenue, or strong server engineering already in place.

Step by step

  1. Create products in App Store Connect first. Pick product IDs you will not regret (you cannot rename them):
pro_monthly       — auto-renewable subscription, $4.99/month
pro_yearly        — auto-renewable subscription, $39.99/year
pro_lifetime      — non-consumable,               $79.99

Set localized title + description for each in App Store Connect → My Apps → Features → In-App Purchases.

  1. In RevenueCat → Project Settings → API Keys, grab the iOS public SDK key. Paste your App Store Connect issuer/key ID/private key under Project Settings → Apple App Store.

  2. Configure Entitlements and Offerings in RevenueCat:

Entitlement: "pro"             (what the user has access to)
   ↓ attached to ↓
Products:    pro_monthly, pro_yearly, pro_lifetime

Offering:   "default"          (what the paywall shows)
   ↓ contains ↓
Packages:    $rc_monthly  → pro_monthly
             $rc_annual   → pro_yearly
             $rc_lifetime → pro_lifetime

This indirection lets you change which products appear without an app update.

  1. Add the SDK via SPM (https://github.com/RevenueCat/purchases-ios) and initialize on launch:
// AppDelegate.swift or App.swift
import RevenueCat

@main
struct MyApp: App {
    init() {
        Purchases.logLevel = .info
        Purchases.configure(withAPIKey: "appl_XXXXXXXXXXXXXXXX")
    }
    var body: some Scene { WindowGroup { ContentView() } }
}
  1. Fetch offerings and present a paywall:
import RevenueCat

func loadPaywall() async throws -> Offering? {
    let offerings = try await Purchases.shared.offerings()
    return offerings.current   // matches the "current" offering in dashboard
}

func purchase(_ package: Package) async throws -> Bool {
    let result = try await Purchases.shared.purchase(package: package)
    let hasPro = result.customerInfo.entitlements["pro"]?.isActive == true
    return hasPro
}
  1. Gate features on the entitlement, not the product ID — that way new products just work:
func userHasPro() async -> Bool {
    guard let info = try? await Purchases.shared.customerInfo() else { return false }
    return info.entitlements["pro"]?.isActive == true
}

// SwiftUI usage
if await userHasPro() {
    AdvancedFeatureView()
} else {
    PaywallView()
}
  1. Restore Purchases: Apple requires this button (Rule 3.1.1). RevenueCat is one call:
func restore() async throws -> Bool {
    let info = try await Purchases.shared.restorePurchases()
    return info.entitlements["pro"]?.isActive == true
}
  1. Backend gating — listen to RevenueCat webhooks so your server knows about cancellations and renewals:
// Vercel / Cloudflare Worker — minimal webhook receiver
export async function POST(req: Request) {
  const sig = req.headers.get('Authorization');
  if (sig !== `Bearer ${process.env.RC_WEBHOOK_SECRET}`) {
    return new Response('unauthorized', { status: 401 });
  }
  const event = await req.json();
  // INITIAL_PURCHASE | RENEWAL | CANCELLATION | EXPIRATION | BILLING_ISSUE | ...
  await updateUserEntitlement(event.event.app_user_id, event.event.type);
  return new Response('ok');
}
  1. Sandbox QA before submission:
App Store Connect → Users and Access → Sandbox → Testers → +
  • create test@example.com  (use a new email, not your Apple ID)
  • set Region = United States
Device → Settings → App Store → Sandbox Account → sign in
TestFlight → install build → buy each package → tap Restore on a fresh device

RevenueCat dashboard → Activity should show each sandbox transaction within seconds — Apple’s own reporting lags hours.

Common pitfalls

  • Hard-coding product IDs in your app. Use RevenueCat’s Offerings so you can change products without an app update.
  • Skipping the Restore Purchases button. Apple’s rule 3.1.1 requires it for non-consumable and subscription products.
  • Forgetting to handle the “pending purchase” state (parental approval, payment issues). Test with sandbox parental controls.
  • Trusting client-side entitlement checks alone if you have a backend. Always verify subscription state server-side via webhooks for any high-value access.
  • Not setting up sandbox testers in App Store Connect before testing. Real Apple IDs will be charged real money.
  • Treating RevenueCat’s free tier as unlimited. Above $2,500/month tracked revenue, you pay a percentage — model this into pricing.

Who this is for

Solo developers and small teams shipping their first or second IAP-enabled app who want to avoid backend work.

When to skip this

Apps with strict no-third-party-data policies, very high-revenue apps where percentage fees become significant, or developers who already have strong subscription infrastructure.

FAQ

  • How much does RevenueCat cost?: Free up to $2,500/month in tracked revenue. Above that, 1% of tracked revenue. Compare against the engineering time you save.
  • Does RevenueCat work with TestFlight?: Yes — TestFlight builds use the StoreKit sandbox, and RevenueCat’s SDK handles sandbox transactions correctly.
  • What about Android?: RevenueCat supports Google Play purchases with the same SDK and a unified API. Cross-platform identity becomes trivial.
  • Can I migrate off RevenueCat later?: Yes, but it is non-trivial. You can export transaction data and rebuild on direct StoreKit, but plan for several days of work.

Tags: #Indie dev #App Store #IAP #RevenueCat #Monetization