From 2f19ce80e3334e46822a339d050154ee4a61183f Mon Sep 17 00:00:00 2001 From: Haidar Date: Sat, 15 Nov 2025 22:45:56 +0200 Subject: [PATCH 1/4] Add Transaction.unfinished API and expose appAccountToken in SK2PurchaseDetails This PR adds two new features to in_app_purchase_storekit for StoreKit 2: 1. SK2Transaction.unfinishedTransactions() - Queries only unfinished transactions, mirroring Apple's Transaction.unfinished API for better performance. 2. SK2PurchaseDetails.appAccountToken - Exposes the UUID that associates transactions with users in custom backend systems. Both features are additive and maintain full backward compatibility. Tests included for both features. --- .../in_app_purchase_storekit/CHANGELOG.md | 5 + .../InAppPurchasePlugin+StoreKit2.swift | 30 + .../StoreKit2/StoreKit2Messages.g.swift | 24 +- .../lib/src/sk2_pigeon.g.dart | 514 +++++++++--------- .../sk2_transaction_wrapper.dart | 14 +- .../src/types/app_store_purchase_details.dart | 5 + .../pigeons/sk2_pigeon.dart | 3 + .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 12 + ...app_purchase_storekit_2_platform_test.dart | 48 ++ 10 files changed, 388 insertions(+), 269 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 60407b4c907..22c26fd86a9 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,8 @@ +## 0.4.7 + +* Adds `SK2Transaction.unfinishedTransactions()` method to query only unfinished transactions. +* Exposes `appAccountToken` property in `SK2PurchaseDetails` for user identification. + ## 0.4.6+2 * Updates to Pigeon 26. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift index da8874e0a1c..f8191aeb357 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift @@ -230,6 +230,22 @@ extension InAppPurchasePlugin: InAppPurchase2API { } } + /// Wrapper method around StoreKit2's Transaction.unfinished + /// https://developer.apple.com/documentation/storekit/transaction/unfinished + func unfinishedTransactions( + completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void + ) { + Task { + @MainActor in + do { + let transactionsMsgs = await rawUnfinishedTransactions().map { + $0.convertToPigeon(receipt: nil) + } + completion(.success(transactionsMsgs)) + } + } + } + func restorePurchases(completion: @escaping (Result) -> Void) { Task { [weak self] in guard let self = self else { return } @@ -362,6 +378,20 @@ extension InAppPurchasePlugin: InAppPurchase2API { return transactions } + /// Helper function that fetches and unwraps all verified unfinished transactions + func rawUnfinishedTransactions() async -> [Transaction] { + var transactions: [Transaction] = [] + for await verificationResult in Transaction.unfinished { + switch verificationResult { + case .verified(let transaction): + transactions.append(transaction) + case .unverified: + break + } + } + return transactions + } + /// Helper function to fetch specific transaction func fetchTransaction(by id: UInt64) async throws -> Transaction? { for await result in Transaction.all { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift index 82341c6f6b0..9dd3fa5cc10 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift @@ -73,7 +73,7 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } -func deepEqualsStoreKit2Messages(_ lhs: Any?, _ rhs: Any?) -> Bool { +func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? switch (cleanLhs, cleanRhs) { @@ -114,7 +114,7 @@ func deepEqualsStoreKit2Messages(_ lhs: Any?, _ rhs: Any?) -> Bool { } } -func deepHashStoreKit2Messages(value: Any?, hasher: inout Hasher) { +func deepHashsk2_pigeon(value: Any?, hasher: inout Hasher) { if let valueList = value as? [AnyHashable] { for item in valueList { deepHashStoreKit2Messages(value: item, hasher: &hasher) } return @@ -727,6 +727,8 @@ protocol InAppPurchase2API { func isIntroductoryOfferEligible( productId: String, completion: @escaping (Result) -> Void) func transactions(completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void) + func unfinishedTransactions( + completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void) func finish(id: Int64, completion: @escaping (Result) -> Void) func startListeningToTransactions() throws func stopListeningToTransactions() throws @@ -860,6 +862,24 @@ class InAppPurchase2APISetup { } else { transactionsChannel.setMessageHandler(nil) } + let unfinishedTransactionsChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + unfinishedTransactionsChannel.setMessageHandler { _, reply in + api.unfinishedTransactions { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + unfinishedTransactionsChannel.setMessageHandler(nil) + } let finishChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart index 0d40ad3578b..1de225dae32 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart @@ -18,11 +18,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({ - Object? result, - PlatformException? error, - bool empty = false, -}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -31,40 +27,37 @@ List wrapResponse({ } return [error.code, error.message, error.details]; } - bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed.every( - ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), - ); + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every( - (MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key]), - ); + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } + enum SK2ProductTypeMessage { /// A consumable in-app purchase. consumable, - /// A non-consumable in-app purchase. nonConsumable, - /// A non-renewing subscription. nonRenewable, - /// An auto-renewable subscription. autoRenewable, } -enum SK2SubscriptionOfferTypeMessage { introductory, promotional, winBack } +enum SK2SubscriptionOfferTypeMessage { + introductory, + promotional, + winBack, +} enum SK2SubscriptionOfferPaymentModeMessage { payAsYouGo, @@ -72,7 +65,12 @@ enum SK2SubscriptionOfferPaymentModeMessage { freeTrial, } -enum SK2SubscriptionPeriodUnitMessage { day, week, month, year } +enum SK2SubscriptionPeriodUnitMessage { + day, + week, + month, + year, +} enum SK2ProductPurchaseResultMessage { success, @@ -104,12 +102,18 @@ class SK2SubscriptionOfferMessage { SK2SubscriptionOfferPaymentModeMessage paymentMode; List _toList() { - return [id, price, type, period, periodCount, paymentMode]; + return [ + id, + price, + type, + period, + periodCount, + paymentMode, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2SubscriptionOfferMessage decode(Object result) { result as List; @@ -126,8 +130,7 @@ class SK2SubscriptionOfferMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferMessage || - other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -138,11 +141,15 @@ class SK2SubscriptionOfferMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2SubscriptionPeriodMessage { - SK2SubscriptionPeriodMessage({required this.value, required this.unit}); + SK2SubscriptionPeriodMessage({ + required this.value, + required this.unit, + }); /// The number of units that the period represents. int value; @@ -151,12 +158,14 @@ class SK2SubscriptionPeriodMessage { SK2SubscriptionPeriodUnitMessage unit; List _toList() { - return [value, unit]; + return [ + value, + unit, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2SubscriptionPeriodMessage decode(Object result) { result as List; @@ -169,8 +178,7 @@ class SK2SubscriptionPeriodMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionPeriodMessage || - other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionPeriodMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -181,7 +189,8 @@ class SK2SubscriptionPeriodMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2SubscriptionInfoMessage { @@ -209,14 +218,12 @@ class SK2SubscriptionInfoMessage { } Object encode() { - return _toList(); - } + return _toList(); } static SK2SubscriptionInfoMessage decode(Object result) { result as List; return SK2SubscriptionInfoMessage( - promotionalOffers: (result[0] as List?)! - .cast(), + promotionalOffers: (result[0] as List?)!.cast(), subscriptionGroupID: result[1]! as String, subscriptionPeriod: result[2]! as SK2SubscriptionPeriodMessage, ); @@ -225,8 +232,7 @@ class SK2SubscriptionInfoMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionInfoMessage || - other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionInfoMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -237,7 +243,8 @@ class SK2SubscriptionInfoMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// A Pigeon message class representing a Product @@ -292,8 +299,7 @@ class SK2ProductMessage { } Object encode() { - return _toList(); - } + return _toList(); } static SK2ProductMessage decode(Object result) { result as List; @@ -323,7 +329,8 @@ class SK2ProductMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2PriceLocaleMessage { @@ -337,12 +344,14 @@ class SK2PriceLocaleMessage { String currencySymbol; List _toList() { - return [currencyCode, currencySymbol]; + return [ + currencyCode, + currencySymbol, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2PriceLocaleMessage decode(Object result) { result as List; @@ -366,7 +375,8 @@ class SK2PriceLocaleMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// A Pigeon message class representing a Signature @@ -388,12 +398,16 @@ class SK2SubscriptionOfferSignatureMessage { String signature; List _toList() { - return [keyID, nonce, timestamp, signature]; + return [ + keyID, + nonce, + timestamp, + signature, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2SubscriptionOfferSignatureMessage decode(Object result) { result as List; @@ -408,8 +422,7 @@ class SK2SubscriptionOfferSignatureMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferSignatureMessage || - other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferSignatureMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -420,7 +433,8 @@ class SK2SubscriptionOfferSignatureMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2SubscriptionOfferPurchaseMessage { @@ -434,27 +448,27 @@ class SK2SubscriptionOfferPurchaseMessage { SK2SubscriptionOfferSignatureMessage promotionalOfferSignature; List _toList() { - return [promotionalOfferId, promotionalOfferSignature]; + return [ + promotionalOfferId, + promotionalOfferSignature, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2SubscriptionOfferPurchaseMessage decode(Object result) { result as List; return SK2SubscriptionOfferPurchaseMessage( promotionalOfferId: result[0]! as String, - promotionalOfferSignature: - result[1]! as SK2SubscriptionOfferSignatureMessage, + promotionalOfferSignature: result[1]! as SK2SubscriptionOfferSignatureMessage, ); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferPurchaseMessage || - other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferPurchaseMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -465,7 +479,8 @@ class SK2SubscriptionOfferPurchaseMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2ProductPurchaseOptionsMessage { @@ -494,8 +509,7 @@ class SK2ProductPurchaseOptionsMessage { } Object encode() { - return _toList(); - } + return _toList(); } static SK2ProductPurchaseOptionsMessage decode(Object result) { result as List; @@ -510,8 +524,7 @@ class SK2ProductPurchaseOptionsMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2ProductPurchaseOptionsMessage || - other.runtimeType != runtimeType) { + if (other is! SK2ProductPurchaseOptionsMessage || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -522,7 +535,8 @@ class SK2ProductPurchaseOptionsMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2TransactionMessage { @@ -579,8 +593,7 @@ class SK2TransactionMessage { } Object encode() { - return _toList(); - } + return _toList(); } static SK2TransactionMessage decode(Object result) { result as List; @@ -613,11 +626,16 @@ class SK2TransactionMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class SK2ErrorMessage { - SK2ErrorMessage({required this.code, required this.domain, this.userInfo}); + SK2ErrorMessage({ + required this.code, + required this.domain, + this.userInfo, + }); int code; @@ -626,12 +644,15 @@ class SK2ErrorMessage { Map? userInfo; List _toList() { - return [code, domain, userInfo]; + return [ + code, + domain, + userInfo, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SK2ErrorMessage decode(Object result) { result as List; @@ -656,9 +677,11 @@ class SK2ErrorMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -666,49 +689,49 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SK2ProductTypeMessage) { + } else if (value is SK2ProductTypeMessage) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferTypeMessage) { + } else if (value is SK2SubscriptionOfferTypeMessage) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferPaymentModeMessage) { + } else if (value is SK2SubscriptionOfferPaymentModeMessage) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionPeriodUnitMessage) { + } else if (value is SK2SubscriptionPeriodUnitMessage) { buffer.putUint8(132); writeValue(buffer, value.index); - } else if (value is SK2ProductPurchaseResultMessage) { + } else if (value is SK2ProductPurchaseResultMessage) { buffer.putUint8(133); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferMessage) { + } else if (value is SK2SubscriptionOfferMessage) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionPeriodMessage) { + } else if (value is SK2SubscriptionPeriodMessage) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionInfoMessage) { + } else if (value is SK2SubscriptionInfoMessage) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is SK2ProductMessage) { + } else if (value is SK2ProductMessage) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is SK2PriceLocaleMessage) { + } else if (value is SK2PriceLocaleMessage) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionOfferSignatureMessage) { + } else if (value is SK2SubscriptionOfferSignatureMessage) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionOfferPurchaseMessage) { + } else if (value is SK2SubscriptionOfferPurchaseMessage) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is SK2ProductPurchaseOptionsMessage) { + } else if (value is SK2ProductPurchaseOptionsMessage) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is SK2TransactionMessage) { + } else if (value is SK2TransactionMessage) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is SK2ErrorMessage) { + } else if (value is SK2ErrorMessage) { buffer.putUint8(143); writeValue(buffer, value.encode()); } else { @@ -719,48 +742,40 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SK2ProductTypeMessage.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; - return value == null - ? null - : SK2SubscriptionOfferTypeMessage.values[value]; - case 131: + return value == null ? null : SK2SubscriptionOfferTypeMessage.values[value]; + case 131: final int? value = readValue(buffer) as int?; - return value == null - ? null - : SK2SubscriptionOfferPaymentModeMessage.values[value]; - case 132: + return value == null ? null : SK2SubscriptionOfferPaymentModeMessage.values[value]; + case 132: final int? value = readValue(buffer) as int?; - return value == null - ? null - : SK2SubscriptionPeriodUnitMessage.values[value]; - case 133: + return value == null ? null : SK2SubscriptionPeriodUnitMessage.values[value]; + case 133: final int? value = readValue(buffer) as int?; - return value == null - ? null - : SK2ProductPurchaseResultMessage.values[value]; - case 134: + return value == null ? null : SK2ProductPurchaseResultMessage.values[value]; + case 134: return SK2SubscriptionOfferMessage.decode(readValue(buffer)!); - case 135: + case 135: return SK2SubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SK2SubscriptionInfoMessage.decode(readValue(buffer)!); - case 137: + case 137: return SK2ProductMessage.decode(readValue(buffer)!); - case 138: + case 138: return SK2PriceLocaleMessage.decode(readValue(buffer)!); - case 139: + case 139: return SK2SubscriptionOfferSignatureMessage.decode(readValue(buffer)!); - case 140: + case 140: return SK2SubscriptionOfferPurchaseMessage.decode(readValue(buffer)!); - case 141: + case 141: return SK2ProductPurchaseOptionsMessage.decode(readValue(buffer)!); - case 142: + case 142: return SK2TransactionMessage.decode(readValue(buffer)!); - case 143: + case 143: return SK2ErrorMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -772,13 +787,9 @@ class InAppPurchase2API { /// Constructor for [InAppPurchase2API]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - InAppPurchase2API({ - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty - ? '.$messageChannelSuffix' - : ''; + InAppPurchase2API({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -786,14 +797,12 @@ class InAppPurchase2API { final String pigeonVar_messageChannelSuffix; Future canMakePayments() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.canMakePayments$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.canMakePayments$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -816,17 +825,13 @@ class InAppPurchase2API { } Future> products(List identifiers) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.products$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [identifiers], + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.products$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([identifiers]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -843,26 +848,18 @@ class InAppPurchase2API { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)! - .cast(); + return (pigeonVar_replyList[0] as List?)!.cast(); } } - Future purchase( - String id, { - SK2ProductPurchaseOptionsMessage? options, - }) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.purchase$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [id, options], + Future purchase(String id, {SK2ProductPurchaseOptionsMessage? options}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.purchase$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([id, options]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -884,17 +881,13 @@ class InAppPurchase2API { } Future isWinBackOfferEligible(String productId, String offerId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isWinBackOfferEligible$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [productId, offerId], + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isWinBackOfferEligible$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([productId, offerId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -916,17 +909,13 @@ class InAppPurchase2API { } Future isIntroductoryOfferEligible(String productId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isIntroductoryOfferEligible$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [productId], + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isIntroductoryOfferEligible$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([productId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -948,14 +937,40 @@ class InAppPurchase2API { } Future> transactions() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.transactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.transactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } + + Future> unfinishedTransactions() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -973,23 +988,18 @@ class InAppPurchase2API { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)! - .cast(); + return (pigeonVar_replyList[0] as List?)!.cast(); } } Future finish(int id) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [id], + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([id]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -1006,14 +1016,12 @@ class InAppPurchase2API { } Future startListeningToTransactions() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.startListeningToTransactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.startListeningToTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1031,14 +1039,12 @@ class InAppPurchase2API { } Future stopListeningToTransactions() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.stopListeningToTransactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.stopListeningToTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1056,14 +1062,12 @@ class InAppPurchase2API { } Future restorePurchases() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1081,14 +1085,12 @@ class InAppPurchase2API { } Future countryCode() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1111,14 +1113,12 @@ class InAppPurchase2API { } Future sync() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1141,45 +1141,29 @@ abstract class InAppPurchase2CallbackAPI { void onTransactionsUpdated(List newTransactions); - static void setUp( - InAppPurchase2CallbackAPI? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty - ? '.$messageChannelSuffix' - : ''; + static void setUp(InAppPurchase2CallbackAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel - pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert( - message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null.', - ); + assert(message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null.'); final List args = (message as List?)!; - final List? arg_newTransactions = - (args[0] as List?)?.cast(); - assert( - arg_newTransactions != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null, expected non-null List.', - ); + final List? arg_newTransactions = (args[0] as List?)?.cast(); + assert(arg_newTransactions != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null, expected non-null List.'); try { api.onTransactionsUpdated(arg_newTransactions!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } 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 bc09ff5c96f..9e9ac6531c0 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 @@ -76,7 +76,7 @@ class SK2Transaction { /// A wrapper around [Transaction.all] /// https://developer.apple.com/documentation/storekit/transaction/3851203-all - /// A sequence that emits all the customer’s transactions for your app. + /// A sequence that emits all the customer's transactions for your app. static Future> transactions() async { final List msgs = await hostApi2.transactions(); final List transactions = msgs @@ -85,6 +85,17 @@ class SK2Transaction { return transactions; } + /// A wrapper around [Transaction.unfinished] + /// https://developer.apple.com/documentation/storekit/transaction/unfinished + /// A sequence that emits unfinished transactions for the customer. + static Future> unfinishedTransactions() async { + final List msgs = await hostApi2.unfinishedTransactions(); + final List transactions = msgs + .map((SK2TransactionMessage e) => e.convertFromPigeon()) + .toList(); + return transactions; + } + /// Start listening to transactions. /// Call this as soon as you can your app to avoid missing transactions. static void startListeningToTransactions() { @@ -135,6 +146,7 @@ extension on SK2TransactionMessage { // Any failed transaction will simply not be returned. status: restoring ? PurchaseStatus.restored : PurchaseStatus.purchased, purchaseID: id.toString(), + appAccountToken: appAccountToken, ); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart index 5964137dc83..66a935dbde5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart @@ -91,8 +91,13 @@ class SK2PurchaseDetails extends PurchaseDetails { required super.verificationData, required super.transactionDate, required super.status, + this.appAccountToken, }); + /// A UUID that associates the transaction with a user on your own service. + /// This is the value set when making the purchase via appAccountToken option. + final String? appAccountToken; + @override bool get pendingCompletePurchase => status == PurchaseStatus.purchased; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart index ff6e42a7414..780ad63779a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart @@ -240,6 +240,9 @@ abstract class InAppPurchase2API { @async List transactions(); + @async + List unfinishedTransactions(); + @async void finish(int id); 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 cb642ec20ae..46309f2a9fc 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.6+2 +version: 0.4.7 environment: sdk: ^3.9.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 4b155205af5..94d065a9639 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 @@ -450,6 +450,18 @@ class FakeStoreKit2Platform implements InAppPurchase2API { ]); } + @override + Future> unfinishedTransactions() { + return Future>.value([ + SK2TransactionMessage( + id: 123, + originalId: 123, + productId: 'product_id', + purchaseDate: '12-12', + ), + ]); + } + @override Future startListeningToTransactions() async { isListenerRegistered = true; 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 8ce69c30066..4203839df6f 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 @@ -665,4 +665,52 @@ void main() { }, ); }); + + group('unfinished transactions', () { + test('should return unfinished transactions', () async { + final List transactions = + await SK2Transaction.unfinishedTransactions(); + + expect(transactions, isNotEmpty); + expect(transactions.first.id, '123'); + expect(transactions.first.productId, 'product_id'); + }); + }); + + group('appAccountToken exposure', () { + test('should expose appAccountToken in SK2PurchaseDetails', () async { + const String testToken = 'test-uuid-12345'; + final SK2PurchaseDetails details = SK2PurchaseDetails( + productID: 'test_product', + purchaseID: '999', + verificationData: PurchaseVerificationData( + localVerificationData: '', + serverVerificationData: '', + source: 'app_store', + ), + transactionDate: '2025-11-15', + status: PurchaseStatus.purchased, + appAccountToken: testToken, + ); + + expect(details.appAccountToken, testToken); + }); + + test('should handle null appAccountToken in SK2PurchaseDetails', () async { + final SK2PurchaseDetails details = SK2PurchaseDetails( + productID: 'test_product', + purchaseID: '999', + verificationData: PurchaseVerificationData( + localVerificationData: '', + serverVerificationData: '', + source: 'app_store', + ), + transactionDate: '2025-11-15', + status: PurchaseStatus.purchased, + appAccountToken: null, + ); + + expect(details.appAccountToken, isNull); + }); + }); } From 45b43d3ecef479d95bb876f13e070f42a7ed87dc Mon Sep 17 00:00:00 2001 From: Haidar Date: Sun, 16 Nov 2025 21:00:20 +0200 Subject: [PATCH 2/4] Add JWS representation support to unfinishedTransactions Previously returned nil for receipt data, now properly includes jwsRepresentation for server-side verification. --- .../InAppPurchasePlugin+StoreKit2.swift | 26 +++++++------------ .../sk2_transaction_wrapper.dart | 8 +++++- .../test/fakes/fake_storekit_platform.dart | 1 + ...app_purchase_storekit_2_platform_test.dart | 10 ++++++- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift index f8191aeb357..817d506558b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift @@ -238,8 +238,16 @@ extension InAppPurchasePlugin: InAppPurchase2API { Task { @MainActor in do { - let transactionsMsgs = await rawUnfinishedTransactions().map { - $0.convertToPigeon(receipt: nil) + var transactionsMsgs: [SK2TransactionMessage] = [] + for await verificationResult in Transaction.unfinished { + switch verificationResult { + case .verified(let transaction): + transactionsMsgs.append( + transaction.convertToPigeon(receipt: verificationResult.jwsRepresentation) + ) + case .unverified: + break + } } completion(.success(transactionsMsgs)) } @@ -378,20 +386,6 @@ extension InAppPurchasePlugin: InAppPurchase2API { return transactions } - /// Helper function that fetches and unwraps all verified unfinished transactions - func rawUnfinishedTransactions() async -> [Transaction] { - var transactions: [Transaction] = [] - for await verificationResult in Transaction.unfinished { - switch verificationResult { - case .verified(let transaction): - transactions.append(transaction) - case .unverified: - break - } - } - return transactions - } - /// Helper function to fetch specific transaction func fetchTransaction(by id: UInt64) async throws -> Transaction? { for await result in Transaction.all { 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 9e9ac6531c0..5a703f4e19a 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 @@ -27,6 +27,7 @@ class SK2Transaction { this.subscriptionGroupID, this.price, this.error, + this.receiptData, this.jsonRepresentation, }); @@ -63,7 +64,11 @@ class SK2Transaction { /// Any error returned from StoreKit final SKError? error; - /// The json representation of a transaction + /// The JWS (JSON Web Signature) representation of the transaction. + /// This is the jwsRepresentation from StoreKit used for server-side verification. + final String? receiptData; + + /// The json representation of a transaction. final String? jsonRepresentation; /// Wrapper around [Transaction.finish] @@ -122,6 +127,7 @@ extension on SK2TransactionMessage { purchaseDate: purchaseDate, expirationDate: expirationDate, appAccountToken: appAccountToken, + receiptData: receiptData, jsonRepresentation: jsonRepresentation, ); } 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 94d065a9639..9f8069b3dc5 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 @@ -458,6 +458,7 @@ class FakeStoreKit2Platform implements InAppPurchase2API { originalId: 123, productId: 'product_id', purchaseDate: '12-12', + receiptData: 'fake_jws_representation', ), ]); } 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 4203839df6f..7b1a6965f90 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 @@ -675,6 +675,15 @@ void main() { expect(transactions.first.id, '123'); expect(transactions.first.productId, 'product_id'); }); + + test('should expose receiptData (JWS) in unfinished transactions', () async { + final List transactions = + await SK2Transaction.unfinishedTransactions(); + + expect(transactions, isNotEmpty); + expect(transactions.first.receiptData, isNotNull); + expect(transactions.first.receiptData, 'fake_jws_representation'); + }); }); group('appAccountToken exposure', () { @@ -707,7 +716,6 @@ void main() { ), transactionDate: '2025-11-15', status: PurchaseStatus.purchased, - appAccountToken: null, ); expect(details.appAccountToken, isNull); From b1070766f60c916896df54475a259a6adbaa6bcc Mon Sep 17 00:00:00 2001 From: Haidar Date: Tue, 18 Nov 2025 18:56:38 +0200 Subject: [PATCH 3/4] Regenerated pigeon files with version 26.1.0 and manually added swift-format-ignore comments for deepEqualssk2_pigeon and deepHashsk2_pigeon functions as a workaround for flutter/flutter#178736. --- .../StoreKit2/StoreKit2Messages.g.swift | 2 + .../lib/src/sk2_pigeon.g.dart | 503 ++++++++++-------- .../sk2_transaction_wrapper.dart | 3 +- ...app_purchase_storekit_2_platform_test.dart | 17 +- 4 files changed, 289 insertions(+), 236 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift index 9dd3fa5cc10..c8fe9208f99 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift @@ -73,6 +73,7 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +// swift-format-ignore: AlwaysUseLowerCamelCase func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -114,6 +115,7 @@ func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool { } } +// swift-format-ignore: AlwaysUseLowerCamelCase func deepHashsk2_pigeon(value: Any?, hasher: inout Hasher) { if let valueList = value as? [AnyHashable] { for item in valueList { deepHashStoreKit2Messages(value: item, hasher: &hasher) } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart index 1de225dae32..01d2e3981be 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart @@ -18,7 +18,11 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({ + Object? result, + PlatformException? error, + bool empty = false, +}) { if (empty) { return []; } @@ -27,37 +31,40 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } + bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + a.indexed.every( + ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), + ); } if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + return a.length == b.length && + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } - enum SK2ProductTypeMessage { /// A consumable in-app purchase. consumable, + /// A non-consumable in-app purchase. nonConsumable, + /// A non-renewing subscription. nonRenewable, + /// An auto-renewable subscription. autoRenewable, } -enum SK2SubscriptionOfferTypeMessage { - introductory, - promotional, - winBack, -} +enum SK2SubscriptionOfferTypeMessage { introductory, promotional, winBack } enum SK2SubscriptionOfferPaymentModeMessage { payAsYouGo, @@ -65,12 +72,7 @@ enum SK2SubscriptionOfferPaymentModeMessage { freeTrial, } -enum SK2SubscriptionPeriodUnitMessage { - day, - week, - month, - year, -} +enum SK2SubscriptionPeriodUnitMessage { day, week, month, year } enum SK2ProductPurchaseResultMessage { success, @@ -102,18 +104,12 @@ class SK2SubscriptionOfferMessage { SK2SubscriptionOfferPaymentModeMessage paymentMode; List _toList() { - return [ - id, - price, - type, - period, - periodCount, - paymentMode, - ]; + return [id, price, type, period, periodCount, paymentMode]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2SubscriptionOfferMessage decode(Object result) { result as List; @@ -130,7 +126,8 @@ class SK2SubscriptionOfferMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferMessage || other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -141,15 +138,11 @@ class SK2SubscriptionOfferMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2SubscriptionPeriodMessage { - SK2SubscriptionPeriodMessage({ - required this.value, - required this.unit, - }); + SK2SubscriptionPeriodMessage({required this.value, required this.unit}); /// The number of units that the period represents. int value; @@ -158,14 +151,12 @@ class SK2SubscriptionPeriodMessage { SK2SubscriptionPeriodUnitMessage unit; List _toList() { - return [ - value, - unit, - ]; + return [value, unit]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2SubscriptionPeriodMessage decode(Object result) { result as List; @@ -178,7 +169,8 @@ class SK2SubscriptionPeriodMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionPeriodMessage || other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionPeriodMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -189,8 +181,7 @@ class SK2SubscriptionPeriodMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2SubscriptionInfoMessage { @@ -218,12 +209,14 @@ class SK2SubscriptionInfoMessage { } Object encode() { - return _toList(); } + return _toList(); + } static SK2SubscriptionInfoMessage decode(Object result) { result as List; return SK2SubscriptionInfoMessage( - promotionalOffers: (result[0] as List?)!.cast(), + promotionalOffers: (result[0] as List?)! + .cast(), subscriptionGroupID: result[1]! as String, subscriptionPeriod: result[2]! as SK2SubscriptionPeriodMessage, ); @@ -232,7 +225,8 @@ class SK2SubscriptionInfoMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionInfoMessage || other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionInfoMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -243,8 +237,7 @@ class SK2SubscriptionInfoMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } /// A Pigeon message class representing a Product @@ -299,7 +292,8 @@ class SK2ProductMessage { } Object encode() { - return _toList(); } + return _toList(); + } static SK2ProductMessage decode(Object result) { result as List; @@ -329,8 +323,7 @@ class SK2ProductMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2PriceLocaleMessage { @@ -344,14 +337,12 @@ class SK2PriceLocaleMessage { String currencySymbol; List _toList() { - return [ - currencyCode, - currencySymbol, - ]; + return [currencyCode, currencySymbol]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2PriceLocaleMessage decode(Object result) { result as List; @@ -375,8 +366,7 @@ class SK2PriceLocaleMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } /// A Pigeon message class representing a Signature @@ -398,16 +388,12 @@ class SK2SubscriptionOfferSignatureMessage { String signature; List _toList() { - return [ - keyID, - nonce, - timestamp, - signature, - ]; + return [keyID, nonce, timestamp, signature]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2SubscriptionOfferSignatureMessage decode(Object result) { result as List; @@ -422,7 +408,8 @@ class SK2SubscriptionOfferSignatureMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferSignatureMessage || other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferSignatureMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -433,8 +420,7 @@ class SK2SubscriptionOfferSignatureMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2SubscriptionOfferPurchaseMessage { @@ -448,27 +434,27 @@ class SK2SubscriptionOfferPurchaseMessage { SK2SubscriptionOfferSignatureMessage promotionalOfferSignature; List _toList() { - return [ - promotionalOfferId, - promotionalOfferSignature, - ]; + return [promotionalOfferId, promotionalOfferSignature]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2SubscriptionOfferPurchaseMessage decode(Object result) { result as List; return SK2SubscriptionOfferPurchaseMessage( promotionalOfferId: result[0]! as String, - promotionalOfferSignature: result[1]! as SK2SubscriptionOfferSignatureMessage, + promotionalOfferSignature: + result[1]! as SK2SubscriptionOfferSignatureMessage, ); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2SubscriptionOfferPurchaseMessage || other.runtimeType != runtimeType) { + if (other is! SK2SubscriptionOfferPurchaseMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -479,8 +465,7 @@ class SK2SubscriptionOfferPurchaseMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2ProductPurchaseOptionsMessage { @@ -509,7 +494,8 @@ class SK2ProductPurchaseOptionsMessage { } Object encode() { - return _toList(); } + return _toList(); + } static SK2ProductPurchaseOptionsMessage decode(Object result) { result as List; @@ -524,7 +510,8 @@ class SK2ProductPurchaseOptionsMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! SK2ProductPurchaseOptionsMessage || other.runtimeType != runtimeType) { + if (other is! SK2ProductPurchaseOptionsMessage || + other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -535,8 +522,7 @@ class SK2ProductPurchaseOptionsMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2TransactionMessage { @@ -593,7 +579,8 @@ class SK2TransactionMessage { } Object encode() { - return _toList(); } + return _toList(); + } static SK2TransactionMessage decode(Object result) { result as List; @@ -626,16 +613,11 @@ class SK2TransactionMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SK2ErrorMessage { - SK2ErrorMessage({ - required this.code, - required this.domain, - this.userInfo, - }); + SK2ErrorMessage({required this.code, required this.domain, this.userInfo}); int code; @@ -644,15 +626,12 @@ class SK2ErrorMessage { Map? userInfo; List _toList() { - return [ - code, - domain, - userInfo, - ]; + return [code, domain, userInfo]; } Object encode() { - return _toList(); } + return _toList(); + } static SK2ErrorMessage decode(Object result) { result as List; @@ -677,11 +656,9 @@ class SK2ErrorMessage { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -689,49 +666,49 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SK2ProductTypeMessage) { + } else if (value is SK2ProductTypeMessage) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferTypeMessage) { + } else if (value is SK2SubscriptionOfferTypeMessage) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferPaymentModeMessage) { + } else if (value is SK2SubscriptionOfferPaymentModeMessage) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionPeriodUnitMessage) { + } else if (value is SK2SubscriptionPeriodUnitMessage) { buffer.putUint8(132); writeValue(buffer, value.index); - } else if (value is SK2ProductPurchaseResultMessage) { + } else if (value is SK2ProductPurchaseResultMessage) { buffer.putUint8(133); writeValue(buffer, value.index); - } else if (value is SK2SubscriptionOfferMessage) { + } else if (value is SK2SubscriptionOfferMessage) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionPeriodMessage) { + } else if (value is SK2SubscriptionPeriodMessage) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionInfoMessage) { + } else if (value is SK2SubscriptionInfoMessage) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is SK2ProductMessage) { + } else if (value is SK2ProductMessage) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is SK2PriceLocaleMessage) { + } else if (value is SK2PriceLocaleMessage) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionOfferSignatureMessage) { + } else if (value is SK2SubscriptionOfferSignatureMessage) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is SK2SubscriptionOfferPurchaseMessage) { + } else if (value is SK2SubscriptionOfferPurchaseMessage) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is SK2ProductPurchaseOptionsMessage) { + } else if (value is SK2ProductPurchaseOptionsMessage) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is SK2TransactionMessage) { + } else if (value is SK2TransactionMessage) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is SK2ErrorMessage) { + } else if (value is SK2ErrorMessage) { buffer.putUint8(143); writeValue(buffer, value.encode()); } else { @@ -742,40 +719,48 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SK2ProductTypeMessage.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; - return value == null ? null : SK2SubscriptionOfferTypeMessage.values[value]; - case 131: + return value == null + ? null + : SK2SubscriptionOfferTypeMessage.values[value]; + case 131: final int? value = readValue(buffer) as int?; - return value == null ? null : SK2SubscriptionOfferPaymentModeMessage.values[value]; - case 132: + return value == null + ? null + : SK2SubscriptionOfferPaymentModeMessage.values[value]; + case 132: final int? value = readValue(buffer) as int?; - return value == null ? null : SK2SubscriptionPeriodUnitMessage.values[value]; - case 133: + return value == null + ? null + : SK2SubscriptionPeriodUnitMessage.values[value]; + case 133: final int? value = readValue(buffer) as int?; - return value == null ? null : SK2ProductPurchaseResultMessage.values[value]; - case 134: + return value == null + ? null + : SK2ProductPurchaseResultMessage.values[value]; + case 134: return SK2SubscriptionOfferMessage.decode(readValue(buffer)!); - case 135: + case 135: return SK2SubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SK2SubscriptionInfoMessage.decode(readValue(buffer)!); - case 137: + case 137: return SK2ProductMessage.decode(readValue(buffer)!); - case 138: + case 138: return SK2PriceLocaleMessage.decode(readValue(buffer)!); - case 139: + case 139: return SK2SubscriptionOfferSignatureMessage.decode(readValue(buffer)!); - case 140: + case 140: return SK2SubscriptionOfferPurchaseMessage.decode(readValue(buffer)!); - case 141: + case 141: return SK2ProductPurchaseOptionsMessage.decode(readValue(buffer)!); - case 142: + case 142: return SK2TransactionMessage.decode(readValue(buffer)!); - case 143: + case 143: return SK2ErrorMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -787,9 +772,13 @@ class InAppPurchase2API { /// Constructor for [InAppPurchase2API]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - InAppPurchase2API({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + InAppPurchase2API({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -797,12 +786,14 @@ class InAppPurchase2API { final String pigeonVar_messageChannelSuffix; Future canMakePayments() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.canMakePayments$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.canMakePayments$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -825,13 +816,17 @@ class InAppPurchase2API { } Future> products(List identifiers) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.products$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.products$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [identifiers], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([identifiers]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -848,18 +843,26 @@ class InAppPurchase2API { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)!.cast(); + return (pigeonVar_replyList[0] as List?)! + .cast(); } } - Future purchase(String id, {SK2ProductPurchaseOptionsMessage? options}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.purchase$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + Future purchase( + String id, { + SK2ProductPurchaseOptionsMessage? options, + }) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.purchase$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [id, options], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([id, options]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -881,13 +884,17 @@ class InAppPurchase2API { } Future isWinBackOfferEligible(String productId, String offerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isWinBackOfferEligible$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isWinBackOfferEligible$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [productId, offerId], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([productId, offerId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -909,13 +916,17 @@ class InAppPurchase2API { } Future isIntroductoryOfferEligible(String productId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isIntroductoryOfferEligible$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isIntroductoryOfferEligible$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [productId], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([productId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -937,12 +948,14 @@ class InAppPurchase2API { } Future> transactions() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.transactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.transactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -960,17 +973,20 @@ class InAppPurchase2API { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)!.cast(); + return (pigeonVar_replyList[0] as List?)! + .cast(); } } Future> unfinishedTransactions() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -988,18 +1004,23 @@ class InAppPurchase2API { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)!.cast(); + return (pigeonVar_replyList[0] as List?)! + .cast(); } } Future finish(int id) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [id], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([id]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -1016,12 +1037,14 @@ class InAppPurchase2API { } Future startListeningToTransactions() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.startListeningToTransactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.startListeningToTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1039,12 +1062,14 @@ class InAppPurchase2API { } Future stopListeningToTransactions() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.stopListeningToTransactions$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.stopListeningToTransactions$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1062,12 +1087,14 @@ class InAppPurchase2API { } Future restorePurchases() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1085,12 +1112,14 @@ class InAppPurchase2API { } Future countryCode() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1113,12 +1142,14 @@ class InAppPurchase2API { } Future sync() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -1141,29 +1172,45 @@ abstract class InAppPurchase2CallbackAPI { void onTransactionsUpdated(List newTransactions); - static void setUp(InAppPurchase2CallbackAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + InAppPurchase2CallbackAPI? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated$messageChannelSuffix', pigeonChannelCodec, - binaryMessenger: binaryMessenger); + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null.'); + assert( + message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null.', + ); final List args = (message as List?)!; - final List? arg_newTransactions = (args[0] as List?)?.cast(); - assert(arg_newTransactions != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null, expected non-null List.'); + final List? arg_newTransactions = + (args[0] as List?)?.cast(); + assert( + arg_newTransactions != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null, expected non-null List.', + ); try { api.onTransactionsUpdated(arg_newTransactions!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); } }); } 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 5a703f4e19a..55305cc4b4e 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 @@ -94,7 +94,8 @@ class SK2Transaction { /// https://developer.apple.com/documentation/storekit/transaction/unfinished /// A sequence that emits unfinished transactions for the customer. static Future> unfinishedTransactions() async { - final List msgs = await hostApi2.unfinishedTransactions(); + final List msgs = await hostApi2 + .unfinishedTransactions(); final List transactions = msgs .map((SK2TransactionMessage e) => e.convertFromPigeon()) .toList(); 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 7b1a6965f90..a9910991207 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 @@ -676,14 +676,17 @@ void main() { expect(transactions.first.productId, 'product_id'); }); - test('should expose receiptData (JWS) in unfinished transactions', () async { - final List transactions = - await SK2Transaction.unfinishedTransactions(); + test( + 'should expose receiptData (JWS) in unfinished transactions', + () async { + final List transactions = + await SK2Transaction.unfinishedTransactions(); - expect(transactions, isNotEmpty); - expect(transactions.first.receiptData, isNotNull); - expect(transactions.first.receiptData, 'fake_jws_representation'); - }); + expect(transactions, isNotEmpty); + expect(transactions.first.receiptData, isNotNull); + expect(transactions.first.receiptData, 'fake_jws_representation'); + }, + ); }); group('appAccountToken exposure', () { From 6445537805af34326528b58f57de77e8ac8c871e Mon Sep 17 00:00:00 2001 From: Haidar Date: Wed, 19 Nov 2025 15:35:31 +0200 Subject: [PATCH 4/4] Updates pigeon from 26.1.0 to 26.1.1 and regenerates the pigeon files. --- .../StoreKit2/StoreKit2Messages.g.swift | 8 +++----- .../in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart | 2 +- .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift index c8fe9208f99..eba2f16eabe 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.1), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -73,8 +73,7 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } -// swift-format-ignore: AlwaysUseLowerCamelCase -func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool { +func deepEqualsStoreKit2Messages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? switch (cleanLhs, cleanRhs) { @@ -115,8 +114,7 @@ func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool { } } -// swift-format-ignore: AlwaysUseLowerCamelCase -func deepHashsk2_pigeon(value: Any?, hasher: inout Hasher) { +func deepHashStoreKit2Messages(value: Any?, hasher: inout Hasher) { if let valueList = value as? [AnyHashable] { for item in valueList { deepHashStoreKit2Messages(value: item, hasher: &hasher) } return diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart index 01d2e3981be..9f0c9956704 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers 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 46309f2a9fc..5882c914518 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -31,7 +31,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - pigeon: ^26.1.0 + pigeon: ^26.1.1 test: ^1.16.0 topics: