Skip to content

Commit

Permalink
Added ability to purchase multiple quantity of one product (flutter#5711
Browse files Browse the repository at this point in the history
)
  • Loading branch information
vlad0209 authored and mauricioluz committed Jan 26, 2023
1 parent 7e2306d commit f43c4f4
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AppStorePurchaseParam extends PurchaseParam {
AppStorePurchaseParam({
required ProductDetails productDetails,
String? applicationUserName,
this.quantity = 1,
this.simulatesAskToBuyInSandbox = false,
}) : super(
productDetails: productDetails,
Expand All @@ -28,4 +29,7 @@ class AppStorePurchaseParam extends PurchaseParam {
///
/// See also [SKPaymentWrapper.simulatesAskToBuyInSandbox].
final bool simulatesAskToBuyInSandbox;

/// Quantity of the product user requested to buy.
final int quantity;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: in_app_purchase_storekit
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,37 @@ class FakeStoreKitPlatform {
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,
error: null,
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(
Expand All @@ -91,10 +97,12 @@ class FakeStoreKitPlatform {
}

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(
Expand All @@ -105,9 +113,11 @@ class FakeStoreKitPlatform {
}

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,
Expand Down Expand Up @@ -166,33 +176,38 @@ class FakeStoreKitPlatform {
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]);
}
break;
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,68 @@ void main() {
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', () {
Expand Down

0 comments on commit f43c4f4

Please sign in to comment.