Sanjeev SinghBuilding Software Since 2016
← All field notes
Mobile apps7 min read

Flutter In-App Purchases and Subscriptions: The Complete Guide.

A complete guide to Flutter in-app purchase subscriptions on iOS and Android, covering StoreKit 2, Google Play Billing, server validation, restores and testing.

  • Flutter
  • In-app purchases
  • Subscriptions
  • StoreKit 2

To add subscriptions to a Flutter app, use the official in_app_purchase plugin (StoreKit on iOS, Google Play Billing on Android) or a service such as RevenueCat, and make your server the source of truth: the app sends each purchase to your backend, the backend verifies it with Apple or Google, stores the entitlement and listens for renewals, cancellations and refunds through store notifications. Most subscription bugs come from trusting the phone instead of the server.

This guide walks through the whole setup, the bugs that catch most teams, and how to test before store review.

What are the main parts of a subscription system?

Part Where it lives Job
Store products App Store Connect and Google Play Console Define subscriptions, prices, trials and groups
Purchase flow Flutter app Show products, start purchases, listen for updates
Verification Your backend Confirm each purchase with Apple or Google
Entitlements Your database Record who has access to what, and until when
Notifications Your backend Receive renewals, cancellations, refunds and billing issues
Restore Flutter app and backend Give access back on a new phone

Which Flutter package should I use?

in_app_purchase is the official Flutter plugin, maintained by the Flutter team on pub.dev. It wraps StoreKit on iOS and the Google Play Billing Library on Android. It gives you full control and no extra fees, but you build verification, entitlements and notification handling yourself.

RevenueCat is a hosted service with a Flutter SDK. It handles receipt verification, entitlements, cross-platform status and analytics, and charges a share of revenue above a free tier. It is a reasonable choice for teams that want subscriptions working quickly without building backend logic.

Neither is wrong. If you already run a backend and want full control of user data, the official plugin plus your own server is a good fit. If you have no backend, RevenueCat saves a lot of work.

Comparison of the official in_app_purchase plugin, which gives full control with no fees but needs your own backend, and the RevenueCat SDK, which handles verification for a revenue share and suits apps with no backend

How does the purchase flow work in the app?

The core pattern with in_app_purchase is to listen to the purchase stream as soon as the app starts, not only when a purchase button is tapped, because purchases can complete later or be restored.

final iap = InAppPurchase.instance;

void initPurchases() {
  iap.purchaseStream.listen((purchases) async {
    for (final p in purchases) {
      if (p.status == PurchaseStatus.purchased ||
          p.status == PurchaseStatus.restored) {
        final ok = await api.verifyPurchase(p.verificationData); // your backend
        if (ok) await refreshEntitlements();
      }
      if (p.pendingCompletePurchase) {
        await iap.completePurchase(p);
      }
    }
  });
}

Key points:

  1. Load products with queryProductDetails using the IDs from the store consoles.
  2. Start a subscription with buyNonConsumable and the chosen product.
  3. Verify on the server before granting access.
  4. Complete the purchase. On Android, purchases that aren’t acknowledged within three days are automatically refunded.
  5. Handle pending purchases. Some payment methods take time; show a “processing” state instead of an error.

How do I verify purchases on the server?

The app sends the purchase’s verification data to your backend. The backend then:

  • For Apple: validates the transaction using the App Store Server API, and stores the original transaction ID so later renewals and restores link to the same customer.
  • For Google: calls the Google Play Developer API to check the purchase token, then acknowledges it if the app hasn’t.

Then it writes an entitlement record: user, product, expiry date and status. The app asks your backend for the user’s entitlements, not the store.

Server verification flow: the Flutter app sends purchase data to your backend, the backend verifies it with the Apple or Google store API and writes an entitlement with user and expiry, and the app reads access from the backend, not the store

How do I handle renewals, cancellations and refunds?

Subscriptions change without the app being open. A renewal happens at night, a user cancels in their phone settings, a refund is granted. Your backend must hear about these:

On each notification, update the entitlement. Without this, cancelled users keep premium access until they next open the app, or paying users lose it after a renewal.

Store notification flow: renewals, cancellations, refunds and billing issues arrive through Apple server notifications and Google real-time notifications via Pub/Sub, and the backend updates the entitlement each time

What are the most common subscription bugs?

Symptom Usual cause Fix
User paid but still sees the paywall App checks local state, or verification failed silently Server as source of truth, retry verification
Restore Purchases does nothing No restore call, or restored purchases not verified Call restorePurchases and verify each result
Cancelled users keep premium No store notifications Handle Apple and Google notifications
Android refunds after three days Purchase never acknowledged Complete or acknowledge every purchase
Trial offered twice Eligibility not checked Let the store decide eligibility; don’t fake trials in the app
Access lost on a new phone Entitlement tied to the device Tie entitlements to the user account

What do Apple and Google require on the paywall?

Store review is strict about subscriptions. Your paywall should show the price, the billing period and the trial terms clearly before purchase, link to your terms and privacy policy, and include a working Restore Purchases option on iOS. Apps with sign-up must also offer account deletion inside the app on iOS. Missing any of these is a common rejection reason.

Paywall checklist: show the price, billing period, trial terms and terms and privacy links before purchase, and on iOS include Restore Purchases and in-app account deletion, since missing one is a common rejection

How do I test subscriptions?

  1. iOS: use a StoreKit configuration file for fast local testing in Xcode, then sandbox testers in App Store Connect. Sandbox renewals are accelerated, so a monthly subscription renews every few minutes.
  2. Android: add license testers in Play Console and publish to an internal testing track. Test subscriptions also renew on an accelerated schedule.
  3. Test the full cycle: buy, renew, cancel, expire, restore on a second device, refund and a failed payment.
  4. Test your backend notifications in both sandbox and production configurations.

How I build subscriptions in Flutter

On CRUD Review, the Flutter app is live on the App Store and Google Play with subscriptions and a three-day free trial through both stores’ billing. The backend, built with Node.js and PostgreSQL, is the source of truth for every user’s access, and the app’s state is managed with BLoC, so each screen shows exactly what the backend says the user owns.

If you’re planning a new app, how much a Flutter app costs gives realistic budgets, including the store commission on subscriptions. If you’re still deciding on platform, start with web app or mobile app.

I build and fix in-app purchases and subscriptions in Flutter apps on StoreKit 2 and Google Play Billing, with server verification and store notifications. Tell me what’s going wrong and get a fixed quote within 24 hours.

Sanjeev Singh

The end · Written by

Sanjeev Singh

Full-stack & mobile developer

Building web apps, Flutter apps, Shopify and WordPress stores and AI automations since 2016. Since 2020 I’ve led digital platforms at Datadrone, and I write these notes to help founders make the first decision right.

Building something?

Good decisions compound.Let’s make the first one.

[email protected]

Local time (India): --:--

Start a conversation

What are you building?

What can I help with?

No mailing list. No sales handoff. Your details come directly to me.

Pick a day

Sanjeev Singh × you

30 minutes on Google Meet