Skip to content

Unhandled Exception: PlatformException(storekit_duplicate_product_object, There is a pending transaction for the same product identifier #79473

@rossman-x

Description

@rossman-x

Steps to Reproduce

  1. Include in app purchase official plugin.
  2. From sign up page on your iphone, create a payment button to redirect users to the in app purchase.
  3. Create a subscription to follow purchase progress.
  4. First payment will work will, but if you cancel the purchase and try to pay again, it will show this error: Unhandled Exception: PlatformException(storekit_duplicate_product_object, There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using 'completePurchase' to avoid edge cases., {applicationUsername: xxxx, requestData: null, quantity: 1, productIdentifier: XXXX, simulatesAskToBuyInSandbox: false}, null)

I complete all purchases before and after running the purchase, using completePurchase(details)

flutter doctor -v
[✓] Flutter (Channel stable, 2.0.3, on macOS 11.2 20D64 darwin-x64, locale en-GB)
    • Flutter version 2.0.3 at /Users/ramiosman/flutter
    • Framework revision 4d7946a68d (13 days ago), 2021-03-18 17:24:33 -0700
    • Engine revision 3459eb2436
    • Dart version 2.12.2

[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/ramiosman/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    ! Some Android licenses not accepted.  To resolve this, run: flutter doctor --android-licenses

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.4, Build version 12D4e
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] VS Code (version 1.54.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected device (2 available)
    • Rami’s iPhone (mobile) • 00008030-00014D023A99802E • ios            • iOS 14.4.1
    • Chrome (web)           • chrome                    • web-javascript • Google Chrome 89.0.4389.90
    ! Error: iPhone is not connected. Xcode will continue when iPhone is connected. (code -13)
    ! Error: iPhone is not connected. Xcode will continue when iPhone is connected. (code -13)

! Doctor found issues in 1 category.

in_app_purchase version : ^0.5.1+1

Code :

Listener :

      _subscription = _iap.purchaseUpdatedStream
          .listen((List<PurchaseDetails> purchases) async {
        print(purchases.toString());
        if (purchases.isEmpty) return;
        await PaymentFutures.completePurchases(_iap);
        PurchaseDetails lastPurchase = purchases.last;
        if (lastPurchase.status == PurchaseStatus.error) {
          if (FirebaseAuth.instance.currentUser != null) {
            try {
              await counterRef
                  .child(FirebaseAuth.instance.currentUser.uid)
                  .set(null);
              await FirebaseAuth.instance.currentUser.delete();
            } catch (error) {
              print(error.toString());
            }
          }
          moveForward(true, isLoad: false);
          GlobalUseWidgets.alertDialog(context, "Payment error",
              "An error has occurred while trying to make the payment, please try again or contact support.");
          return;
        }

        if (lastPurchase.status == PurchaseStatus.purchased) {
          Navigator.of(context).push(MaterialPageRoute(builder: (bc) {
            return HomeScreen();
          }));
        }
        print(purchases.toString());
      });
      PaymentFutures.completePurchases(_iap).then((value) async {
        setState(() {
          isLoading = false;
          isInAppPurchaseAvailable = isAvailable;
        });
      });

PaymentFutures.completePurchases :

  static Future<void> completePurchases(iap) async{
    final QueryPurchaseDetailsResponse purchaseDetailsResponse =
    await iap.queryPastPurchases();

    for (PurchaseDetails details in purchaseDetailsResponse.pastPurchases) {
      final pending = Platform.isIOS
          ? details.pendingCompletePurchase
          : !details.billingClientPurchase.isAcknowledged;
      if (pending) {
          await iap.completePurchase(details);
      }
    }
  }

runPurchase

  static Future<void> runPurchase(
      InAppPurchaseConnection iap, String uid) async {

    final ProductDetailsResponse productDetailsResponse =
        await iap.queryProductDetails({R.subscriptionIAPId});
    if (productDetailsResponse.productDetails.isEmpty)
      throw Future.error("Subscription not found");

    final ProductDetails details = productDetailsResponse.productDetails.first;

    final PurchaseParam purchaseParam = PurchaseParam(
        productDetails: details,
        applicationUserName: uid,
        sandboxTesting: false);

    if(Platform.isIOS) {
      var transactions = await SKPaymentQueueWrapper().transactions();
      transactions.forEach((skPaymentTransactionWrapper) {
        SKPaymentQueueWrapper().finishTransaction(skPaymentTransactionWrapper);
      });
    }

    await iap.buyNonConsumable(purchaseParam: purchaseParam);

  }

await PaymentFutures.completePurchases(_iap); is called before purchase is made.

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listp: in_app_purchasePlugin for in-app purchasepackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyr: timeoutIssue is closed due to author not providing the requested details in time

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions