Added ability to purchase multiple quantity of one product (#5711)

diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
index a6897ea..445fdb6 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
+++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.1
+
+* Adds ability to purchase more than one of a product.
+
 ## 0.3.0+10
 
 * Ignores deprecation warnings for upcoming styleFrom button API changes.
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart
index 629355d..f81b366 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart
+++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart
@@ -71,7 +71,8 @@
   Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) async {
     await _skPaymentQueueWrapper.addPayment(SKPaymentWrapper(
         productIdentifier: purchaseParam.productDetails.id,
-        quantity: 1,
+        quantity:
+            purchaseParam is AppStorePurchaseParam ? purchaseParam.quantity : 1,
         applicationUsername: purchaseParam.applicationUserName,
         simulatesAskToBuyInSandbox: purchaseParam is AppStorePurchaseParam &&
             purchaseParam.simulatesAskToBuyInSandbox,
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart
index b2d8eea..168ef5c 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart
+++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart
@@ -12,6 +12,7 @@
   AppStorePurchaseParam({
     required ProductDetails productDetails,
     String? applicationUserName,
+    this.quantity = 1,
     this.simulatesAskToBuyInSandbox = false,
   }) : super(
           productDetails: productDetails,
@@ -28,4 +29,7 @@
   ///
   /// See also [SKPaymentWrapper.simulatesAskToBuyInSandbox].
   final bool simulatesAskToBuyInSandbox;
+
+  /// Quantity of the product user requested to buy.
+  final int quantity;
 }
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
index ada883c..dd65b25 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
@@ -2,7 +2,7 @@
 description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
 repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
-version: 0.3.0+10
+version: 0.3.1
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart
index 7c54375..e987a5c 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart
+++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart
@@ -56,20 +56,24 @@
     queueIsActive = false;
   }
 
-  SKPaymentTransactionWrapper createPendingTransaction(String id) {
+  SKPaymentTransactionWrapper createPendingTransaction(String id,
+      {int quantity = 1}) {
     return SKPaymentTransactionWrapper(
-        transactionIdentifier: '',
-        payment: SKPaymentWrapper(productIdentifier: id),
-        transactionState: SKPaymentTransactionStateWrapper.purchasing,
-        transactionTimeStamp: 123123.121,
-        error: null,
-        originalTransaction: null);
+      transactionIdentifier: '',
+      payment: SKPaymentWrapper(productIdentifier: id, quantity: quantity),
+      transactionState: SKPaymentTransactionStateWrapper.purchasing,
+      transactionTimeStamp: 123123.121,
+      error: null,
+      originalTransaction: null,
+    );
   }
 
   SKPaymentTransactionWrapper createPurchasedTransaction(
-      String productId, String transactionId) {
+      String productId, String transactionId,
+      {int quantity = 1}) {
     return SKPaymentTransactionWrapper(
-        payment: SKPaymentWrapper(productIdentifier: productId),
+        payment:
+            SKPaymentWrapper(productIdentifier: productId, quantity: quantity),
         transactionState: SKPaymentTransactionStateWrapper.purchased,
         transactionTimeStamp: 123123.121,
         transactionIdentifier: transactionId,
@@ -77,10 +81,12 @@
         originalTransaction: null);
   }
 
-  SKPaymentTransactionWrapper createFailedTransaction(String productId) {
+  SKPaymentTransactionWrapper createFailedTransaction(String productId,
+      {int quantity = 1}) {
     return SKPaymentTransactionWrapper(
         transactionIdentifier: '',
-        payment: SKPaymentWrapper(productIdentifier: productId),
+        payment:
+            SKPaymentWrapper(productIdentifier: productId, quantity: quantity),
         transactionState: SKPaymentTransactionStateWrapper.failed,
         transactionTimeStamp: 123123.121,
         error: const SKError(
@@ -91,10 +97,12 @@
   }
 
   SKPaymentTransactionWrapper createCanceledTransaction(
-      String productId, int errorCode) {
+      String productId, int errorCode,
+      {int quantity = 1}) {
     return SKPaymentTransactionWrapper(
         transactionIdentifier: '',
-        payment: SKPaymentWrapper(productIdentifier: productId),
+        payment:
+            SKPaymentWrapper(productIdentifier: productId, quantity: quantity),
         transactionState: SKPaymentTransactionStateWrapper.failed,
         transactionTimeStamp: 123123.121,
         error: SKError(
@@ -105,9 +113,11 @@
   }
 
   SKPaymentTransactionWrapper createRestoredTransaction(
-      String productId, String transactionId) {
+      String productId, String transactionId,
+      {int quantity = 1}) {
     return SKPaymentTransactionWrapper(
-        payment: SKPaymentWrapper(productIdentifier: productId),
+        payment:
+            SKPaymentWrapper(productIdentifier: productId, quantity: quantity),
         transactionState: SKPaymentTransactionStateWrapper.restored,
         transactionTimeStamp: 123123.121,
         transactionIdentifier: transactionId,
@@ -166,25 +176,29 @@
         return Future<void>.sync(() {});
       case '-[InAppPurchasePlugin addPayment:result:]':
         final String id = call.arguments['productIdentifier'] as String;
+        final int quantity = call.arguments['quantity'] as int;
         final SKPaymentTransactionWrapper transaction =
-            createPendingTransaction(id);
+            createPendingTransaction(id, quantity: quantity);
+        transactions.add(transaction);
         InAppPurchaseStoreKitPlatform.observer.updatedTransactions(
             transactions: <SKPaymentTransactionWrapper>[transaction]);
         sleep(const Duration(milliseconds: 30));
         if (testTransactionFail) {
           final SKPaymentTransactionWrapper transactionFailed =
-              createFailedTransaction(id);
+              createFailedTransaction(id, quantity: quantity);
           InAppPurchaseStoreKitPlatform.observer.updatedTransactions(
               transactions: <SKPaymentTransactionWrapper>[transactionFailed]);
         } else if (testTransactionCancel > 0) {
           final SKPaymentTransactionWrapper transactionCanceled =
-              createCanceledTransaction(id, testTransactionCancel);
+              createCanceledTransaction(id, testTransactionCancel,
+                  quantity: quantity);
           InAppPurchaseStoreKitPlatform.observer.updatedTransactions(
               transactions: <SKPaymentTransactionWrapper>[transactionCanceled]);
         } else {
           final SKPaymentTransactionWrapper transactionFinished =
               createPurchasedTransaction(
-                  id, transaction.transactionIdentifier ?? '');
+                  id, transaction.transactionIdentifier ?? '',
+                  quantity: quantity);
           InAppPurchaseStoreKitPlatform.observer.updatedTransactions(
               transactions: <SKPaymentTransactionWrapper>[transactionFinished]);
         }
@@ -192,7 +206,8 @@
       case '-[InAppPurchasePlugin finishTransaction:result:]':
         finishedTransactions.add(createPurchasedTransaction(
             call.arguments['productIdentifier'] as String,
-            call.arguments['transactionIdentifier'] as String));
+            call.arguments['transactionIdentifier'] as String,
+            quantity: transactions.first.payment.quantity));
         break;
       case '-[SKPaymentQueue startObservingTransactionQueue]':
         queueIsActive = true;
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart
index e92d848..852599a 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart
+++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart
@@ -427,6 +427,68 @@
       final PurchaseStatus purchaseStatus = await completer.future;
       expect(purchaseStatus, PurchaseStatus.canceled);
     });
+
+    test(
+        'buying non consumable, should be able to purchase multiple quantity of one product',
+        () async {
+      final List<PurchaseDetails> details = <PurchaseDetails>[];
+      final Completer<List<PurchaseDetails>> completer =
+          Completer<List<PurchaseDetails>>();
+      final Stream<List<PurchaseDetails>> stream =
+          iapStoreKitPlatform.purchaseStream;
+      late StreamSubscription<List<PurchaseDetails>> subscription;
+      subscription = stream.listen((List<PurchaseDetails> purchaseDetailsList) {
+        details.addAll(purchaseDetailsList);
+        for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
+          if (purchaseDetails.pendingCompletePurchase) {
+            iapStoreKitPlatform.completePurchase(purchaseDetails);
+            completer.complete(details);
+            subscription.cancel();
+          }
+        }
+      });
+      final AppStoreProductDetails productDetails =
+          AppStoreProductDetails.fromSKProduct(dummyProductWrapper);
+      final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam(
+          productDetails: productDetails,
+          quantity: 5,
+          applicationUserName: 'appName');
+      await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam);
+      await completer.future;
+      expect(
+          fakeStoreKitPlatform.finishedTransactions.first.payment.quantity, 5);
+    });
+
+    test(
+        'buying consumable, should be able to purchase multiple quantity of one product',
+        () async {
+      final List<PurchaseDetails> details = <PurchaseDetails>[];
+      final Completer<List<PurchaseDetails>> completer =
+          Completer<List<PurchaseDetails>>();
+      final Stream<List<PurchaseDetails>> stream =
+          iapStoreKitPlatform.purchaseStream;
+      late StreamSubscription<List<PurchaseDetails>> subscription;
+      subscription = stream.listen((List<PurchaseDetails> purchaseDetailsList) {
+        details.addAll(purchaseDetailsList);
+        for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
+          if (purchaseDetails.pendingCompletePurchase) {
+            iapStoreKitPlatform.completePurchase(purchaseDetails);
+            completer.complete(details);
+            subscription.cancel();
+          }
+        }
+      });
+      final AppStoreProductDetails productDetails =
+          AppStoreProductDetails.fromSKProduct(dummyProductWrapper);
+      final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam(
+          productDetails: productDetails,
+          quantity: 5,
+          applicationUserName: 'appName');
+      await iapStoreKitPlatform.buyConsumable(purchaseParam: purchaseParam);
+      await completer.future;
+      expect(
+          fakeStoreKitPlatform.finishedTransactions.first.payment.quantity, 5);
+    });
   });
 
   group('complete purchase', () {