Skip to content

Commit 08a9b2c

Browse files
[in_app_purchase_storekit] Add support for quantity in consumable product purchases (#171570) (#9698)
This PR adds support for specifying a `quantity` when purchasing **consumable** products using StoreKit 2 in the `in_app_purchase_storekit` package. #### ✅ What’s changing * Introduces logic to read the `quantity` field from `PurchaseParam` and insert it into the native StoreKit 2 `.purchase(options:)` call. * Ensures this logic is only applied to consumable products. * Adds null checks and default behavior to maintain compatibility with purchases that don’t specify quantity. #### 🧪 Testing * Added unit tests to verify quantity handling in `Sk2PurchaseParam`: * ✅ Verifies that a custom quantity is passed correctly to native purchase options. * ✅ Ensures that quantity defaults to `1` when not explicitly provided. * Tests cover `buyConsumable` flows using `fakeStoreKit2Platform`. * Verified on a real device using StoreKit 2 with different `quantity` values. * Ensures no change to StoreKit 1 flow or other product types. #### 📌 Fixes Fix [#171570](flutter/flutter#171570)
1 parent 3f38423 commit 08a9b2c

File tree

4 files changed

+44
-1
lines changed

4 files changed

+44
-1
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.4.4
2+
3+
* Added support for specifying quantity in `SK2ProductPurchaseOptions` (consumable purchases on iOS).
4+
15
## 0.4.3
26

37
* Adds **Introductory Offer Eligibility** support for StoreKit2

packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ extension InAppPurchasePlugin: InAppPurchase2API {
6060
{
6161
purchaseOptions.insert(.appAccountToken(accountTokenUUID))
6262
}
63+
if let quantity = options?.quantity {
64+
purchaseOptions.insert(.quantity(Int(quantity)))
65+
}
6366

6467
if #available(iOS 17.4, macOS 14.4, *) {
6568
if let promotionalOffer = options?.promotionalOffer {

packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: in_app_purchase_storekit
22
description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
33
repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
5-
version: 0.4.3
5+
version: 0.4.4
66

77
environment:
88
sdk: ^3.6.0

packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,42 @@ void main() {
260260
expect(lastPurchaseOptions.winBackOfferId, isNull);
261261
expect(lastPurchaseOptions.promotionalOffer, isNull);
262262
});
263+
264+
test('should pass quantity for consumable product with Sk2PurchaseParam',
265+
() async {
266+
final Sk2PurchaseParam purchaseParam = Sk2PurchaseParam(
267+
productDetails:
268+
AppStoreProduct2Details.fromSK2Product(dummyProductWrapper),
269+
quantity: 3,
270+
applicationUserName: 'testUser',
271+
);
272+
273+
await iapStoreKitPlatform.buyConsumable(purchaseParam: purchaseParam);
274+
275+
final SK2ProductPurchaseOptionsMessage lastPurchaseOptions =
276+
fakeStoreKit2Platform.lastPurchaseOptions!;
277+
278+
expect(lastPurchaseOptions.appAccountToken, 'testUser');
279+
expect(lastPurchaseOptions.quantity, 3);
280+
expect(lastPurchaseOptions.winBackOfferId, isNull);
281+
expect(lastPurchaseOptions.promotionalOffer, isNull);
282+
});
283+
284+
test('should default to quantity = 1 when not provided in Sk2PurchaseParam',
285+
() async {
286+
final Sk2PurchaseParam purchaseParam = Sk2PurchaseParam(
287+
productDetails:
288+
AppStoreProduct2Details.fromSK2Product(dummyProductWrapper),
289+
applicationUserName: 'testUser',
290+
);
291+
292+
await iapStoreKitPlatform.buyConsumable(purchaseParam: purchaseParam);
293+
294+
final SK2ProductPurchaseOptionsMessage lastPurchaseOptions =
295+
fakeStoreKit2Platform.lastPurchaseOptions!;
296+
297+
expect(lastPurchaseOptions.quantity, 1);
298+
});
263299
});
264300

265301
group('restore purchases', () {

0 commit comments

Comments
 (0)