flutterin-app-purchase

Flutter [in_app_purchase] Get all plans inside subscription


I'm using the in_app_purchase package, but I only can get one plan inside the subscriptions

I have 3 subscriptions:

Basic subscription
Premium subscription
Enterprise subscription

And inside each subscription, I want to have 2 plans:

Month plan
Year plan

I always get the plan that has the "backward compatibility"("This will be the baseline returned by the deprecated Google Play Billing Library method querySkuDetailsAsync()") enabled.

Is any way to get all plans, or do I have to have 6 subscriptions with only 1 plan in each one?

Edit:

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
import 'package:motorline_home/widgets/materials/appbar/appbar_title_widget.dart';
import 'package:motorline_home/widgets/materials/pop_button_widget.dart';
import 'package:rxdart/subjects.dart';

class SubscriptionPage extends StatefulWidget {

  const SubscriptionPage({
    Key? key,
  }) : super(key: key);

  @override
  State<SubscriptionPage> createState() => _SubscriptionPageState();
}

class _SubscriptionPageState extends State<SubscriptionPage> {
  // In app subscriptions
  InAppPurchase _inAppPurchase = InAppPurchase.instance;
  late StreamSubscription<List<PurchaseDetails>> _inAppPurchaseSubscription;
  StreamController<List<ProductDetails>> _streamGooglePlaySubscriptions =
      BehaviorSubject();
  final List<String> _subscriptionsIDs = [
    "basic",
    "premium",
    "enterprise",
  ];

  @override
  void initState() {
    super.initState();

    // In app purchase subscription
    _inAppPurchaseSubscription =
        _inAppPurchase.purchaseStream.listen((purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      print("In app purchase onDone");
      _inAppPurchaseSubscription.cancel();
    }, onError: (error) {
      print("In app purchase error: ${error.toString()}");
      // handle error here.
      _inAppPurchaseSubscription.cancel();
    });
    // Initialize in app purchase
    _initializeInAppPurchase();
  }

  @override
  void dispose() {
    if (Platform.isIOS) {
      final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
      _inAppPurchase
          .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      iosPlatformAddition.setDelegate(null);
    }

    // Cancel in app purchase listener
    _inAppPurchaseSubscription.cancel();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: AppBarTitleWidget(
          title: FlutterI18n.translate(context, "subscriptions"),
        ),
        leading: PopButtonWidget(),
      ),
      // Body
      body: Container(),
    );
  }

  void _initializeInAppPurchase() async {
    print("Initializing in app purchase");
    bool available = await _inAppPurchase.isAvailable();
    print("In app purchase initialized: $available");

    if (available) {
      if (Platform.isIOS) {
        final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
        _inAppPurchase
            .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
        await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
      }

      // Get subscriptions
      List<ProductDetails> subscriptions = await _getSubscriptions(
        productIds:
          _subscriptionsIDs.toSet(),
      );
      // Sort by price
      subscriptions.sort((a, b) => a.rawPrice.compareTo(b.rawPrice));

      // Add subscriptions to stream
      _streamGooglePlaySubscriptions.add(subscriptions);

      // DEBUG: Print subscriptions
      print("In app purchase subscription subscriptions: ${subscriptions}");
      for (var subscription in subscriptions) {
        print("In app purchase plan: ${subscription.id}: ${subscription.rawPrice}");
        print("In app purchase description: ${subscription.description}");
        // HOW GET ALL PLANS IN EACH SUBSCRIPTION ID?
      }

      await InAppPurchase.instance.restorePurchases();
    }
  }

  // In app purchase updates
  void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
    purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
      // If purchase is pending
      if (purchaseDetails.status == PurchaseStatus.pending) {
        print("In app purchase pending...");
        // Show pending ui
      } else {
        if (purchaseDetails.status == PurchaseStatus.canceled) {
          print("In app purchase cancelled");
        }
        // If purchase failed
        if (purchaseDetails.status == PurchaseStatus.error) {
          print("In app purchase error");
          // Show error
        } else if (purchaseDetails.status == PurchaseStatus.purchased ||
            purchaseDetails.status == PurchaseStatus.restored) {
          print("In app purchase restored or purchased");
        }

        if (purchaseDetails.pendingCompletePurchase) {
          debugPrint("In app purchase complete purchased");
          debugPrint(
              "In app purchase purchase id : ${purchaseDetails.purchaseID}");
          debugPrint(
              "In app purchase server data : ${purchaseDetails.verificationData.serverVerificationData}");
          debugPrint(
              "In app purchase local data  : ${purchaseDetails.verificationData.localVerificationData}");
          // Verify purchase on backend

          try {
            // VALIDADE PURCHASE IN BACKEND
          } catch (error) {
            debugPrint("In app purchase error: ${error.toString()}");
          }
        }
      }
    });
  }

  // Get subscription
  Future<List<ProductDetails>> _getSubscriptions(
      {required Set<String> productIds}) async {
    ProductDetailsResponse response =
        await _inAppPurchase.queryProductDetails(productIds);

    return response.productDetails;
  }
}

/// Example implementation of the
/// [`SKPaymentQueueDelegate`](https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate?language=objc).
///
/// The payment queue delegate can be implementated to provide information
/// needed to complete transactions.
class ExamplePaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
  @override
  bool shouldContinueTransaction(
      SKPaymentTransactionWrapper transaction, SKStorefrontWrapper storefront) {
    return true;
  }

  @override
  bool shouldShowPriceConsent() {
    return false;
  }
}

Solution

  • What you like to do is not possible. You need to create for each subscription a new plan, you can't say a Premium Subscription does have a yearly and a monthly plan.