From 1cd7f78dd01400313dc8fb845f9ff3658ee1ab84 Mon Sep 17 00:00:00 2001 From: louisehsu Date: Tue, 9 Jun 2026 13:14:00 -0700 Subject: [PATCH 1/3] init --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++ .../messages.g.m | 10 ++-- .../sk2_transaction_wrapper.dart | 1 + .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 46 +++++++++++-------- ...app_purchase_storekit_2_platform_test.dart | 18 ++++++++ 6 files changed, 55 insertions(+), 26 deletions(-) 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 20ae28ac9cfc..35217311720b 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.4.10+1 + +* Fixes SK2Transaction to expose the real purchased quantity instead of defaulting to 1. + ## 0.4.10 * Clarifies `completePurchase` usage and the consequences of unfinished transactions in the README and API docstrings. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m index 7be9e75eb517..ae7ef1937a2e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m @@ -733,11 +733,11 @@ void SetUpFIAInAppPurchaseAPIWithSuffix(id binaryMesseng binaryMessenger:binaryMessenger codec:FIAGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers: - completion:)], - @"FIAInAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(startProductRequestProductIdentifiers:completion:)", - api); + NSCAssert( + [api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], + @"FIAInAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startProductRequestProductIdentifiers:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart index 660242745942..50b569f3ab2b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart @@ -126,6 +126,7 @@ extension on SK2TransactionMessage { productId: productId, purchaseDate: purchaseDate ?? '', expirationDate: expirationDate, + quantity: purchasedQuantity, appAccountToken: appAccountToken, receiptData: receiptData, jsonRepresentation: jsonRepresentation, 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 4a3746860f68..ceba6391adaa 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 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/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.4.10 +version: 0.4.10+1 environment: sdk: ^3.10.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 33af9503f736..d9084a89f3a6 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 @@ -319,6 +319,8 @@ class FakeStoreKit2Platform implements InAppPurchase2API { late bool testTransactionFail; late int testTransactionCancel; late List finishedTransactions; + late List transactionsList; + late List unfinishedTransactionsList; PlatformException? queryProductException; bool isListenerRegistered = false; @@ -348,6 +350,28 @@ class FakeStoreKit2Platform implements InAppPurchase2API { eligibleWinBackOffers = >{}; eligibleIntroductoryOffers = {}; simulatedPurchaseResult = SK2ProductPurchaseResultMessage.success; + transactionsList = [ + SK2TransactionMessage( + id: 123, + originalId: 123, + productId: 'product_id', + purchaseDate: '12-12', + purchasedQuantity: 2, + status: SK2PurchaseStatusMessage.purchased, + ), + ]; + unfinishedTransactionsList = [ + SK2TransactionMessage( + id: 123, + originalId: 123, + productId: 'product_id', + purchaseDate: '12-12', + receiptData: 'fake_jws_representation', + appAccountToken: 'fake_app_account_token', + purchasedQuantity: 3, + status: SK2PurchaseStatusMessage.purchased, + ), + ]; } SK2TransactionMessage createRestoredTransaction( @@ -449,30 +473,12 @@ class FakeStoreKit2Platform implements InAppPurchase2API { @override Future> transactions() { - return Future>.value([ - SK2TransactionMessage( - id: 123, - originalId: 123, - productId: 'product_id', - purchaseDate: '12-12', - status: SK2PurchaseStatusMessage.purchased, - ), - ]); + return Future>.value(transactionsList); } @override Future> unfinishedTransactions() { - return Future>.value([ - SK2TransactionMessage( - id: 123, - originalId: 123, - productId: 'product_id', - purchaseDate: '12-12', - receiptData: 'fake_jws_representation', - appAccountToken: 'fake_app_account_token', - status: SK2PurchaseStatusMessage.purchased, - ), - ]); + return Future>.value(unfinishedTransactionsList); } @override diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart index 883a6c468698..6376b73ac43f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart @@ -655,5 +655,23 @@ void main() { expect(transactions.first.appAccountToken, isNotNull); expect(transactions.first.appAccountToken, 'fake_app_account_token'); }); + + test('should expose purchased quantity in unfinished transactions', () async { + final List transactions = await SK2Transaction.unfinishedTransactions(); + + expect(transactions, isNotEmpty); + expect(transactions.first.quantity, 3); + }); + }); + + group('transactions', () { + test('should return transactions', () async { + final List transactions = await SK2Transaction.transactions(); + + expect(transactions, isNotEmpty); + expect(transactions.first.id, '123'); + expect(transactions.first.productId, 'product_id'); + expect(transactions.first.quantity, 2); + }); }); } From 4b7fc37d55cf4e5d3beceedd4cc2eb8b7884e778 Mon Sep 17 00:00:00 2001 From: louisehsu Date: Tue, 9 Jun 2026 14:23:49 -0700 Subject: [PATCH 2/3] format --- .../Sources/in_app_purchase_storekit_objc/messages.g.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m index ae7ef1937a2e..7be9e75eb517 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/messages.g.m @@ -733,11 +733,11 @@ void SetUpFIAInAppPurchaseAPIWithSuffix(id binaryMesseng binaryMessenger:binaryMessenger codec:FIAGetMessagesCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], - @"FIAInAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(startProductRequestProductIdentifiers:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers: + completion:)], + @"FIAInAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startProductRequestProductIdentifiers:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); From 2824e47b0d50c97d7d2b9565e0ee1274a33e22a8 Mon Sep 17 00:00:00 2001 From: louisehsu Date: Tue, 9 Jun 2026 16:46:03 -0700 Subject: [PATCH 3/3] pr comments --- .../test/fakes/fake_storekit_platform.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d9084a89f3a6..29536169b027 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 @@ -319,8 +319,8 @@ class FakeStoreKit2Platform implements InAppPurchase2API { late bool testTransactionFail; late int testTransactionCancel; late List finishedTransactions; - late List transactionsList; - late List unfinishedTransactionsList; + List transactionsList = []; + List unfinishedTransactionsList = []; PlatformException? queryProductException; bool isListenerRegistered = false;