In app Purchase in Flutter

Unlocking Revenue Streams: with multiple purchase plans for both the platforms Android and iOS.

Step 1: Setting Up Developer Accoun

Before integrating in-app purchases into your Flutter app, you must have accounts on Google Play Developer Console & App Store Connect. These platforms will be used for managing your in-app products and purchases.
Google Play Store: Sign up for a Google Play Developer account. You’ll need to pay a one-time registration fee

Google Play documentation

Apple App Store: Enroll in the Apple Developer Program. This involves an annual fee.

App Store documentation

Once your developer accounts are set up and activated, you’ll gain access to the developer consoles where you can manage your apps and in-app purchases.

Step 2: Creating Products

After setting up your developer accounts, you’ll need to create products for your in-app purchases:

Google Play Console: Navigate to your app’s dashboard and select “Monetize” > “In-app products” > “Add new product”. Follow the prompts to create your in-app products, such as subscriptions, consumables, or one-time purchases.

App Store Connect: In your app’s dashboard, go to “App Store” > “App Store Connect” > “My Apps” > Your app > “Features” > “In-App Purchases”. Click the “+” button to create new in-app purchases, specifying the type and details of each product.

To facilitate in-app purchases within your Flutter app, you’ll need to define unique product IDs for each available product or subscription plan. These product IDs serve as identifiers to differentiate between different types of purchases and enable your app to retrieve the corresponding product details from the app store.

Here are examples of product IDs for various subscription plans:

Yearly Plan Product ID: com.example.yearly
Monthly Plan Product ID: com.example.monthly

You can create different product IDs for each subscription plan or product offered within your app, ensuring clarity and consistency in your app’s purchasing process. These product IDs should be defined in your app’s code and used accordingly when initiating purchases or querying product details.

Step 3: Integrating In-App Purchases into Flutter

Now, let’s dive into the code setup within your Flutter app:

Adding dependencies

dependencies:
  flutter:
    sdk: flutter
  in_app_purchase: ^3.1.13
  get: ^4.6.5

“Plugin Integration: Utilize a Flutter plugin for in-app purchases. A commonly used option is the in_app_purchase plugin. While I have personally employed GetX for state management, feel free to choose any state management solution that suits your needs.”

class InAppPurchaseUtils extends GetxController {
  // Private constructor
  InAppPurchaseUtils._();

  // Singleton instance
  static final InAppPurchaseUtils _instance = InAppPurchaseUtils._();

  // Getter to access the instance
  static InAppPurchaseUtils get inAppPurchaseUtilsInstance => _instance;

  // Create a private variable
  final InAppPurchase _iap = InAppPurchase.instance;

  // Add your methods related to in-app purchases here
  ...
  // Example method
  void purchaseProduct(String productId) {
    // Implementation for purchasing a product
  }
}
Dart

By making InAppPurchaseUtils a singleton, you ensure that only one instance of the class exists throughout the application, preventing the creation of multiple instances.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // Create an instance of InAppPurchaseUtils
  final InAppPurchaseUtils inAppPurchaseUtils = InAppPurchaseUtils.instance;

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Your App Name',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Your App'),
        ),
        body: Center(
          child: Text('In-app purchase related content here'),
        ),
      ),
      initialBinding: BindingsBuilder(() {
        Get.put<InAppPurchaseUtils>(inAppPurchaseUtils);
      }),
    );
  }
}
Dart

Initialization: Initialize the plugin in your app’s main function or during app startup.

class InAppPurchaseUtils extends GetxController {

....
 
late StreamSubscription<List<PurchaseDetails>> _purchasesSubscription;

 @override
  void onInit() {
    super.onInit();
    initialize();
  }

 @override
  void onClose() {
    _purchasesSubscription.cancel();
    super.onClose();
  }

  Future<void> initialize() async {
    if(!(await _iap.isAvailable())) return;
    ///catch all purchase updates
    _purchasesSubscription = InAppPurchase.instance.purchaseStream.listen((List<PurchaseDetails> purchaseDetailsList) {
      handlePurchaseUpdates(purchaseDetailsList);
     },
      onDone: () {
        _purchasesSubscription.cancel();
       },
      onError: (error) { },
    );
  }

void handlePurchaseUpdates(List<PurchaseDetails> purchaseDetailsList) {
    // Implement your logic for handling purchase updates here
  }
....

}
Dart

In the InAppPurchaseUtils class, the core functionality of handling in-app purchases relies on utilizing a stream provided by the in_app_purchase plugin.

StreamSubscription<List<PurchaseDetails>> _purchasesSubscription.
Dart

This is a purchase stream that allows the class to listen for updates whenever a purchase is made or updated

_iap.isAvailable()
Dart

This method checks if in-app purchases are available on the current platform.

_purchasesSubscription = InAppPurchase.instance.purchaseStream.listen((List<PurchaseDetails> purchaseDetailsList) {
      handlePurchaseUpdates(purchaseDetailsList);
     }
Dart

Whenever an event occurs within the in-app purchase system, such as a successful purchase or an error during the transaction process, the stream notifies the _purchasesSubscription. Upon receiving these notifications, the class invokes the handlePurchaseUpdates method to appropriately manage and respond to the event.

In essence, the _purchasesSubscription allows the InAppPurchaseUtils class to stay constantly updated with the latest information regarding in-app purchases. This ensures that the application can react promptly to any changes or updates in the purchasing process, providing a seamless user experience.

handlePurchaseUpdates(purchaseDetailsList){
     for (int index = 0 ; index < purchaseDetailsList.length; index++) {
         var purchaseStatus = purchaseDetailsList[index].status;
         switch (purchaseDetailsList[index].status) {
           case PurchaseStatus.pending:
             print(' purchase is in pending ');
             continue;
           case PurchaseStatus.error:
             print(' purchase error ');
             break;
           case PurchaseStatus.canceled:
             print(' purchase cancel ');
             break;
           case PurchaseStatus.purchased:
             print(' purchased ');
             break;
           case PurchaseStatus.restored:
             print(' purchase restore ');
             break;
         }
         
        if (purchaseDetailsList[index].pendingCompletePurchase){
           await _iap.completePurchase(purchaseDetailsList[index]).then((value){
             if(purchaseStatus == PurchaseStatus.purchased){
              //on purchase success you can call your logic and your API here.
             }
           });
         }
       }
}
Dart

This method processes each purchase detail within the provided list, allowing for dynamic handling of various purchase events based on their respective flags.

Additionally, if a purchase detail requires pending completion, the method invokes the

_iap.completePurchase();
Dart

method to finalize the purchase. This step is crucial for ensuring the completion of pending purchases and enabling further processing, such as invoking relevant logic or API calls upon successful purchases.

Making a purchase

There are two main types of In-App purchase

  1. Consumable
    Once bought, consumable purchases become depleted as the user uses them. After using all of a consumable purchase, the user must purchase another consumable IAP again to enjoy the same benefits.
    Example: Extra lives in a gaming app
  2. Non-consumable
    Once a user buys a non-consumable purchase, it’s permanently available to them. It does not expire with time or use. Non-consumable purchases are often premium features.
    Example: Additional filters in a photo app.
 Future<void> buyNonConsumableProduct(String productId) async {
    try {
      Set<String> productIds = {newProductId};

      final ProductDetails productDetails = 
                await _iap.queryProductDetails(productId);
      if (productDetails == null) {
        // Product not found
        return;
      }
      
      final PurchaseParam purchaseParam = 
                           PurchaseParam(productDetails: productDetails.first);
      await _iap.buyNonConsumable(purchaseParam: purchaseParam);
    } catch (e) {
      // Handle purchase error
      print('Failed to buy plan: $e');
    }
  }
}
Dart

the method calls “_iap.buyNonConsumable” to initiate the purchase of the non-consumable product. This method triggers the purchase flow, allowing users to complete the transaction securely through the app store.

Future<void> buyConsumableProduct(String productId) async {
    try {
      Set<String> productIds = {newProductId};

      final ProductDetails productDetails = 
                await _iap.queryProductDetails(productId);
      if (productDetails == null) {
        // Product not found
        return;
      }
      
      final PurchaseParam purchaseParam = 
                           PurchaseParam(productDetails: productDetails.first);
     await _iap.buyConsumable(purchaseParam: purchaseParam,autoConsume: true);
    } catch (e) {
      // Handle purchase error
      print('Failed to buy plan: $e');
    }
  }
}
Dart

the method calls “_iap.buyConsumable” to initiate the purchase of the consumable product. The autoConsume parameter is set to true to enable automatic consumption of the product after purchase, ensuring a seamless user experience.

“productId Parameter” : This parameter represents the unique identifier of the non-consumable product defined in both the Google Play Store and Apple App Store. It serves as a reference to the specific product users intend to purchase.

When creating purchase plans like monthly or weekly subscriptions in the Play Store and App Store, it’s crucial to ensure consistency in the plan names across both platforms. This ensures a seamless purchasing experience for users, regardless of the device they’re using.

Restore Purchases

Restoring purchases typically refers to the process of retrieving a user’s previous purchases, especially in scenarios where the app is reinstalled or the user switches to a new device. This ensures that users can regain access to any content or features they’ve previously purchased without having to make additional payments.

  restorePurchases() async {
      try{
         await _iap.restorePurchases();
       }catch(error){
        //you can handle error if restore purchase fails
       }
   }
Dart

Time to Purchase in Just One Click!

Hurray! Now that our in-app purchase setup is complete, it’s time to make purchases with just one click. Let’s see how easy it is :

import 'package:flutter/material.dart';
import 'package:your_app_name/in_app_purchase_utils.dart';

class PurchaseScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Purchase Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            InAppPurchaseUtils.inAppPurchaseUtilsInstance.buyNonConsumableProduct("pass your product id here");
          },
          child: Text('Purchase Plan'),
        ),
      ),
    );
  }
}
Dart

Simply navigate to the PurchaseScreen in your app and tap the “Purchase Plan” button. It’s that easy! With just one click, users can access premium content and features offered by your app.

Leave a Comment

Your email address will not be published. Required fields are marked *