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 21796591786..888ded48642 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.22 + +* Adds `sync()` and `countryCode()`. + ## 0.3.21 * Adds Swift Package Manager compatibility. 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 05300e0c33d..4422ebc0f35 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 @@ -6,7 +6,6 @@ import StoreKit @available(iOS 15.0, macOS 12.0, *) extension InAppPurchasePlugin: InAppPurchase2API { - // MARK: - Pigeon Functions /// Wrapper method around StoreKit2's canMakePayments() method @@ -146,6 +145,43 @@ extension InAppPurchasePlugin: InAppPurchase2API { } } + /// Wrapper method around StoreKit2's countryCode() method + /// https://developer.apple.com/documentation/storekit/storefront/countrycode + func countryCode(completion: @escaping (Result) -> Void) { + Task { + guard let currentStorefront = await Storefront.current else { + let error = PigeonError( + code: "storekit2_failed_to_fetch_country_code", + message: "Storekit has failed to fetch the country code.", + details: "Storefront.current returned nil.") + completion(.failure(error)) + return + } + completion(.success(currentStorefront.countryCode)) + return + } + } + + /// Wrapper method around StoreKit2's sync() method + /// https://developer.apple.com/documentation/storekit/appstore/sync() + /// When called, a system prompt will ask users to enter their authentication details + func sync(completion: @escaping (Result) -> Void) { + Task { + do { + try await AppStore.sync() + completion(.success(())) + return + } catch { + let pigeonError = PigeonError( + code: "storekit2_failed_to_sync_to_app_store", + message: "Storekit has failed to sync to the app store.", + details: "\(error)") + completion(.failure(pigeonError)) + return + } + } + } + /// This Task listens to Transation.updates as shown here /// https://developer.apple.com/documentation/storekit/transaction/3851206-updates /// This function should be called as soon as the app starts to avoid missing any Transactions done outside of the app. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/sk2_pigeon.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/sk2_pigeon.g.swift index 25c848efda2..e53c62bba06 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/sk2_pigeon.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/sk2_pigeon.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.7.3), do not edit directly. +// Autogenerated from Pigeon (v25.1.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -18,9 +18,9 @@ import Foundation final class PigeonError: Error { let code: String let message: String? - let details: Any? + let details: Sendable? - init(code: String, message: String?, details: Any?) { + init(code: String, message: String?, details: Sendable?) { self.code = code self.message = message self.details = details @@ -521,6 +521,8 @@ protocol InAppPurchase2API { func startListeningToTransactions() throws func stopListeningToTransactions() throws func restorePurchases(completion: @escaping (Result) -> Void) + func countryCode(completion: @escaping (Result) -> Void) + func sync(completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -676,6 +678,41 @@ class InAppPurchase2APISetup { } else { restorePurchasesChannel.setMessageHandler(nil) } + let countryCodeChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + countryCodeChannel.setMessageHandler { _, reply in + api.countryCode { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + countryCodeChannel.setMessageHandler(nil) + } + let syncChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + syncChannel.setMessageHandler { _, reply in + api.sync { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + syncChannel.setMessageHandler(nil) + } } } /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. @@ -713,7 +750,7 @@ class InAppPurchase2CallbackAPI: InAppPurchase2CallbackAPIProtocol { let details: String? = nilOrValue(listResponse[2]) completion(.failure(PigeonError(code: code, message: message, details: details))) } else { - completion(.success(Void())) + completion(.success(())) } } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 51787d9c642..6272dcd583e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -241,6 +241,9 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { /// See: https://developer.apple.com/documentation/storekit/skstorefront?language=objc @override Future countryCode() async { + if (_useStoreKit2) { + return Storefront().countryCode(); + } return (await _skPaymentQueueWrapper.storefront())?.countryCode ?? ''; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart index 3b88aacc16e..447f41f0a18 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart @@ -3,16 +3,25 @@ // found in the LICENSE file. import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; -import '../in_app_purchase_storekit.dart'; +import '../in_app_purchase_storekit.dart'; +import '../store_kit_2_wrappers.dart'; import '../store_kit_wrappers.dart'; /// Contains InApp Purchase features that are only available on iOS. class InAppPurchaseStoreKitPlatformAddition extends InAppPurchasePlatformAddition { + /// Synchronizes your app’s transaction information and subscription status + /// with information from the App Store. + /// Storekit 2 only + Future sync() { + return AppStore().sync(); + } + /// Present Code Redemption Sheet. /// /// Available on devices running iOS 14 and iPadOS 14 and later. + /// Available for StoreKit 1 and 2 Future presentCodeRedemptionSheet() { return SKPaymentQueueWrapper().presentCodeRedemptionSheet(); } 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 749c50f9fff..a27af1af409 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. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.7.3), do not edit directly. +// Autogenerated from Pigeon (v25.1.0), 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 @@ -29,6 +29,22 @@ 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])); + } + if (a is Map && b is Map) { + final Iterable keys = (a as Map).keys; + return a.length == b.length && + keys.every((Object? key) => + (b as Map).containsKey(key) && + _deepEquals(a[key], b[key])); + } + return a == b; +} + enum SK2ProductTypeMessage { /// A consumable in-app purchase. consumable, @@ -89,7 +105,7 @@ class SK2SubscriptionOfferMessage { SK2SubscriptionOfferPaymentModeMessage paymentMode; - Object encode() { + List _toList() { return [ id, price, @@ -100,6 +116,10 @@ class SK2SubscriptionOfferMessage { ]; } + Object encode() { + return _toList(); + } + static SK2SubscriptionOfferMessage decode(Object result) { result as List; return SK2SubscriptionOfferMessage( @@ -111,6 +131,28 @@ class SK2SubscriptionOfferMessage { paymentMode: result[5]! as SK2SubscriptionOfferPaymentModeMessage, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2SubscriptionOfferMessage || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return id == other.id && + price == other.price && + type == other.type && + period == other.period && + periodCount == other.periodCount && + paymentMode == other.paymentMode; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2SubscriptionPeriodMessage { @@ -125,13 +167,17 @@ class SK2SubscriptionPeriodMessage { /// The unit of time that this period represents. SK2SubscriptionPeriodUnitMessage unit; - Object encode() { + List _toList() { return [ value, unit, ]; } + Object encode() { + return _toList(); + } + static SK2SubscriptionPeriodMessage decode(Object result) { result as List; return SK2SubscriptionPeriodMessage( @@ -139,6 +185,23 @@ class SK2SubscriptionPeriodMessage { unit: result[1]! as SK2SubscriptionPeriodUnitMessage, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2SubscriptionPeriodMessage || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return value == other.value && unit == other.unit; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2SubscriptionInfoMessage { @@ -157,7 +220,7 @@ class SK2SubscriptionInfoMessage { /// The duration that this subscription lasts before auto-renewing. SK2SubscriptionPeriodMessage subscriptionPeriod; - Object encode() { + List _toList() { return [ promotionalOffers, subscriptionGroupID, @@ -165,6 +228,10 @@ class SK2SubscriptionInfoMessage { ]; } + Object encode() { + return _toList(); + } + static SK2SubscriptionInfoMessage decode(Object result) { result as List; return SK2SubscriptionInfoMessage( @@ -174,6 +241,25 @@ class SK2SubscriptionInfoMessage { subscriptionPeriod: result[2]! as SK2SubscriptionPeriodMessage, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2SubscriptionInfoMessage || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(promotionalOffers, other.promotionalOffers) && + subscriptionGroupID == other.subscriptionGroupID && + subscriptionPeriod == other.subscriptionPeriod; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } /// A Pigeon message class representing a Product @@ -214,7 +300,7 @@ class SK2ProductMessage { /// The currency and locale information for this product SK2PriceLocaleMessage priceLocale; - Object encode() { + List _toList() { return [ id, displayName, @@ -227,6 +313,10 @@ class SK2ProductMessage { ]; } + Object encode() { + return _toList(); + } + static SK2ProductMessage decode(Object result) { result as List; return SK2ProductMessage( @@ -240,6 +330,29 @@ class SK2ProductMessage { priceLocale: result[7]! as SK2PriceLocaleMessage, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2ProductMessage || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return id == other.id && + displayName == other.displayName && + description == other.description && + price == other.price && + displayPrice == other.displayPrice && + type == other.type && + subscription == other.subscription && + priceLocale == other.priceLocale; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2PriceLocaleMessage { @@ -252,13 +365,17 @@ class SK2PriceLocaleMessage { String currencySymbol; - Object encode() { + List _toList() { return [ currencyCode, currencySymbol, ]; } + Object encode() { + return _toList(); + } + static SK2PriceLocaleMessage decode(Object result) { result as List; return SK2PriceLocaleMessage( @@ -266,6 +383,23 @@ class SK2PriceLocaleMessage { currencySymbol: result[1]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2PriceLocaleMessage || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return currencyCode == other.currencyCode && + currencySymbol == other.currencySymbol; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2ProductPurchaseOptionsMessage { @@ -278,13 +412,17 @@ class SK2ProductPurchaseOptionsMessage { int? quantity; - Object encode() { + List _toList() { return [ appAccountToken, quantity, ]; } + Object encode() { + return _toList(); + } + static SK2ProductPurchaseOptionsMessage decode(Object result) { result as List; return SK2ProductPurchaseOptionsMessage( @@ -292,6 +430,24 @@ class SK2ProductPurchaseOptionsMessage { quantity: result[1] as int?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2ProductPurchaseOptionsMessage || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return appAccountToken == other.appAccountToken && + quantity == other.quantity; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2TransactionMessage { @@ -331,7 +487,7 @@ class SK2TransactionMessage { String? jsonRepresentation; - Object encode() { + List _toList() { return [ id, originalId, @@ -347,6 +503,10 @@ class SK2TransactionMessage { ]; } + Object encode() { + return _toList(); + } + static SK2TransactionMessage decode(Object result) { result as List; return SK2TransactionMessage( @@ -363,6 +523,32 @@ class SK2TransactionMessage { jsonRepresentation: result[10] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2TransactionMessage || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return id == other.id && + originalId == other.originalId && + productId == other.productId && + purchaseDate == other.purchaseDate && + expirationDate == other.expirationDate && + purchasedQuantity == other.purchasedQuantity && + appAccountToken == other.appAccountToken && + restoring == other.restoring && + receiptData == other.receiptData && + error == other.error && + jsonRepresentation == other.jsonRepresentation; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class SK2ErrorMessage { @@ -378,7 +564,7 @@ class SK2ErrorMessage { Map? userInfo; - Object encode() { + List _toList() { return [ code, domain, @@ -386,6 +572,10 @@ class SK2ErrorMessage { ]; } + Object encode() { + return _toList(); + } + static SK2ErrorMessage decode(Object result) { result as List; return SK2ErrorMessage( @@ -394,6 +584,24 @@ class SK2ErrorMessage { userInfo: (result[2] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SK2ErrorMessage || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return code == other.code && + domain == other.domain && + _deepEquals(userInfo, other.userInfo); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); } class _PigeonCodec extends StandardMessageCodec { @@ -519,8 +727,9 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -548,8 +757,10 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([identifiers]); final List? pigeonVar_replyList = - await pigeonVar_channel.send([identifiers]) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -579,8 +790,10 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([id, options]); final List? pigeonVar_replyList = - await pigeonVar_channel.send([id, options]) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -608,8 +821,9 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -638,8 +852,10 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([id]); final List? pigeonVar_replyList = - await pigeonVar_channel.send([id]) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -662,8 +878,9 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -686,8 +903,9 @@ class InAppPurchase2API { pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -710,8 +928,64 @@ class InAppPurchase2API { 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 { + return; + } + } + + 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 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 String?)!; + } + } + + 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 Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; + await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_appstore_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_appstore_wrapper.dart index a57ad561126..fc8ef570e5c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_appstore_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_appstore_wrapper.dart @@ -11,8 +11,16 @@ InAppPurchase2API _hostApi = InAppPurchase2API(); final class AppStore { /// Dart wrapper for StoreKit2's canMakePayments() /// Returns a bool that indicates whether the person can make purchases. - /// (https://developer.apple.com/documentation/storekit/appstore/3822277-canmakepayments) + /// https://developer.apple.com/documentation/storekit/appstore/3822277-canmakepayments Future canMakePayments() { return _hostApi.canMakePayments(); } + + /// Dart wrapper for StoreKit2's sync() + /// Synchronizes your app’s transaction information and subscription status with information from the App Store. + /// Will initiate an authentication pop up. + /// https://developer.apple.com/documentation/storekit/appstore/sync() + Future sync() { + return _hostApi.sync(); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_storefront_wrapper.dart new file mode 100644 index 00000000000..4d7ebcfa9bd --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_storefront_wrapper.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import '../../store_kit_2_wrappers.dart'; + +InAppPurchase2API _hostApi = InAppPurchase2API(); + +/// Wrapper for StoreKit2's Storefront +/// (https://developer.apple.com/documentation/storekit/storefront) +final class Storefront { + /// Dart wrapper for StoreKit2's countryCode() + /// Returns the 3 letter code for a store's locale + /// (https://developer.apple.com/documentation/storekit/storefront/countrycode) + Future countryCode() async { + return _hostApi.countryCode(); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/store_kit_2_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/store_kit_2_wrappers.dart index 05482180b1a..c34c24a7c1a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/store_kit_2_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/store_kit_2_wrappers.dart @@ -5,4 +5,5 @@ export 'src/sk2_pigeon.g.dart'; export 'src/store_kit_2_wrappers/sk2_appstore_wrapper.dart'; export 'src/store_kit_2_wrappers/sk2_product_wrapper.dart'; +export 'src/store_kit_2_wrappers/sk2_storefront_wrapper.dart'; export 'src/store_kit_2_wrappers/sk2_transaction_wrapper.dart'; 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 cfb92b57b92..da57a65ee6e 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 @@ -199,6 +199,12 @@ abstract class InAppPurchase2API { @async void restorePurchases(); + + @async + String countryCode(); + + @async + void sync(); } @FlutterApi() 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 c6d973ce1f0..447f9077d11 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.3.21 +version: 0.3.22 environment: sdk: ^3.4.0 @@ -31,7 +31,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - pigeon: ^22.4.2 + pigeon: ^25.1.0 test: ^1.16.0 topics: 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 15ea45f2a42..bf8773e8bd9 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 @@ -394,6 +394,16 @@ class FakeStoreKit2Platform implements TestInAppPurchase2Api { InAppPurchaseStoreKitPlatform.sk2TransactionObserver .onTransactionsUpdated(transactionList); } + + @override + Future countryCode() async { + return 'ABC'; + } + + @override + Future sync() { + return Future.value(); + } } SK2TransactionMessage createPendingTransaction(String id, {int quantity = 1}) { 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 658932e7ce6..d9a40562f59 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 @@ -192,4 +192,12 @@ void main() { } }); }); + + group('billing configuration', () { + test('country_code', () async { + const String expectedCountryCode = 'ABC'; + final String countryCode = await iapStoreKitPlatform.countryCode(); + expect(countryCode, expectedCountryCode); + }); + }); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/sk2_test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/sk2_test_api.g.dart index 3a51df7105e..57399529e55 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/sk2_test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/sk2_test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.7.3), do not edit directly. +// Autogenerated from Pigeon (v25.1.0), 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, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports @@ -134,6 +134,10 @@ abstract class TestInAppPurchase2Api { Future restorePurchases(); + Future countryCode(); + + Future sync(); + static void setUp( TestInAppPurchase2Api? api, { BinaryMessenger? binaryMessenger, @@ -372,5 +376,57 @@ abstract class TestInAppPurchase2Api { }); } } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + try { + final String output = await api.countryCode(); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + try { + await api.sync(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } }