πŸš€ ShipToStore

iOS publishing guide Β· 6 steps Β· ~3h 45m

πŸ’° In-App Purchases & Subscriptions

IAP products, the implementation layer, and the compliance rules that cause most monetization rejections.

Create IAP products in App Store Connect

30 min

In App Store Connect β†’ your app β†’ Monetization choose In-App Purchases or Subscriptions and create each product:

  • Consumable β€” used up and re-buyable (coins, credits).
  • Non-consumable β€” bought once, owned forever (lifetime unlock, pro features).
  • Auto-renewable subscription β€” recurring; must live inside a Subscription Group (create the group first; users can switch between products within a group, which is how upgrades/downgrades work).
  • Non-renewing subscription β€” fixed-duration access you manage yourself (rare; you handle expiry and cross-device sync).

Name product IDs in a stable scheme you'll never change, e.g. pro_monthly, pro_yearly, coins_500 β€” product IDs are permanent and can never be reused, even after deletion.

⚠️ Pitfall

If products are stuck at 'Missing Metadata' with everything seemingly filled in, the usual missing piece is the Paid Apps Agreement / banking / tax gate from Phase 3 β€” not the product form.

Fill product metadata: price, localization, review screenshot

25 min

Each product needs, before it can be submitted:

  • Reference name β€” internal only (shows in reports).
  • Product ID β€” e.g. pro_monthly (permanent!).
  • Price β€” pick a price point; subscriptions also choose duration (and optional intro offers / free trials).
  • Localized display name + description β€” what users see on the payment sheet, in at least your primary language. Keep the display name short ('Pro Monthly').
  • Review screenshot β€” a screenshot of your purchase UI/paywall showing this product. It's only for the reviewer, not the store. A simulator screenshot of the paywall is fine β€” but it must exist or the product can't be submitted.
  • Review notes β€” optional; explain anything odd (e.g. 'Pro unlocks the Stats tab').

The first IAP for an app must be submitted together with an app version; later products can be submitted on their own.

Choose your IAP implementation: RevenueCat vs StoreKit 2

20 min

Two solid paths:

RevenueCat (recommended default)

  • Wraps StoreKit and handles receipt validation, restore, cross-device entitlement status, renewals, refunds, and webhooks β€” the edge cases that eat weeks.
  • Free under $2.5K monthly tracked revenue, ~1% after.
  • SDKs for every stack: react-native-purchases, purchases-flutter, native Swift.
  • Gives you a server-side source of truth and analytics from day one.

StoreKit 2 direct

  • No fee, no third party, modern async/await API (Product.purchase(), Transaction.currentEntitlements).
  • You own: server-side verification (App Store Server API / JWS transaction verification), restore logic, subscription-status edge cases (billing retry, grace period), and cross-platform entitlement sync if you ever ship Android.
  • Right choice if you're native-only, single-platform, and allergic to dependencies.

Also worth knowing: Adapty and Qonversion (RevenueCat alternatives), Superwall (paywall A/B-testing layer that pairs with RevenueCat). For a first app: RevenueCat unless you have a reason otherwise.

Set up RevenueCat (if chosen)

1h
  1. Create a project at app.revenuecat.com β†’ add an App Store app with your Bundle ID.
  2. Generate an App Store Connect API key (Users and Access β†’ Integrations β†’ App Store Connect API, role App Manager) and an In-App Purchase key; upload them to RevenueCat so it can validate purchases and read product/subscription state.
  3. Define an Entitlement (e.g. pro) β€” the abstract 'thing the user has'.
  4. Attach your App Store products (pro_monthly, pro_yearly) to the entitlement.
  5. Create an Offering (e.g. default) with Packages (monthly/annual) β€” your paywall fetches the current offering so you can change products without an app update.
  6. Integrate the SDK and check entitlement status at launch.
npx expo install react-native-purchases
# bare RN: npm i react-native-purchases && cd ios && pod install
flutter pub add purchases_flutter
import Purchases from 'react-native-purchases';

Purchases.configure({ apiKey: '<public_apple_api_key>' });
const info = await Purchases.getCustomerInfo();
const isPro = info.entitlements.active['pro'] !== undefined;

Pass the mandatory IAP compliance checklist

45 min

Before submitting, verify every box:

  • Restore Purchases button is visible (paywall and/or settings). RevenueCat: Purchases.restorePurchases(). Missing restore is a guaranteed rejection.
  • Paywall shows the full terms: price, billing period, and that it auto-renews, plus links to your privacy policy and Terms of Use (auto-renewable subs require a Terms of Use link β€” Apple's standard EULA link is acceptable).
  • Digital goods use IAP β€” no Stripe/PayPal/web-checkout links for digital content, including inside webviews (Guideline 3.1.1).
  • Physical goods/services must NOT use IAP β€” use Stripe/Apple Pay for those instead.
  • ⏱️ External purchase links are policy-in-motion: since the Epic v. Apple injunction, US storefront apps have gained limited rights to link out to external payment β€” but terms, disclosure sheets, and commissions have changed repeatedly. Check Apple's current policy before building anything on external links.

⚠️ Pitfall

Rejections for 3.1.1 often come from incidental leaks: a marketing webview footer with a 'Subscribe' link, an email-confirmation screen pointing at web checkout. Audit every webview and link in the binary.

Test purchases in the sandbox

45 min

Never submit untested IAP β€” reviewers buy your products.

Sandbox tester route (closest to production):

  1. App Store Connect β†’ Users and Access β†’ Sandbox β†’ Test Accounts β†’ + β€” create a tester with an email that is not an existing Apple ID (plus-addressing works: you+sandbox1@gmail.com).
  2. On a real device: Settings β†’ App Store β†’ Sandbox Account (sign in there, not in the main Apple ID slot).
  3. Run a development build and purchase β€” the sheet shows [Environment: Sandbox] and no real money moves.

StoreKit Configuration file route (fastest iteration):

  • Xcode: File β†’ New β†’ File… β†’ StoreKit Configuration File, optionally synced from App Store Connect; select it in the scheme (Product β†’ Scheme β†’ Edit Scheme… β†’ Run β†’ Options β†’ StoreKit Configuration). Works in the simulator, supports clock-acceleration and refund simulation.

Know the quirks: sandbox subscriptions renew on an accelerated schedule (e.g. 1 month β†’ 5 minutes), and TestFlight purchases are free and behave sandbox-like β€” warn your beta testers.

← Metadata & assetsBuild, upload & processing β†’

Track this interactively

Check off steps, skip what doesn't apply, and pick up where you left off.

Open the checklist β€” free