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', () {