Flutter In-App Purchase Subscription Implementation

Master the official in_app_purchase plugin to implement subscriptions across iOS, Android, and macOS from a single codebase.

Understanding In-App Purchase Types

In-app purchases represent digital goods and services that users can purchase directly within mobile applications. The in_app_purchase plugin supports three distinct product categories, each requiring different implementation approaches and handling patterns. Understanding these categories is essential for selecting the appropriate monetization strategy for your Flutter application.

Our team of mobile development specialists has extensive experience implementing subscription-based monetization strategies across iOS and Android platforms, ensuring compliance with store guidelines while maximizing revenue potential.

Consumable Products

Consumable products are digital items that can be purchased multiple times and are typically used within a single app session. Examples include virtual currency, extra lives in games, temporary power-ups, or any digital asset that gets depleted through use. The plugin requires explicit consumption after purchase, allowing users to repurchase the same item indefinitely. Implementation involves calling buyConsumable() for each purchase and marking the item as consumed once the content has been delivered.

Non-Consumable Products

Non-consumable products represent one-time purchases that provide permanent access to features or content. Examples include premium app unlocks, ad removal, or permanent character upgrades in games. Unlike consumables, these purchases are tracked by the underlying store and can be restored across device reinstallations without server-side tracking. The plugin handles restoration through restorePurchases(), which should be made available to users in settings or account management screens.

Subscription Products

Subscription products provide ongoing access to content or features for a recurring period, typically weekly, monthly, quarterly, or annually. Subscriptions represent the most common monetization model for modern mobile applications, enabling predictable revenue streams and sustained user engagement. The plugin treats subscriptions similarly to non-consumables for purchase purposes but requires additional handling for subscription-specific events like renewal, cancellation, and upgrade/downgrade scenarios.

Key Subscription Implementation Components

Unified API

Single codebase approach for iOS, Android, and macOS platforms

Purchase Stream

Real-time handling of purchase events and status updates

Product Management

Query and display available products from store catalogs

Subscription Lifecycle

Handle renewals, cancellations, upgrades, and downgrades

Server Validation

Secure receipt verification through backend services

Restore Functionality

Enable purchase recovery across devices and app reinstalls

Setting Up the in_app_purchase Plugin

The official Flutter in_app_purchase plugin provides a unified API that abstracts the differences between Apple's App Store and Google's Play Store. Adding the plugin to your Flutter project requires standard dependency management, but proper initialization and subscription to purchase updates demands careful attention to the application lifecycle.

For production-ready implementations, consider integrating with a comprehensive web development backend to handle server-side validation, subscription management, and analytics tracking.

Dependency Configuration

dependencies:
 flutter:
 sdk: flutter
 in_app_purchase: ^3.2.3

Initializing the Purchase Stream

Establishing a purchase update listener early in your application lifecycle is critical for capturing all purchase events, including those from previous sessions that may still be pending. The recommended approach involves subscribing to the purchase stream in your main widget's initState() method, ensuring the listener is active before any UI rendering occurs.

final Stream purchaseUpdated = InAppPurchase.instance.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
 _listenToPurchaseUpdated(purchaseDetailsList);
});

The subscription should be cancelled during widget disposal to prevent memory leaks and ensure clean resource management. Both the App Store and Google Play require specific account configurations and store-side setup before in-app purchases can function in your application.

Loading and Displaying Products

Querying available products from the store is a critical step in presenting purchase options to users. The queryProductDetails() method retrieves product details from both underlying stores, returning platform-specific information through a standardized API. Before attempting to load products, verify that the store is available by checking the isAvailable() method, which is particularly important for devices that may be sideloaded or emulators without store access.

Implementing the Purchase Flow

Executing purchases through the plugin involves calling the appropriate purchase method based on product type. The underlying store handles all payment processing, including user authentication, payment method selection, and transaction completion. Your application receives updates through the purchase stream, where it must validate, deliver, and acknowledge each purchase.

Executing a Purchase

Consumable and non-consumable products require different purchase methods due to how the underlying stores track ownership. Consumables use buyConsumable(), which allows repeated purchases of the same item. Non-consumables and subscriptions use buyNonConsumable(), which prevents duplicate purchases until the existing item is restored through account recovery.

if (_isConsumable(productDetails)) {
 InAppPurchase.instance.buyConsumable(
 purchaseParam: purchaseParam,
 autoConsume: kAutoConsumeTrue,
 );
} else {
 InAppPurchase.instance.buyNonConsumable(
 purchaseParam: purchaseParam,
 );
}

The autoConsume parameter for consumable purchases controls whether the plugin automatically marks the item as consumed after delivery. Setting this to false and manually calling consumePurchase() provides greater reliability for production applications that require precise consumption tracking.

Handling Purchase Updates

The purchase stream delivers all purchase events, including successful transactions, pending payments, errors, and restored purchases. Each PurchaseDetails object contains status information that determines how your application should respond. Processing these events requires a systematic approach that validates each purchase, delivers content appropriately, and acknowledges completion to the store through the completePurchase() method.

Completing Purchases and Server Validation

Calling completePurchase() is a critical step that tells the underlying store your application has processed the transaction successfully. Failure to complete purchases within three days can result in automatic refunds, making this step essential for revenue protection.

Server-Side Validation

Client-side purchase validation provides basic protection but is vulnerable to manipulation by malicious users. Production applications should implement server-side validation by sending the purchase receipt to your backend, where it can be verified against Apple's or Google's servers directly. For iOS, server-side validation involves sending the transaction receipt to Apple's validation endpoint. For Android, validation involves verifying the purchase token against Google's Play Developer API.

final response = await http.post(
 Uri.parse('https://your-server.com/verify-purchase'),
 body: {
 'receiptData': verificationData.serverVerificationData,
 'platform': purchaseDetails.productID.contains('ios') ? 'ios' : 'android',
 },
);

Manual Consumption for Consumables

When autoConsume is set to false, your application must explicitly call completePurchase() followed by consumePurchase() to properly handle consumable transactions. Android requires consumption for all consumable products, while iOS handles consumption tracking internally without developer intervention.

Never trust client-side purchase data without server-side verification. Always validate receipts against platform servers through your backend, and implement proper access control for premium features. Store purchase tokens and transaction identifiers securely, using them to verify subsequent renewals and detect potential fraud.

Managing Subscriptions

Subscription management extends beyond the initial purchase to include ongoing lifecycle events like renewal, cancellation, upgrade, downgrade, and billing issues. The plugin provides mechanisms for handling these events through the purchase stream, where subscription status updates are delivered alongside initial purchases.

For advanced analytics and churn prediction, integrating AI automation services can provide valuable insights into user behavior and subscription patterns.

Handling Subscription Status Updates

Active subscriptions are renewed automatically by both Apple and Google unless the user cancels before the renewal date. Your application receives subscription updates through the purchase stream, including PurchaseStatus.purchased events for successful renewals. These events should update the user's subscription status in your backend, ensuring continued access to premium features.

Upgrading and Downgrading Subscriptions

Subscription tier changes require different handling on iOS and Android due to platform-specific APIs. On Android, provide the original purchase details when initiating an upgrade or downgrade, along with a proration mode that determines how the billing cycle adjusts. On iOS, subscription grouping handles tier relationships automatically through App Store Connect configuration.

Handling Price Changes

When subscription prices change, existing subscribers must explicitly accept the new price to continue their subscription beyond the current period. Both platforms handle this through confirmation dialogs that users must accept. On Android, when a price increase is published, existing subscribers enter a legacy price cohort and receive notifications about the change. If they do not accept the price increase, the subscription expires at the end of the current billing period.

Implementing proper subscription management is essential for maintaining user trust and ensuring compliance with platform guidelines for subscription implementations.

Restoring Previous Purchases

Purchase restoration allows users to recover their non-consumable and subscription purchases after reinstalling the app or switching devices. Both Apple and Google require providing a restoration mechanism in your app, typically through a settings screen or account management section.

Implementing Restore Functionality

await InAppPurchase.instance.restorePurchases();

Restored purchases are emitted through the purchase stream with PurchaseStatus.restored, allowing your application to process them using the same validation and delivery logic as new purchases. After restoration, verify each restored purchase through your backend validation endpoint to ensure authenticity and update the user's entitlements accordingly.

The restore process is instantaneous from the user's perspective but requires careful error handling for edge cases like network failures or invalid receipts. Users should be informed gracefully when restoration is complete, with clear messaging about which purchases have been recovered.

Best Practices for Production Implementation

Security Considerations

Never trust client-side purchase data without server-side verification. Malicious users can intercept and modify app traffic to simulate purchases that never occurred. Always validate receipts against platform servers through your backend, and implement proper access control for premium features.

Store purchase tokens and transaction identifiers securely, using them to verify subsequent renewals and detect potential fraud. Implement server-side tracking of purchase status that updates when renewals occur, allowing you to revoke access for subscriptions that fail to renew.

Error Handling

Network failures, store unavailability, and invalid products can all cause purchase failures. Implement comprehensive error handling that provides meaningful feedback to users while logging details for debugging. Consider retry logic for transient failures and graceful degradation when purchases are unavailable.

Compliance Requirements

Both Apple and Google have strict guidelines regarding subscription implementations, including requirements for clear pricing display, easy cancellation access, and accurate subscription status communication. Review platform guidelines regularly and ensure your implementation complies with current requirements to avoid app store rejection or removal.

Platform-Specific Considerations

While the plugin provides a unified API, important differences exist between iOS and Android implementations that require platform-specific handling. Recent versions of the plugin support StoreKit 2 on iOS, providing improved transaction management and receipt handling. For Android, the billing implementation uses the Google Play Billing Library, which receives regular updates with new features and security improvements.

Frequently Asked Questions

What Flutter version is required for in_app_purchase?

The in_app_purchase plugin supports Flutter 3.0 or higher. For iOS, the deployment target must be 12.0 or higher. For Android, the minimum SDK version must be 21 or higher.

How do I test in-app purchases locally?

Use sandbox accounts for iOS testing and internal testing tracks for Android. Both platforms provide test environments that simulate purchases without actual billing. The plugin works identically in test and production environments.

Can I use the same product IDs across iOS and Android?

While possible, it's recommended to use platform-specific prefixes or suffixes in product IDs to prevent confusion and ensure proper tracking. Some developers use the same IDs for simplicity, but platform-specific IDs provide better organization.

What happens if the app closes during a purchase?

The underlying store handles purchase flow completion independently of your app. When the user returns to the app, the purchase will be delivered through the purchase stream. Your app should handle pending purchases appropriately in the stream listener.

How do I implement promotional codes for subscriptions?

iOS 14+ supports presenting a code redemption sheet through the plugin. Use `presentCodeRedemptionSheet()` on the StoreKit platform addition to enable this feature for your users.

Ready to Implement Subscriptions in Your Flutter App?

Our team specializes in building cross-platform mobile applications with robust monetization strategies. Contact us to discuss your subscription implementation.