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 e34d332e942f..aeb236752ec8 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.18+4 + +* Adds StoreKit 2 support for restoring purchases. + ## 0.3.18+3 * Updates Pigeon for non-nullable collection type support. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchaseStoreKit2.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift similarity index 80% rename from packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchaseStoreKit2.swift rename to packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift index cd8e96b5d135..47daff575302 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchaseStoreKit2.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift @@ -96,13 +96,43 @@ extension InAppPurchasePlugin: InAppPurchase2API { @MainActor in do { let transactionsMsgs = await rawTransactions().map { - $0.convertToPigeon() + $0.convertToPigeon(receipt: nil) } completion(.success(transactionsMsgs)) } } } + func restorePurchases(completion: @escaping (Result) -> Void) { + Task { [weak self] in + guard let self = self else { return } + do { + var unverifiedPurchases: [UInt64: (receipt: String, error: Error?)] = [:] + for await completedPurchase in Transaction.currentEntitlements { + switch completedPurchase { + case .verified(let purchase): + self.sendTransactionUpdate( + transaction: purchase, receipt: "\(completedPurchase.jwsRepresentation)") + case .unverified(let failedPurchase, let error): + unverifiedPurchases[failedPurchase.id] = ( + receipt: completedPurchase.jwsRepresentation, error: error + ) + } + } + if !unverifiedPurchases.isEmpty { + completion( + .failure( + PigeonError( + code: "storekit2_restore_failed", + message: + "This purchase could not be restored.", + details: unverifiedPurchases))) + } + completion(.success(Void())) + } + } + } + /// Wrapper method around StoreKit2's finish() method https://developer.apple.com/documentation/storekit/transaction/3749694-finish func finish(id: Int64, completion: @escaping (Result) -> Void) { Task { @@ -136,9 +166,10 @@ extension InAppPurchasePlugin: InAppPurchase2API { } /// Sends an transaction back to Dart. Access these transactions with `purchaseStream` - func sendTransactionUpdate(transaction: Transaction) { - let transactionMessage = transaction.convertToPigeon() - transactionCallbackAPI?.onTransactionsUpdated(newTransaction: transactionMessage) { result in + private func sendTransactionUpdate(transaction: Transaction, receipt: String? = nil) { + let transactionMessage = transaction.convertToPigeon(receipt: receipt) + self.transactionCallbackAPI?.onTransactionsUpdated(newTransactions: [transactionMessage]) { + result in switch result { case .success: break case .failure(let error): diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift index b04caa25ae64..f2b2511afa98 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift @@ -186,7 +186,7 @@ extension Product.PurchaseResult { @available(iOS 15.0, macOS 12.0, *) extension Transaction { - func convertToPigeon(restoring: Bool = false) -> SK2TransactionMessage { + func convertToPigeon(receipt: String?) -> SK2TransactionMessage { let dateFromatter: DateFormatter = DateFormatter() dateFromatter.dateFormat = "yyyy-MM-dd HH:mm:ss" @@ -198,7 +198,8 @@ extension Transaction { purchaseDate: dateFromatter.string(from: purchaseDate), purchasedQuantity: Int64(purchasedQuantity), appAccountToken: appAccountToken?.uuidString, - restoring: restoring + restoring: receipt != nil, + receiptData: receipt ) } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift index f6ff6bfe63b3..f79f51b260de 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -315,6 +315,7 @@ struct SK2TransactionMessage { var purchasedQuantity: Int64 var appAccountToken: String? = nil var restoring: Bool + var receiptData: String? = nil var error: SK2ErrorMessage? = nil // swift-format-ignore: AlwaysUseLowerCamelCase @@ -326,7 +327,8 @@ struct SK2TransactionMessage { let purchasedQuantity = pigeonVar_list[4] as! Int64 let appAccountToken: String? = nilOrValue(pigeonVar_list[5]) let restoring = pigeonVar_list[6] as! Bool - let error: SK2ErrorMessage? = nilOrValue(pigeonVar_list[7]) + let receiptData: String? = nilOrValue(pigeonVar_list[7]) + let error: SK2ErrorMessage? = nilOrValue(pigeonVar_list[8]) return SK2TransactionMessage( id: id, @@ -336,6 +338,7 @@ struct SK2TransactionMessage { purchasedQuantity: purchasedQuantity, appAccountToken: appAccountToken, restoring: restoring, + receiptData: receiptData, error: error ) } @@ -348,6 +351,7 @@ struct SK2TransactionMessage { purchasedQuantity, appAccountToken, restoring, + receiptData, error, ] } @@ -508,6 +512,7 @@ protocol InAppPurchase2API { func finish(id: Int64, completion: @escaping (Result) -> Void) func startListeningToTransactions() throws func stopListeningToTransactions() throws + func restorePurchases(completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -645,12 +650,30 @@ class InAppPurchase2APISetup { } else { stopListeningToTransactionsChannel.setMessageHandler(nil) } + let restorePurchasesChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + restorePurchasesChannel.setMessageHandler { _, reply in + api.restorePurchases { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + restorePurchasesChannel.setMessageHandler(nil) + } } } /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol InAppPurchase2CallbackAPIProtocol { func onTransactionsUpdated( - newTransaction newTransactionArg: SK2TransactionMessage, + newTransactions newTransactionsArg: [SK2TransactionMessage], completion: @escaping (Result) -> Void) } class InAppPurchase2CallbackAPI: InAppPurchase2CallbackAPIProtocol { @@ -664,14 +687,14 @@ class InAppPurchase2CallbackAPI: InAppPurchase2CallbackAPIProtocol { return sk2_pigeonPigeonCodec.shared } func onTransactionsUpdated( - newTransaction newTransactionArg: SK2TransactionMessage, + newTransactions newTransactionsArg: [SK2TransactionMessage], completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([newTransactionArg] as [Any?]) { response in + channel.sendMessage([newTransactionsArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift index c2fcfbdfedf0..34caa9271331 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift @@ -226,4 +226,28 @@ final class InAppPurchase2PluginTests: XCTestCase { } await fulfillment(of: [expectation], timeout: 5) } + + func testRestoreProductSuccess() async throws { + let purchaseExpectation = self.expectation(description: "Purchase request should succeed") + let restoreExpectation = self.expectation(description: "Restore request should succeed") + + plugin.purchase(id: "subscription_silver", options: nil) { result in + switch result { + case .success(_): + purchaseExpectation.fulfill() + case .failure(let error): + XCTFail("Purchase should NOT fail. Failed with \(error)") + } + } + plugin.restorePurchases { result in + switch result { + case .success(): + restoreExpectation.fulfill() + case .failure(let error): + XCTFail("Restore purchases should NOT fail. Failed with \(error)") + } + } + + await fulfillment(of: [restoreExpectation, purchaseExpectation], timeout: 5) + } } 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 f930e57a8e7a..e74b46ddfc2f 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 @@ -51,7 +51,7 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { /// Callback handler for transaction status changes for StoreKit2 transactions @visibleForTesting - static SK2TransactionObserverWrapper get sk2transactionObserver => + static SK2TransactionObserverWrapper get sk2TransactionObserver => _sk2transactionObserver; /// Registers this class as the default instance of [InAppPurchasePlatform]. @@ -149,6 +149,9 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { @override Future restorePurchases({String? applicationUserName}) async { + if (_useStoreKit2) { + return SK2Transaction.restorePurchases(); + } return _sk1transactionObserver .restoreTransactions( queue: _skPaymentQueueWrapper, 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 8c6753fdf205..ac154ddce079 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.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 @@ -303,6 +303,7 @@ class SK2TransactionMessage { this.purchasedQuantity = 1, this.appAccountToken, this.restoring = false, + this.receiptData, this.error, }); @@ -320,6 +321,8 @@ class SK2TransactionMessage { bool restoring; + String? receiptData; + SK2ErrorMessage? error; Object encode() { @@ -331,6 +334,7 @@ class SK2TransactionMessage { purchasedQuantity, appAccountToken, restoring, + receiptData, error, ]; } @@ -345,7 +349,8 @@ class SK2TransactionMessage { purchasedQuantity: result[4]! as int, appAccountToken: result[5] as String?, restoring: result[6]! as bool, - error: result[7] as SK2ErrorMessage?, + receiptData: result[7] as String?, + error: result[8] as SK2ErrorMessage?, ); } } @@ -685,12 +690,36 @@ class InAppPurchase2API { return; } } + + 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 List? pigeonVar_replyList = + await pigeonVar_channel.send(null) 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; + } + } } abstract class InAppPurchase2CallbackAPI { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onTransactionsUpdated(SK2TransactionMessage newTransaction); + void onTransactionsUpdated(List newTransactions); static void setUp( InAppPurchase2CallbackAPI? api, { @@ -713,12 +742,12 @@ abstract class InAppPurchase2CallbackAPI { assert(message != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null.'); final List args = (message as List?)!; - final SK2TransactionMessage? arg_newTransaction = - (args[0] as SK2TransactionMessage?); - assert(arg_newTransaction != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated was null, expected non-null SK2TransactionMessage.'); + 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_newTransaction!); + api.onTransactionsUpdated(arg_newTransactions!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); 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 5afefddba7af..4e727dce333b 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 @@ -86,6 +86,11 @@ class SK2Transaction { static void stopListeningToTransactions() { _hostApi.stopListeningToTransactions(); } + + /// Restore previously completed purchases. + static Future restorePurchases() async { + await _hostApi.restorePurchases(); + } } extension on SK2TransactionMessage { @@ -127,8 +132,9 @@ class SK2TransactionObserverWrapper implements InAppPurchase2CallbackAPI { final StreamController> transactionsCreatedController; @override - void onTransactionsUpdated(SK2TransactionMessage newTransaction) { - transactionsCreatedController - .add([newTransaction.convertToDetails()]); + void onTransactionsUpdated(List newTransactions) { + transactionsCreatedController.add(newTransactions + .map((SK2TransactionMessage e) => e.convertToDetails()) + .toList()); } } 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 fbf4c421ac1b..e3aa52232684 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 @@ -144,6 +144,7 @@ class SK2TransactionMessage { this.purchasedQuantity = 1, this.appAccountToken, this.error, + this.receiptData, this.restoring = false}); final int id; final int originalId; @@ -152,6 +153,7 @@ class SK2TransactionMessage { final int purchasedQuantity; final String? appAccountToken; final bool restoring; + final String? receiptData; final SK2ErrorMessage? error; } @@ -189,9 +191,12 @@ abstract class InAppPurchase2API { void startListeningToTransactions(); void stopListeningToTransactions(); + + @async + void restorePurchases(); } @FlutterApi() abstract class InAppPurchase2CallbackAPI { - void onTransactionsUpdated(SK2TransactionMessage newTransaction); + void onTransactionsUpdated(List newTransactions); } 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 4c6da54a5a8a..317bc8d0e107 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.18+3 +version: 0.3.18+4 environment: sdk: ^3.3.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 29932a9f8180..55c490bb62b4 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 @@ -285,7 +285,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { class FakeStoreKit2Platform implements TestInAppPurchase2Api { late Set validProductIDs; late Map validProducts; - late List transactionList; + late List transactionList = []; late bool testTransactionFail; late int testTransactionCancel; late List finishedTransactions; @@ -310,6 +310,18 @@ class FakeStoreKit2Platform implements TestInAppPurchase2Api { } } + SK2TransactionMessage createRestoredTransaction( + String productId, String transactionId, + {int quantity = 1}) { + return SK2TransactionMessage( + id: 123, + originalId: 321, + productId: '', + purchaseDate: '', + appAccountToken: '', + restoring: true); + } + @override bool canMakePayments() { return true; @@ -340,8 +352,8 @@ class FakeStoreKit2Platform implements TestInAppPurchase2Api { {SK2ProductPurchaseOptionsMessage? options}) { final SK2TransactionMessage transaction = createPendingTransaction(id); - InAppPurchaseStoreKitPlatform.sk2transactionObserver - .onTransactionsUpdated(transaction); + InAppPurchaseStoreKitPlatform.sk2TransactionObserver + .onTransactionsUpdated([transaction]); return Future.value( SK2ProductPurchaseResultMessage.success); } @@ -371,6 +383,12 @@ class FakeStoreKit2Platform implements TestInAppPurchase2Api { void stopListeningToTransactions() { isListenerRegistered = false; } + + @override + Future restorePurchases() async { + InAppPurchaseStoreKitPlatform.sk2TransactionObserver + .onTransactionsUpdated(transactionList); + } } 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 02c80b9d9bee..6fb2ba030704 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 @@ -151,4 +151,41 @@ void main() { throwsA(isInstanceOf())); }); }); + + group('restore purchases', () { + test('should emit restored transactions on purchase stream', () async { + fakeStoreKit2Platform.transactionList + .add(fakeStoreKit2Platform.createRestoredTransaction('foo', 'RT1')); + fakeStoreKit2Platform.transactionList + .add(fakeStoreKit2Platform.createRestoredTransaction('foo', 'RT2')); + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { + if (purchaseDetailsList.first.status == PurchaseStatus.restored) { + subscription.cancel(); + completer.complete(purchaseDetailsList); + } + }); + + await iapStoreKitPlatform.restorePurchases(); + final List details = await completer.future; + + expect(details.length, 2); + for (int i = 0; i < fakeStoreKit2Platform.transactionList.length; i++) { + final SK2TransactionMessage expected = + fakeStoreKit2Platform.transactionList[i]; + final PurchaseDetails actual = details[i]; + + expect(actual.purchaseID, expected.id.toString()); + expect(actual.verificationData, isNotNull); + expect(actual.status, PurchaseStatus.restored); + // In storekit 2, restored purchases don't have to finished. + expect(actual.pendingCompletePurchase, false); + } + }); + }); } 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 58850af3ece4..5c595d45c09e 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.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 @@ -132,6 +132,8 @@ abstract class TestInAppPurchase2Api { void stopListeningToTransactions(); + Future restorePurchases(); + static void setUp( TestInAppPurchase2Api? api, { BinaryMessenger? binaryMessenger, @@ -344,5 +346,31 @@ abstract class TestInAppPurchase2Api { }); } } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases$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.restorePurchases(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } }