payment_sdk.dart 3.6 KB
import 'dart:async';

import 'package:Parlando/apis/api_order.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:common_utils/common_utils.dart';
import 'package:in_app_purchase/in_app_purchase.dart';

class PaymentSdk {
  PaymentSdk._privateConstructor();

  static final PaymentSdk _instance = PaymentSdk._privateConstructor();

  static PaymentSdk get instance {
    return _instance;
  }

  static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip'};

  StreamSubscription<List<PurchaseDetails>>? _subscription;
  List<ProductDetails> products = [];
  Function? onPaySuccess;
  Function? onPending;
  Function? onFailed;
  Function? onCancel;

  initState({
    Function? onPaySuccess,
    Function? onPending,
    Function? onFailed,
    Function? onCancel,
  }) {
    this.onPaySuccess = onPaySuccess;
    this.onPending = onPending;
    this.onFailed = onFailed;
    this.onCancel = onCancel;
    final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream;
    _subscription = purchaseUpdated.listen((purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscription?.cancel();
    }, onError: (error) {
      // handle error here.
    });
  }

  Future<List<ProductDetails>> queryProducts() async {
    final bool available = await InAppPurchase.instance.isAvailable();
    if (!available) {
      return [];
    }
    final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(_kIds);
    return products = response.productDetails;
  }

  buy(ProductDetails details, MembershipDataGoodsList e) {
    OrderApi.request.createOrder(e.id.toString()).then((value) {
      var orderId = value?['data']?['data']?['order_sn'];
      if (TextUtil.isEmpty(orderId)) {
        onFailed?.call();
        return;
      }
      final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId);
      if (_isConsumable(details)) {
        InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
      } else {
        InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
      }
    });
  }

  void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async {
    for (var purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        onPending?.call();
      } else {
        if (purchaseDetails.status == PurchaseStatus.error) {
          _handleError(purchaseDetails.error!);
        } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) {
          bool valid = await _verifyPurchase(purchaseDetails);
          if (valid) {
            _deliverProduct(purchaseDetails);
          } else {
            _handleInvalidPurchase(purchaseDetails);
          }
        } else {
          onCancel?.call();
        }
        if (purchaseDetails.pendingCompletePurchase) {
          await InAppPurchase.instance.completePurchase(purchaseDetails);
        }
      }
    }
  }

  _verifyPurchase(PurchaseDetails purchaseDetails) async {
    return true;
  }

  void _deliverProduct(PurchaseDetails purchaseDetails) {
    onPaySuccess?.call();
  }

  void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
    onFailed?.call();
  }

  void _handleError(IAPError iapError) {
    onFailed?.call();
  }

  bool _isConsumable(ProductDetails details) {
    return true;
  }

  void dispose() {
    onPaySuccess = null;
    onPending = null;
    onFailed = null;
    onCancel = null;
    _subscription?.cancel();
  }
}