flutter_inapp_purchase

repository·main·Indexed 20 days ago

https://github.com/hyochan/flutter_inapp_purchase

A Flutter plugin for implementing in-app purchases that conforms to the Open IAP specification for vendor-neutral interoperability. It supports Google Play Billing, Meta Horizon (Meta Quest), and iOS StoreKit 2 (on iOS 15.0+). The library provides a unified API for fetching products and subscriptions, as well as a builder DSL for purchase requests.

Tokens
128.6K
Snippets
350
Records
450
Agent score
64%

What's inside flutter_inapp_purchase

  1. Important: Repository Migration Notice

    main

    This repository is archived and read-only. The project has moved to the OpenIAP monorepo.

    Note: The pub.dev package name remains flutter_inapp_purchase and is still the correct way to install it via flutter pub add flutter_inapp_purchase.

  2. Handle platform-specific products using union types

    main

    The library uses union types for products. When you fetch products, the returned objects are platform-specific classes (ProductIOS or ProductAndroid). You must check the type of the product at runtime to access platform-specific properties.

    Product Classes

    iOS Products

    • ProductIOS: Extends Product. Includes id, displayName, description, price, displayPrice, and type (ProductTypeIOS).
    • ProductSubscriptionIOS: Extends ProductSubscription. Includes id, displayName, description, price, displayPrice, and subscription (SubscriptionInfoIOS).

    Android Products

    • ProductAndroid: Extends Product. Includes productId, productType, title, name, and description.
    • ProductSubscriptionAndroid: Extends ProductSubscription. Includes productId, title, name, and description.
  3. Use the FlutterInappPurchase singleton class

    main

    The FlutterInappPurchase class is a singleton used to manage in-app purchases on iOS and Android. It handles the connection to platform-specific billing services (App Store and Google Play), product queries, purchase flows, and transaction management.

    Access the singleton via FlutterInappPurchase.instance.

    final iap = FlutterInappPurchase.instance;
  4. Manage FlutterInappPurchase instances

    main

    You can manage the FlutterInappPurchase instance in two ways depending on your state management needs:

    1. Manual Instance (Recommended): Create a new instance for better control and testability.
    2. Singleton: Use the built-in singleton for global state management across your application.
    // Option 1: Create your own instance (recommended for most cases)
    final iap = FlutterInappPurchase();
    
    // Option 2: Use singleton for global state management
    final iap = FlutterInappPurchase.instance;
  5. Use `lib/types.dart` for consistent IAP API shapes

    main

    When exposing In-App Purchase (IAP) APIs, always prefer the generated handler typedefs and helper classes found in lib/types.dart. These types are synchronized with the OpenIAP schema, ensuring that parameter and return shapes remain consistent across the codebase. Using these generated signatures automatically handles nullability, optional flags, and platform-specific fields without requiring manual boilerplate.

    final queryHandlers = QueryHandlers(
      fetchProducts: ({required ProductRequest params}) async {
        return _platform.fetchProducts(params: params);
      },
    );
    
    final mutationHandlers = MutationHandlers(
      finishTransaction: ({
        required PurchaseInput purchase,
        bool? isConsumable,
      }) async {
        return _platform.finishTransaction(
          purchase: purchase,
          isConsumable: isConsumable,
        );
      },
    );
  6. Understand the OpenIAP specification

    main

    The flutter_inapp_purchase package conforms to the OpenIAP specification, which is an open, vendor-neutral interoperability standard for in-app purchases.

    Instead of using framework-specific types or error models, OpenIAP provides a shared specification layer. This ensures that purchase behavior, error codes, and verification flows remain consistent across different platforms and frameworks. The specification is defined via a single GraphQL schema, which is then used to generate type-safe bindings for various platforms, including Dart (used by flutter_inapp_purchase).

  7. Understand the Purchase Lifecycle Phases

    main

    The purchase process in flutter_inapp_purchase follows six distinct phases. Implementing these in order ensures a reliable user experience and prevents lost revenue from unfinalized transactions.

    1. Connection Phase: Establish a connection to the app store.
    2. Product Discovery Phase: Fetch available products (SKUs) from the store.
    3. Purchase Initiation Phase: Trigger the platform's native purchase dialog.
    4. Transaction Processing Phase: Listen to streams for successful purchases or errors.
    5. Content Delivery Phase: Validate the purchase (ideally on a server) and grant the user their content.
    6. Transaction Finalization Phase: Acknowledge or consume the transaction to tell the store the process is complete.
  8. Platform differences for available purchases

    main

    iOS

    • Use onlyIncludeActiveItemsIOS: true to filter for active subscriptions only.
    • Use onlyIncludeActiveItemsIOS: false to include the full purchase history.
    • Use alsoPublishToEventListenerIOS to trigger the event stream for retrieved items.

    Android

    • Returns all verified purchases from Google Play.
    • Includes purchase tokens for server-side verification.
    • Does not have a separate API for purchase history; it returns verified purchases directly.
  9. Use transaction identifiers and purchase tokens for validation

    main

    The plugin has updated how it handles transaction references to ensure consistency across stores:

    • transactionId: This is the primary store reference. It maps to orderId on Android and the StoreKit transaction ID on iOS.
    • Handling null transactionId: On Android, if Google Play omits an orderId (common for consumables), transactionId will be null.
    • Canonical Receipt: For server-side validation, do not rely solely on transactionId. Instead, use the unified purchaseToken exposed on every purchase record. This maps to the Android purchase token or the iOS JWS.
  10. Listen to purchase updates and errors

    main

    The library uses event streams to communicate purchase results. You must listen to these streams to handle successful purchases and errors.

    Important: Always cancel your StreamSubscription in the dispose() method of your widget to prevent memory leaks.

    StreamSubscription? _purchaseUpdatedSubscription;
    StreamSubscription? _purchaseErrorSubscription;
    
    // In your initialization logic:
    _purchaseUpdatedSubscription = iap.purchaseUpdatedListener.listen((purchase) {
      _handlePurchase(purchase);
    });
    
    _purchaseErrorSubscription = iap.purchaseErrorListener.listen((error) {
      _handleError(error);
    });
    
    @override
    void dispose() {
      _purchaseUpdatedSubscription?.cancel();
      _purchaseErrorSubscription?.cancel();
      super.dispose();
    }