3
votes

I have integrated in flutter in_app_purchase subscription (android ), but it always automatically refund after 3 days

I am using below code for flutter subscription. I can't find the actual issue in the code, please help what I have missed in this code

enter image description here


    import 'dart:async';
    import 'dart:io';
    import 'package:flutter/material.dart';
    import 'package:url_launcher/url_launcher.dart';
    import 'package:in_app_purchase/in_app_purchase.dart';
    import 'util/ConsumableStore.dart';
    
    
    const bool _kAutoConsume = true;
    
    const String _kConsumableId = 'consumable';
    const List _kProductIds = ['subscription_item', 'purchase_item'];
    
    class StoreScreenNew extends StatefulWidget {
      @override
      _StoreScreenState createState() => _StoreScreenState();
    }
    
    class _StoreScreenState extends State {
      final InAppPurchaseConnection _connection = InAppPurchaseConnection.instance;
      StreamSubscription> _subscription;
      List _notFoundIds = [];
      List _products = [];
      List _purchases = [];
      bool _isAvailable = false;
      bool _purchasePending = false;
      bool _loading = true;
      String _queryProductError;
      bool _isConnected = false;
      String storeName = "";
    
      @override
      void initState() {
        checkInternet().then((onValue) {
          setState(() {
            _isConnected = onValue;
          });
        });
        Stream purchaseUpdated =
            InAppPurchaseConnection.instance.purchaseUpdatedStream;
        _subscription = purchaseUpdated.listen((purchaseDetailsList) {
          _listenToPurchaseUpdated(purchaseDetailsList);
        }, onDone: () {
          _subscription.cancel();
        }, onError: (error) {
          // handle error here.
        });
        initStoreInfo();
        super.initState();
      }
    
      Future checkInternet() async {
        try {
          final result = await InternetAddress.lookup('google.com');
          if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
            return Future.value(true);
          } else {
            return Future.value(false);
          }
        } on SocketException catch (_) {
          return Future.value(false);
        }
      }
    
      Future initStoreInfo() async {
        if (Platform.isIOS) {
          storeName = "iTunes";
        } else {
          storeName = "Play Store";
        }
    
        final bool isAvailable = await _connection.isAvailable();
        if (!isAvailable) {
          setState(() {
            _isAvailable = isAvailable;
            _products = [];
            _purchases = [];
            _notFoundIds = [];
            _purchasePending = false;
            _loading = false;
          });
          return;
        }
    
        ProductDetailsResponse productDetailResponse =
            await _connection.queryProductDetails(_kProductIds.toSet());
        if (productDetailResponse.error != null) {
          setState(() {
            _queryProductError = productDetailResponse.error.message;
            _isAvailable = isAvailable;
            _products = productDetailResponse.productDetails;
            _purchases = [];
            _notFoundIds = productDetailResponse.notFoundIDs;
            _purchasePending = false;
            _loading = false;
          });
          return;
        }
    
        if (productDetailResponse.productDetails.isEmpty) {
          setState(() {
            _queryProductError = null;
            _isAvailable = isAvailable;
            _products = productDetailResponse.productDetails;
            _purchases = [];
            _notFoundIds = productDetailResponse.notFoundIDs;
            _purchasePending = false;
            _loading = false;
          });
          return;
        }
    
        final QueryPurchaseDetailsResponse purchaseResponse =
            await _connection.queryPastPurchases();
        if (purchaseResponse.error != null) {
          // handle query past purchase error..
        }
        final List verifiedPurchases = [];
        for (PurchaseDetails purchase in purchaseResponse.pastPurchases) {
          if (await _verifyPurchase(purchase)) {
            verifiedPurchases.add(purchase);
          }
        }
        setState(() {
          _isAvailable = isAvailable;
          _products = productDetailResponse.productDetails;
          _purchases = verifiedPurchases;
          _notFoundIds = productDetailResponse.notFoundIDs;
          _purchasePending = false;
          _loading = false;
        });
      }
    
      @override
      void dispose() {
        _subscription.cancel();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        List stack = [];
        if (_queryProductError == null) {
          stack.add(
            ListView(
              children: [
                _buildConnectionCheckTile(),
                _buildProductList(),
                addPrivacy(),
                addLink()
              ],
            ),
          );
        } else {
          stack.add(Center(
            child: Text(_queryProductError),
          ));
        }
        if (_purchasePending) {
          stack.add(
            Stack(
              children: [
                Opacity(
                  opacity: 0.3,
                  child: const ModalBarrier(dismissible: false, color: Colors.grey),
                ),
                Center(
                  child: CircularProgressIndicator(),
                ),
              ],
            ),
          );
        }
    
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
                backgroundColor: Theme.of(context).primaryColor,
                automaticallyImplyLeading: true,
                title: Text('PRO',
                    style: Theme.of(context).textTheme.headline5),
                leading: IconButton(
                  icon: Icon(Icons.arrow_back),
                  onPressed: () => Navigator.pop(context, false),
                )),
            body: _isConnected
                ? Stack(
                    children: stack,
                  )
                : Center(
                    child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                        SizedBox(
                          height: 10,
                        ),
                        Container(
                            margin: EdgeInsets.all(20),
                            child: Text(
                              "Check your internet connection and try again.",
                              textAlign: TextAlign.center,
                              style: TextStyle(color: Colors.black45, fontSize: 26),
                            ))
                      ])),
          ),
        );
      }
    
      Card _buildConnectionCheckTile() {
        if (_loading) {
          return Card(child: ListTile(title: const Text('Trying to connect...')));
        }
        final Widget storeHeader = ListTile(
          leading: Icon(_isAvailable ? Icons.check : Icons.block,
              color: _isAvailable ? Colors.green : ThemeData.light().errorColor),
          title: Text(
              'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'),
        );
        final List children = [
          !_isAvailable ? storeHeader : Container()
        ];
    
        if (!_isAvailable) {
          children.addAll([
            Divider(),
            ListTile(
              title: Text('Not connected',
                  style: TextStyle(color: ThemeData.light().errorColor)),
              subtitle: const Text(
                  'Unable to connect to the payments processor. Has this app been configured correctly? See the example README for instructions.'),
            ),
          ]);
        }
        return Card(child: Column(children: children));
      }
    
      Card _buildProductList() {
        if (_loading) {
          return Card(
              child: (ListTile(
                  leading: CircularProgressIndicator(),
                  title: Text('Fetching products...'))));
        }
        if (!_isAvailable) {
          return Card();
        }
        final ListTile productHeader = ListTile(
          title: Text(
            'Available Options',
            style: TextStyle(fontSize: 20),
          ),
          
        );
        List productList = [];
        if (_notFoundIds.isNotEmpty) {
          productList.add(ListTile(
              title: Text('[${_notFoundIds.join(", ")}] not found',
                  style: TextStyle(color: ThemeData.light().errorColor)),
              subtitle: Text(
                  'This app needs special configuration to run. Please see example/README.md for instructions.')));
        }
    
     
        Map purchases =
            Map.fromEntries(_purchases.map((PurchaseDetails purchase) {
          if (purchase.pendingCompletePurchase) {
            InAppPurchaseConnection.instance.completePurchase(purchase);
          }
          return MapEntry(purchase.productID, purchase);
        }));
        productList.addAll(_products.map(
          (ProductDetails productDetails) {
            PurchaseDetails previousPurchase = purchases[productDetails.id];
    
            return Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Colors.white,
                boxShadow: [
                  BoxShadow(color: Colors.grey, spreadRadius: 1),
                ],
              ),
              margin: EdgeInsets.all(5),
              padding: EdgeInsets.all(10),
              child: Column(
                children: [
                  Text(
                    productDetails.title,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.bold,
                        color:
                            previousPurchase != null ? Colors.green : Colors.black),
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  Divider(),
                  SizedBox(
                    height: 10,
                  ),
                  Text(
                    productDetails.description,
                    textAlign: TextAlign.left,
                  ),
                  SizedBox(
                    height: 20,
                  ),
                  Divider(),
                  Container(
                      alignment: Alignment.bottomRight,
                      child: previousPurchase != null
                          ? Container(
                              padding: const EdgeInsets.all(10.0),
                              decoration: new BoxDecoration(
                                shape: BoxShape.circle,
                                color: Colors.green,
                              ),
                              child: Icon(
                                Icons.check,
                                size: 30,
                                color: Colors.white,
                              ))
                          : FlatButton(
                              child: Text(
                                productDetails.price,
                                style: TextStyle(fontSize: 18),
                              ),
                              color: Colors.green[800],
                              textColor: Colors.white,
                              onPressed: () {
                                PurchaseParam purchaseParam = PurchaseParam(
                                    productDetails: productDetails,
                                    applicationUserName: null,
                                    sandboxTesting: false);
                                if (productDetails.id == _kConsumableId) {
                                  _connection.buyConsumable(
                                      purchaseParam: purchaseParam,
                                      autoConsume: _kAutoConsume || Platform.isIOS);
                                } else {
                                  _connection.buyNonConsumable(
                                      purchaseParam: purchaseParam);
                                }
                              },
                            ))
                ],
              ),
            );
     
          },
        ));
    
        return Card(
            margin: EdgeInsets.all(10),
            elevation: 0,
            child: Column(
              children: [
                    productHeader,
                    Divider(),
                  ] +
                  productList,
            ));
      }
     
      void showPendingUI() {
        setState(() {
          _purchasePending = true;
        });
      }
    
      void deliverProduct(PurchaseDetails purchaseDetails) async {
         
        if (purchaseDetails.productID == _kConsumableId) {
          await ConsumableStore.save(purchaseDetails.purchaseID);
          App.setPurchasesStatus(true);
          setState(() {
            _purchasePending = false;
          });
        } else {
          setState(() {
            _purchases.add(purchaseDetails);
            _purchasePending = false;
          });
        }
      }
    
      void handleError(IAPError error) {
        setState(() {
          _purchasePending = false;
        });
      }
    
      Future _verifyPurchase(PurchaseDetails purchaseDetails) {
       
        return Future.value(true);
      }
    
      void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
       
      }
    
      void _listenToPurchaseUpdated(List purchaseDetailsList) {
        purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
          if (purchaseDetails.status == PurchaseStatus.pending) {
            showPendingUI();
          } else {
            if (purchaseDetails.status == PurchaseStatus.error) {
              handleError(purchaseDetails.error);
            } else if (purchaseDetails.status == PurchaseStatus.purchased) {
              bool valid = await _verifyPurchase(purchaseDetails);
              if (valid) {
                deliverProduct(purchaseDetails);
              } else {
                _handleInvalidPurchase(purchaseDetails);
                return;
              }
            }
            if (Platform.isAndroid) {
              if (!_kAutoConsume && purchaseDetails.productID == _kConsumableId) {
                await InAppPurchaseConnection.instance
                    .consumePurchase(purchaseDetails);
              }
            }
            if (purchaseDetails.pendingCompletePurchase) {
              await InAppPurchaseConnection.instance
                  .completePurchase(purchaseDetails);
            }
          }
        });
      }
     
    }

1

1 Answers

2
votes

Make sure with a breakpoint that you are calling

InAppPurchaseConnection.instance.completePurchase(purchase);

Maybe something is happening before it is being called.

Maybe dispose() is being called in between.