From 19393dd7d706edfd2ca1bdd9098edc4d082bb5c9 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 17 Apr 2024 14:42:13 -0700 Subject: [PATCH 01/29] tests passes, pre-clean up --- .../Classes/InAppPurchasePlugin+TestOnly.h | 32 - .../darwin/Classes/InAppPurchasePlugin.h | 23 - .../darwin/Classes/InAppPurchasePlugin.m | 404 ---- .../darwin/Classes/InAppPurchasePlugin.swift | 404 ++++ ...in_app_purchase_storekit-Bridging-Header.h | 11 + .../darwin/Classes/in_app_purchase_storekit.h | 15 + .../darwin/Classes/messages.g.h | 191 +- .../darwin/Classes/messages.g.m | 357 ++-- .../darwin/in_app_purchase_storekit.podspec | 7 +- .../ios/Runner.xcodeproj/project.pbxproj | 20 +- .../RunnerTests/RunnerTests-Bridging-Header.h | 6 + .../example/ios/RunnerTests/SwiftStubs.swift | 28 + .../example/ios/RunnerTests/Tests.m | 835 +++++++++ .../RunnerTests/InAppPurchasePluginTests.m | 1651 ++++++++--------- .../example/shared/RunnerTests/Stubs.h | 4 +- .../example/shared/RunnerTests/Stubs.m | 22 - .../lib/src/messages.g.dart | 165 +- .../pigeons/messages.dart | 2 +- .../test/test_api.g.dart | 336 ++-- 19 files changed, 2569 insertions(+), 1944 deletions(-) delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h deleted file mode 100644 index ca090b6b9928..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 -#import "FIAPRequestHandler.h" -#import "InAppPurchasePlugin.h" - -@interface InAppPurchasePlugin () - -// Holding strong references to FIAPRequestHandlers. Remove the handlers from the set after -// the request is finished. -@property(strong, nonatomic, readonly) NSMutableSet *requestHandlers; - -// Callback channel to dart used for when a function from the transaction observer is triggered. -@property(strong, nonatomic) FlutterMethodChannel *transactionObserverCallbackChannel; - -// Callback channel to dart used for when a function from the transaction observer is triggered. -@property(copy, nonatomic) FIAPRequestHandler * (^handlerFactory)(SKRequest *); - -// Convenience initializer with dependancy injection -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory; - -// Transaction observer methods -- (void)handleTransactionsUpdated:(NSArray *)transactions; -- (void)handleTransactionsRemoved:(NSArray *)transactions; -- (void)handleTransactionRestoreFailed:(NSError *)error; -- (void)restoreCompletedTransactionsFinished; -- (BOOL)shouldAddStorePayment:(SKPayment *)payment product:(SKProduct *)product; - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h deleted file mode 100644 index a2b9cb0eb934..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -#if TARGET_OS_OSX -#import -#else -#import -#endif -#import "messages.g.h" - -@class FIAPaymentQueueHandler; -@class FIAPReceiptManager; - -@interface InAppPurchasePlugin : NSObject - -@property(strong, nonatomic) FIAPaymentQueueHandler *paymentQueueHandler; - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - NS_DESIGNATED_INITIALIZER; -- (instancetype)init NS_UNAVAILABLE; - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m deleted file mode 100644 index 2e2abaaed35b..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ /dev/null @@ -1,404 +0,0 @@ -// 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 "InAppPurchasePlugin.h" -#import -#import "FIAObjectTranslator.h" -#import "FIAPPaymentQueueDelegate.h" -#import "FIAPReceiptManager.h" -#import "FIAPRequestHandler.h" -#import "FIAPaymentQueueHandler.h" -#import "InAppPurchasePlugin+TestOnly.h" - -@interface InAppPurchasePlugin () - -// After querying the product, the available products will be saved in the map to be used -// for purchase. -@property(strong, nonatomic, readonly) NSMutableDictionary *productsCache; - -// Callback channel to dart used for when a function from the payment queue delegate is triggered. -@property(strong, nonatomic, readonly) FlutterMethodChannel *paymentQueueDelegateCallbackChannel; -@property(strong, nonatomic, readonly) NSObject *registrar; - -@property(strong, nonatomic, readonly) FIAPReceiptManager *receiptManager; -@property(strong, nonatomic, readonly) - FIAPPaymentQueueDelegate *paymentQueueDelegate API_AVAILABLE(ios(13)) - API_UNAVAILABLE(tvos, macos, watchos); - -@end - -@implementation InAppPurchasePlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" - binaryMessenger:[registrar messenger]]; - - InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; - SetUpInAppPurchaseAPI(registrar.messenger, instance); -} - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { - self = [super init]; - _receiptManager = receiptManager; - _requestHandlers = [NSMutableSet new]; - _productsCache = [NSMutableDictionary new]; - _handlerFactory = ^FIAPRequestHandler *(SKRequest *request) { - return [[FIAPRequestHandler alloc] initWithRequest:request]; - }; - return self; -} - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory { - self = [self initWithReceiptManager:receiptManager]; - _handlerFactory = [handlerFactory copy]; - return self; -} - -- (instancetype)initWithRegistrar:(NSObject *)registrar { - self = [self initWithReceiptManager:[FIAPReceiptManager new]]; - _registrar = registrar; - - __weak typeof(self) weakSelf = self; - _paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueue defaultQueue] - transactionsUpdated:^(NSArray *_Nonnull transactions) { - [weakSelf handleTransactionsUpdated:transactions]; - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - [weakSelf handleTransactionsRemoved:transactions]; - } - restoreTransactionFailed:^(NSError *_Nonnull error) { - [weakSelf handleTransactionRestoreFailed:error]; - } - restoreCompletedTransactionsFinished:^{ - [weakSelf restoreCompletedTransactionsFinished]; - } - shouldAddStorePayment:^BOOL(SKPayment *payment, SKProduct *product) { - return [weakSelf shouldAddStorePayment:payment product:product]; - } - updatedDownloads:^void(NSArray *_Nonnull downloads) { - [weakSelf updatedDownloads:downloads]; - } - transactionCache:[[FIATransactionCache alloc] init]]; - - _transactionObserverCallbackChannel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" - binaryMessenger:[registrar messenger]]; - return self; -} - -- (nullable NSNumber *)canMakePaymentsWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return @([SKPaymentQueue canMakePayments]); -} - -- (nullable NSArray *)transactionsWithError: - (FlutterError *_Nullable *_Nonnull)error { - NSArray *transactions = - [self.paymentQueueHandler getUnfinishedTransactions]; - NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; - for (SKPaymentTransaction *transaction in transactions) { - [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; - } - return transactionMaps; -} - -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error - API_AVAILABLE(ios(13.0), macos(10.15)) { - SKStorefront *storefront = self.paymentQueueHandler.storefront; - if (!storefront) { - return nil; - } - return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; -} - -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers - completion:(void (^)(SKProductsResponseMessage *_Nullable, - FlutterError *_Nullable))completion { - SKProductsRequest *request = - [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; - FIAPRequestHandler *handler = self.handlerFactory(request); - [self.requestHandlers addObject:handler]; - __weak typeof(self) weakSelf = self; - - [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, - NSError *_Nullable startProductRequestError) { - FlutterError *error = nil; - if (startProductRequestError != nil) { - error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" - message:startProductRequestError.localizedDescription - details:startProductRequestError.description]; - completion(nil, error); - return; - } - if (!response) { - error = [FlutterError errorWithCode:@"storekit_platform_no_response" - message:@"Failed to get SKProductResponse in startRequest " - @"call. Error occured on iOS platform" - details:productIdentifiers]; - completion(nil, error); - return; - } - for (SKProduct *product in response.products) { - [self.productsCache setObject:product forKey:product.productIdentifier]; - } - - completion([FIAObjectTranslator convertProductsResponseToPigeon:response], error); - [weakSelf.requestHandlers removeObject:handler]; - }]; -} - -- (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; - // When a product is already fetched, we create a payment object with - // the product to process the payment. - SKProduct *product = [self getProduct:productID]; - if (!product) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_object" - message: - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue." - details:paymentMap]; - return; - } - - SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; - payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; - NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; - payment.quantity = (quantity != nil) ? quantity.integerValue : 1; - NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; - payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] - ? NO - : [simulatesAskToBuyInSandbox boolValue]; - - if (@available(iOS 12.2, *)) { - NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap - forKey:@"paymentDiscount"]; - NSString *errorMsg = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; - - if (errorMsg) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_discount_object" - message:[NSString stringWithFormat:@"You have requested a payment and specified a " - @"payment discount with invalid properties. %@", - errorMsg] - details:paymentMap]; - return; - } - - payment.paymentDiscount = paymentDiscount; - } - if (![self.paymentQueueHandler addPayment:payment]) { - *error = [FlutterError - errorWithCode:@"storekit_duplicate_product_object" - message:@"There is a pending transaction for the same product identifier. Please " - @"either wait for it to be finished or finish it manually using " - @"`completePurchase` to avoid edge cases." - - details:paymentMap]; - return; - } -} - -- (void)finishTransactionFinishMap:(nonnull NSDictionary *)finishMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"]; - NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"]; - - NSArray *pendingTransactions = - [self.paymentQueueHandler getUnfinishedTransactions]; - - for (SKPaymentTransaction *transaction in pendingTransactions) { - // If the user cancels the purchase dialog we won't have a transactionIdentifier. - // So if it is null AND a transaction in the pendingTransactions list has - // also a null transactionIdentifier we check for equal product identifiers. - if ([transaction.transactionIdentifier isEqualToString:transactionIdentifier] || - ([transactionIdentifier isEqual:[NSNull null]] && - transaction.transactionIdentifier == nil && - [transaction.payment.productIdentifier isEqualToString:productIdentifier])) { - @try { - [self.paymentQueueHandler finishTransaction:transaction]; - } @catch (NSException *e) { - *error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception" - message:e.name - details:e.description]; - return; - } - } - } -} - -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName - error:(FlutterError *_Nullable __autoreleasing *_Nonnull) - error { - [self.paymentQueueHandler restoreTransactions:applicationUserName]; -} - -- (void)presentCodeRedemptionSheetWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - [self.paymentQueueHandler presentCodeRedemptionSheet]; -#endif -} - -- (nullable NSString *)retrieveReceiptDataWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - FlutterError *flutterError; - NSString *receiptData = [self.receiptManager retrieveReceiptWithError:&flutterError]; - if (flutterError) { - *error = flutterError; - return nil; - } - return receiptData; -} - -- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties - completion:(nonnull void (^)(FlutterError *_Nullable))completion { - SKReceiptRefreshRequest *request; - if (receiptProperties) { - // if recieptProperties is not null, this call is for testing. - NSMutableDictionary *properties = [NSMutableDictionary new]; - properties[SKReceiptPropertyIsExpired] = receiptProperties[@"isExpired"]; - properties[SKReceiptPropertyIsRevoked] = receiptProperties[@"isRevoked"]; - properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties[@"isVolumePurchase"]; - request = [self getRefreshReceiptRequest:properties]; - } else { - request = [self getRefreshReceiptRequest:nil]; - } - - FIAPRequestHandler *handler = self.handlerFactory(request); - [self.requestHandlers addObject:handler]; - __weak typeof(self) weakSelf = self; - [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, - NSError *_Nullable error) { - FlutterError *requestError; - if (error) { - requestError = [FlutterError errorWithCode:@"storekit_refreshreceiptrequest_platform_error" - message:error.localizedDescription - details:error.description]; - completion(requestError); - return; - } - completion(nil); - [weakSelf.requestHandlers removeObject:handler]; - }]; -} - -- (void)startObservingPaymentQueueWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [_paymentQueueHandler startObservingPaymentQueue]; -} - -- (void)stopObservingPaymentQueueWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [_paymentQueueHandler stopObservingPaymentQueue]; -} - -- (void)registerPaymentQueueDelegateWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - if (@available(iOS 13.0, *)) { - _paymentQueueDelegateCallbackChannel = [FlutterMethodChannel - methodChannelWithName:@"plugins.flutter.io/in_app_purchase_payment_queue_delegate" - binaryMessenger:[_registrar messenger]]; - - _paymentQueueDelegate = [[FIAPPaymentQueueDelegate alloc] - initWithMethodChannel:_paymentQueueDelegateCallbackChannel]; - _paymentQueueHandler.delegate = _paymentQueueDelegate; - } -#endif -} - -- (void)removePaymentQueueDelegateWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - if (@available(iOS 13.0, *)) { - _paymentQueueHandler.delegate = nil; - } - _paymentQueueDelegate = nil; - _paymentQueueDelegateCallbackChannel = nil; -} - -- (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - if (@available(iOS 13.4, *)) { - [_paymentQueueHandler showPriceConsentIfNeeded]; - } -#endif -} - -- (id)getNonNullValueFromDictionary:(NSDictionary *)dictionary forKey:(NSString *)key { - id value = dictionary[key]; - return [value isKindOfClass:[NSNull class]] ? nil : value; -} - -#pragma mark - transaction observer: - -- (void)handleTransactionsUpdated:(NSArray *)transactions { - NSMutableArray *maps = [NSMutableArray new]; - for (SKPaymentTransaction *transaction in transactions) { - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; - } - [self.transactionObserverCallbackChannel invokeMethod:@"updatedTransactions" arguments:maps]; -} - -- (void)handleTransactionsRemoved:(NSArray *)transactions { - NSMutableArray *maps = [NSMutableArray new]; - for (SKPaymentTransaction *transaction in transactions) { - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; - } - [self.transactionObserverCallbackChannel invokeMethod:@"removedTransactions" arguments:maps]; -} - -- (void)handleTransactionRestoreFailed:(NSError *)error { - [self.transactionObserverCallbackChannel - invokeMethod:@"restoreCompletedTransactionsFailed" - arguments:[FIAObjectTranslator getMapFromNSError:error]]; -} - -- (void)restoreCompletedTransactionsFinished { - [self.transactionObserverCallbackChannel - invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" - arguments:nil]; -} - -- (void)updatedDownloads:(NSArray *)downloads { - NSLog(@"Received an updatedDownloads callback, but downloads are not supported."); -} - -- (BOOL)shouldAddStorePayment:(SKPayment *)payment product:(SKProduct *)product { - // We always return NO here. And we send the message to dart to process the payment; and we will - // have a interception method that deciding if the payment should be processed (implemented by the - // programmer). - [self.productsCache setObject:product forKey:product.productIdentifier]; - [self.transactionObserverCallbackChannel - invokeMethod:@"shouldAddStorePayment" - arguments:@{ - @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], - @"product" : [FIAObjectTranslator getMapFromSKProduct:product] - }]; - return NO; -} - -#pragma mark - dependency injection (for unit testing) - -- (SKProductsRequest *)getProductRequestWithIdentifiers:(NSSet *)identifiers { - return [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers]; -} - -- (SKProduct *)getProduct:(NSString *)productID { - return [self.productsCache objectForKey:productID]; -} - -- (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties { - return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift new file mode 100644 index 000000000000..f4abee8b5e86 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -0,0 +1,404 @@ +import Foundation +import StoreKit + +#if os(iOS) +import Flutter +#elseif os(macOS) +import FlutterMacOS +#endif + +extension FlutterError: Error {} +@objcMembers +public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { + public func startProductRequestProductIdentifiers(_ productIdentifiers: [String], completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void) { + let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) + let handler = handlerFactory!(request) + requestHandlers.insert(handler) + + handler.startProductRequest { response, startProductRequestError in + var error: FlutterError; + if let startProductRequestError = startProductRequestError { + error = FlutterError(code: "storekit_getproductrequest_platform_error", + message: startProductRequestError.localizedDescription, + details: startProductRequestError.localizedDescription) + completion(nil, error); + return + } + + guard let response = response else { + error = FlutterError(code: "storekit_platform_no_response", + message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", + details: productIdentifiers) + completion(nil, error); + return + } + + response.products.forEach { product in + self.productsCache[product.productIdentifier] = product + } + + if #available(iOS 12.2, *) { + if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response){ + completion(responseMessage, nil); + } + } + self.requestHandlers.remove(handler) + } + } + + + + // Properties + private(set) var productsCache: NSMutableDictionary = [:] + private(set) var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? + private(set) var receiptManager: FIAPReceiptManager? + private(set) var paymentQueueDelegate: Any? = nil; + // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, + private var requestHandlers = Set() + private var handlerFactory: ((SKRequest) -> FIAPRequestHandler)? + public var registrar: FlutterPluginRegistrar? + public var paymentQueueHandler: FIAPaymentQueueHandler? + public var transactionObserverCallbackChannel: FlutterMethodChannel? + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase", + binaryMessenger: registrar.messenger()) + let instance = InAppPurchasePlugin(registrar: registrar) + registrar.addMethodCallDelegate(instance, channel: channel) + registrar.addApplicationDelegate(instance) + SetUpInAppPurchaseAPI(registrar.messenger(), instance); + } + + @objc + public init(receiptManager: FIAPReceiptManager) { + self.receiptManager = receiptManager + self.requestHandlers = Set(); + self.productsCache = NSMutableDictionary() + self.handlerFactory = { request in + return FIAPRequestHandler(request: request) + } + super.init() + } + + @objc + public convenience init(receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler) { + self.init(receiptManager: receiptManager) + self.handlerFactory = handlerFactory; + } + + @objc + public convenience init(registrar: FlutterPluginRegistrar) { + self.init(receiptManager: FIAPReceiptManager()); + self.registrar = registrar; + + weak var weakSelf = self + + self.paymentQueueHandler = FIAPaymentQueueHandler(queue: SKPaymentQueue.default(), transactionsUpdated: { (transactions: [SKPaymentTransaction]) -> () in + weakSelf!.handleTransactionsUpdated(transactions) + }, transactionRemoved: { (transactions: [SKPaymentTransaction]) -> () in + weakSelf!.handleTransactionsRemoved(transactions) + }, restoreTransactionFailed: { error in + weakSelf!.handleTransactionRestoreFailed(error as NSError) + }, restoreCompletedTransactionsFinished: { () -> () in + weakSelf!.restoreCompletedTransactionsFinished() + }, shouldAddStorePayment: { (payment: SKPayment, product: SKProduct) -> Bool in + return weakSelf!.shouldAddStorePayment(payment: payment, product: product) + }, updatedDownloads: {_ in + weakSelf!.updatedDownloads() + }, transactionCache: FIATransactionCache()) + + transactionObserverCallbackChannel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase", binaryMessenger: registrar.messenger()) + } + + +// public func startProductRequestProductIdentifiers(_ productIdentifiers: [String]) async -> (SKProductsResponseMessage?, FlutterError?) { +// let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) +// let handler = handlerFactory!(request) +// requestHandlers.insert(handler) +// +// if #available(iOS 13.0, *) { +// let result: (SKProductsResponseMessage?, FlutterError?) = await withCheckedContinuation { continuation in +// handler.startProductRequest { [weak self] response, startProductRequestError in +// guard let self = self else { +// continuation.resume(returning: (nil, FlutterError(code: "internal_error", message: "Self is nil", details: nil))) +// return +// } +// +// if let startProductRequestError = startProductRequestError { +// let error = FlutterError(code: "storekit_getproductrequest_platform_error", +// message: startProductRequestError.localizedDescription, +// details: startProductRequestError.localizedDescription) +// continuation.resume(returning: (nil, error)) +// return +// } +// +// guard let response = response else { +// let error = FlutterError(code: "storekit_platform_no_response", +// message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", +// details: productIdentifiers) +// continuation.resume(returning: (nil, error)) +// return +// } +// +// response.products.forEach { product in +// self.productsCache[product.productIdentifier] = product +// } +// +// let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response); +// continuation.resume(returning: (responseMessage, nil)); +// +// self.requestHandlers.remove(handler) +// } +// } +// } else { +// // Fallback on earlier versions +// } +// return (nil, nil); +// } + + public func refreshReceiptReceiptProperties(_ receiptProperties: [String : Any]?, completion: @escaping (FlutterError?) -> Void) { + var request: SKReceiptRefreshRequest + if let receiptProperties = receiptProperties { + // If receiptProperties is not nil, this call is for testing. + var properties: [String: Any] = [:] + properties[SKReceiptPropertyIsExpired] = receiptProperties["isExpired"]! + properties[SKReceiptPropertyIsRevoked] = receiptProperties["isRevoked"]! + properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties["isVolumePurchase"]! + request = getRefreshReceiptRequest(properties: properties); + } else { + request = getRefreshReceiptRequest(properties: nil); + } + + let handler = handlerFactory!(request) + requestHandlers.insert(handler) + handler.startProductRequest { [weak self] response, error in + if let error = error { + let requestError = FlutterError(code: "storekit_refreshreceiptrequest_platform_error", + message: error.localizedDescription, + details: error.localizedDescription); + completion(requestError); + return + } + completion(nil); + self?.requestHandlers.remove(handler); + } + } + + public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> NSNumber? { + return SKPaymentQueue.canMakePayments() as NSNumber; + } + + public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> [SKPaymentTransactionMessage]? { + let transactions = self.paymentQueueHandler?.getUnfinishedTransactions() ?? []; + var transactionMaps: [SKPaymentTransactionMessage] = [] + for transaction in transactions { + if #available(iOS 12.2, *) { + if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { + transactionMaps.append(map ) + } + } else { + // throw version error? + } + } + return transactionMaps + } + + public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) -> SKStorefrontMessage? { + if #available(iOS 13.0, *) { + let storefront = self.paymentQueueHandler?.storefront + if (storefront == nil) { + return nil; + } + return FIAObjectTranslator.convertStorefront(toPigeon: storefront); + } + return nil; + } + + + public func addPaymentPaymentMap(_ paymentMap: [String : Any], error: AutoreleasingUnsafeMutablePointer) { + guard let productID = paymentMap["productIdentifier"] as? String else { + error.pointee = FlutterError(code: "storekit_missing_product_identifier", + message: "The `productIdentifier` is missing from the payment map.", + details: paymentMap) + return; + } + + guard let product = self.getProduct(productID: productID) else { + error.pointee = FlutterError(code: "storekit_invalid_payment_object", + message: "You have requested a payment for an invalid product. Either the `productIdentifier` of the payment is not valid or the product has not been fetched before adding the payment to the payment queue.", + details: paymentMap) + return; + } + + let payment = SKMutablePayment(product: product) + payment.applicationUsername = paymentMap["applicationUsername"] as? String + payment.quantity = paymentMap["quantity"] as? Int ?? 1 + payment.simulatesAskToBuyInSandbox = paymentMap["simulatesAskToBuyInSandbox"] as? Bool ?? false + + if #available(iOS 12.2, *) { + if let paymentDiscountMap = paymentMap["paymentDiscount"] as? [String: Any], !paymentDiscountMap.isEmpty { + var invalidError: NSString? + if let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount(fromMap: paymentDiscountMap, withError: &invalidError) { + payment.paymentDiscount = paymentDiscount + } else if let invalidError = invalidError { + error.pointee = FlutterError(code: "storekit_invalid_payment_discount_object", + message: "You have requested a payment and specified a payment discount with invalid properties. \(invalidError)", + details: paymentMap) + return; + } + } + } + + guard self.paymentQueueHandler?.add(payment) == true else { + error.pointee = FlutterError(code: "storekit_duplicate_product_object", + message: "There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using `completePurchase` to avoid edge cases.", + details: paymentMap) + return; + }; + } + + public func finishTransactionFinishMap(_ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer) { + guard let transactionIdentifier = finishMap["transactionIdentifier"] as? String, + let productIdentifier = finishMap["productIdentifier"] as? String else { + return + } + + let pendingTransactions = paymentQueueHandler!.getUnfinishedTransactions() + + for transaction in pendingTransactions { + // Check if the current transaction's identifier matches the provided one, + // or both identifiers are nil and the product identifiers match. + if transaction.transactionIdentifier == transactionIdentifier || + (transactionIdentifier == NSNull().description && + transaction.transactionIdentifier == nil && + transaction.payment.productIdentifier == productIdentifier) { + paymentQueueHandler!.finish(transaction); + } + } + } + + public func restoreTransactionsApplicationUserName(_ applicationUserName: String?, error: AutoreleasingUnsafeMutablePointer) { + paymentQueueHandler?.restoreTransactions(applicationUserName); + } + + public func presentCodeRedemptionSheetWithError(_ error: AutoreleasingUnsafeMutablePointer) { +#if os(iOS) + paymentQueueHandler!.presentCodeRedemptionSheet() +#endif + } + + public func retrieveReceiptDataWithError(_ error: AutoreleasingUnsafeMutablePointer) -> String? { + var flutterError : FlutterError? = nil; + let receiptData : String? = receiptManager!.retrieveReceiptWithError(&flutterError); + if (receiptData == nil) { + error.pointee = flutterError; + return nil; + } + return receiptData; + } + + public func startObservingPaymentQueueWithError(_ error: AutoreleasingUnsafeMutablePointer) { + paymentQueueHandler!.startObservingPaymentQueue(); + } + + public func stopObservingPaymentQueueWithError(_ error: AutoreleasingUnsafeMutablePointer) { + paymentQueueHandler!.stopObservingPaymentQueue(); + } + + public func registerPaymentQueueDelegateWithError(_ error: AutoreleasingUnsafeMutablePointer) { +#if os(iOS) + if #available(iOS 13.0, *) { + let messenger = registrar?.messenger(); + paymentQueueDelegateCallbackChannel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase_payment_queue_delegate", + binaryMessenger: messenger!) + + paymentQueueDelegate = FIAPPaymentQueueDelegate(methodChannel: paymentQueueDelegateCallbackChannel!) + paymentQueueHandler!.delegate = (paymentQueueDelegate as! any SKPaymentQueueDelegate); + } +#endif + } + + public func removePaymentQueueDelegateWithError(_ error: AutoreleasingUnsafeMutablePointer) { +#if os(iOS) + if #available(iOS 13.0, *) { + paymentQueueDelegateCallbackChannel = nil; + paymentQueueHandler!.delegate = nil; + paymentQueueDelegate = nil; + } +#endif + } + + public func showPriceConsentIfNeededWithError(_ error: AutoreleasingUnsafeMutablePointer) { +#if os(iOS) + if #available(iOS 13.4, *) { + paymentQueueHandler!.showPriceConsentIfNeeded(); + } +#endif + } + + @objc + public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { + var maps: [[AnyHashable: Any]] = [] + for transaction in transactions { + let map = FIAObjectTranslator.getMapFrom(transaction); + maps.append(map); + } + transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) + } + + @objc + public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { + var maps: [[AnyHashable: Any]] = [] + for transaction in transactions { + let map = FIAObjectTranslator.getMapFrom(transaction); + maps.append(map); + } + transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) + } + + @objc + public func handleTransactionRestoreFailed(_ error: NSError) { + transactionObserverCallbackChannel!.invokeMethod("restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)); + } + + @objc + public func restoreCompletedTransactionsFinished() { + transactionObserverCallbackChannel!.invokeMethod("paymentQueueRestoreCompletedTransactionsFinished", arguments:nil); + } + + @objc + public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { + productsCache[product.productIdentifier] = product; + transactionObserverCallbackChannel! + .invokeMethod("shouldAddStorePayment", arguments: [ + "payment": FIAObjectTranslator.getMapFrom(payment), + "product": FIAObjectTranslator.getMapFrom(product) + ]); + return false; + } + + func updatedDownloads() { + NSLog("Received an updatedDownloads callback, but downloads are not supported."); + } + + public func canMakePayments() -> Bool { + return SKPaymentQueue.canMakePayments() as Bool; + } + + func getProduct(productID : String) -> SKProduct? { + return self.productsCache[productID] as? SKProduct; + } + + func getProductRequest(withIdentifiers productIdentifiers: Set) -> SKProductsRequest { + return SKProductsRequest(productIdentifiers: productIdentifiers); + } + + func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { + let value = dictionary[key] + return value is NSNull ? nil : value + } + + func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h new file mode 100644 index 000000000000..375e09e2584e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h @@ -0,0 +1,11 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "FIAObjectTranslator.h" +#import "FIAPaymentQueueHandler.h" +#import "FIAPPaymentQueueDelegate.h" +#import "FIAPReceiptManager.h" +#import "FIAPRequestHandler.h" +#import "FIATransactionCache.h" +#import "messages.g.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h new file mode 100644 index 000000000000..f17a28b6d16d --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h @@ -0,0 +1,15 @@ +// +// in_app_purchase_storekit.h +// Pods +// +// Created by Louise Hsu on 4/11/24. +// + +#ifndef in_app_purchase_storekit_h +#define in_app_purchase_storekit_h + +#if __has_include("in_app_purchase_storekit-umbrella.h") +#import "in_app_purchase_storekit-umbrella.h" +#endif + +#endif /* in_app_purchase_storekit_h */ diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index bc315e781cd6..13180f9ba797 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -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 (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -67,8 +67,7 @@ typedef NS_ENUM(NSUInteger, SKProductDiscountTypeMessage) { typedef NS_ENUM(NSUInteger, SKProductDiscountPaymentModeMessage) { /// Allows user to pay the discounted price at each payment period. SKProductDiscountPaymentModeMessagePayAsYouGo = 0, - /// Allows user to pay the discounted price upfront and receive the product for the rest of time - /// that was paid for. + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. SKProductDiscountPaymentModeMessagePayUpFront = 1, /// User pays nothing during the discounted period. SKProductDiscountPaymentModeMessageFreeTrial = 2, @@ -110,140 +109,140 @@ typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error; -@property(nonatomic, strong) SKPaymentMessage *payment; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error; +@property(nonatomic, strong) SKPaymentMessage * payment; @property(nonatomic, assign) SKPaymentTransactionStateMessage transactionState; -@property(nonatomic, strong, nullable) SKPaymentTransactionMessage *originalTransaction; -@property(nonatomic, strong, nullable) NSNumber *transactionTimeStamp; -@property(nonatomic, copy, nullable) NSString *transactionIdentifier; -@property(nonatomic, strong, nullable) SKErrorMessage *error; +@property(nonatomic, strong, nullable) SKPaymentTransactionMessage * originalTransaction; +@property(nonatomic, strong, nullable) NSNumber * transactionTimeStamp; +@property(nonatomic, copy, nullable) NSString * transactionIdentifier; +@property(nonatomic, strong, nullable) SKErrorMessage * error; @end @interface SKPaymentMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger)quantity - simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString *productIdentifier; -@property(nonatomic, copy, nullable) NSString *applicationUsername; -@property(nonatomic, copy, nullable) NSString *requestData; -@property(nonatomic, assign) NSInteger quantity; -@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage *paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger )quantity + simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy, nullable) NSString * applicationUsername; +@property(nonatomic, copy, nullable) NSString * requestData; +@property(nonatomic, assign) NSInteger quantity; +@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; +@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; @end @interface SKErrorMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCode:(NSInteger)code - domain:(NSString *)domain - userInfo:(nullable NSDictionary *)userInfo; -@property(nonatomic, assign) NSInteger code; -@property(nonatomic, copy) NSString *domain; -@property(nonatomic, copy, nullable) NSDictionary *userInfo; ++ (instancetype)makeWithCode:(NSInteger )code + domain:(NSString *)domain + userInfo:(nullable NSDictionary *)userInfo; +@property(nonatomic, assign) NSInteger code; +@property(nonatomic, copy) NSString * domain; +@property(nonatomic, copy, nullable) NSDictionary * userInfo; @end @interface SKPaymentDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger)timestamp; -@property(nonatomic, copy) NSString *identifier; -@property(nonatomic, copy) NSString *keyIdentifier; -@property(nonatomic, copy) NSString *nonce; -@property(nonatomic, copy) NSString *signature; -@property(nonatomic, assign) NSInteger timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger )timestamp; +@property(nonatomic, copy) NSString * identifier; +@property(nonatomic, copy) NSString * keyIdentifier; +@property(nonatomic, copy) NSString * nonce; +@property(nonatomic, copy) NSString * signature; +@property(nonatomic, assign) NSInteger timestamp; @end @interface SKStorefrontMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier; -@property(nonatomic, copy) NSString *countryCode; -@property(nonatomic, copy) NSString *identifier; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode + identifier:(NSString *)identifier; +@property(nonatomic, copy) NSString * countryCode; +@property(nonatomic, copy) NSString * identifier; @end @interface SKProductsResponseMessage : NSObject + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; -@property(nonatomic, copy, nullable) NSArray *products; -@property(nonatomic, copy, nullable) NSArray *invalidProductIdentifiers; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; +@property(nonatomic, copy, nullable) NSArray * products; +@property(nonatomic, copy, nullable) NSArray * invalidProductIdentifiers; @end @interface SKProductMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale - subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod: - (nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts; -@property(nonatomic, copy) NSString *productIdentifier; -@property(nonatomic, copy) NSString *localizedTitle; -@property(nonatomic, copy) NSString *localizedDescription; -@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; -@property(nonatomic, copy, nullable) NSString *subscriptionGroupIdentifier; -@property(nonatomic, copy) NSString *price; -@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage *subscriptionPeriod; -@property(nonatomic, strong, nullable) SKProductDiscountMessage *introductoryPrice; -@property(nonatomic, copy, nullable) NSArray *discounts; + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale + subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy) NSString * localizedTitle; +@property(nonatomic, copy) NSString * localizedDescription; +@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; +@property(nonatomic, copy, nullable) NSString * subscriptionGroupIdentifier; +@property(nonatomic, copy) NSString * price; +@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage * subscriptionPeriod; +@property(nonatomic, strong, nullable) SKProductDiscountMessage * introductoryPrice; +@property(nonatomic, copy, nullable) NSArray * discounts; @end @interface SKPriceLocaleMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode; -/// The currency symbol for the locale, e.g. $ for US locale. -@property(nonatomic, copy) NSString *currencySymbol; -/// The currency code for the locale, e.g. USD for US locale. -@property(nonatomic, copy) NSString *currencyCode; -/// The country code for the locale, e.g. US for US locale. -@property(nonatomic, copy) NSString *countryCode; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode; +///The currency symbol for the locale, e.g. $ for US locale. +@property(nonatomic, copy) NSString * currencySymbol; +///The currency code for the locale, e.g. USD for US locale. +@property(nonatomic, copy) NSString * currencyCode; +///The country code for the locale, e.g. US for US locale. +@property(nonatomic, copy) NSString * countryCode; @end @interface SKProductDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger)numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type; -@property(nonatomic, copy) NSString *price; -@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; -@property(nonatomic, assign) NSInteger numberOfPeriods; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger )numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type; +@property(nonatomic, copy) NSString * price; +@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; +@property(nonatomic, assign) NSInteger numberOfPeriods; @property(nonatomic, assign) SKProductDiscountPaymentModeMessage paymentMode; -@property(nonatomic, strong) SKProductSubscriptionPeriodMessage *subscriptionPeriod; -@property(nonatomic, copy, nullable) NSString *identifier; +@property(nonatomic, strong) SKProductSubscriptionPeriodMessage * subscriptionPeriod; +@property(nonatomic, copy, nullable) NSString * identifier; @property(nonatomic, assign) SKProductDiscountTypeMessage type; @end @interface SKProductSubscriptionPeriodMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit; -@property(nonatomic, assign) NSInteger numberOfUnits; ++ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit; +@property(nonatomic, assign) NSInteger numberOfUnits; @property(nonatomic, assign) SKSubscriptionPeriodUnitMessage unit; @end @@ -256,23 +255,16 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// @return `nil` only when `error != nil`. - (nullable NSNumber *)canMakePaymentsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)transactionsWithError: - (FlutterError *_Nullable *_Nonnull)error; +- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers - completion:(void (^)(SKProductsResponseMessage *_Nullable, - FlutterError *_Nullable))completion; -- (void)finishTransactionFinishMap:(NSDictionary *)finishMap - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion; +- (void)finishTransactionFinishMap:(NSDictionary *)finishMap error:(FlutterError *_Nullable *_Nonnull)error; +- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; - (void)presentCodeRedemptionSheetWithError:(FlutterError *_Nullable *_Nonnull)error; - (nullable NSString *)retrieveReceiptDataWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties - completion:(void (^)(FlutterError *_Nullable))completion; +- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties completion:(void (^)(FlutterError *_Nullable))completion; - (void)startObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)stopObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)registerPaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; @@ -280,7 +272,6 @@ NSObject *InAppPurchaseAPIGetCodec(void); - (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpInAppPurchaseAPI(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 9588c883b820..020b102fef0d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -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 (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" @@ -132,12 +132,12 @@ - (NSArray *)toList; @implementation SKPaymentTransactionMessage + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error { - SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error { + SKPaymentTransactionMessage* pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = payment; pigeonResult.transactionState = transactionState; pigeonResult.originalTransaction = originalTransaction; @@ -150,8 +150,7 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; - pigeonResult.originalTransaction = - [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.error = [SKErrorMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; @@ -174,12 +173,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger)quantity - simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger )quantity + simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -195,8 +194,7 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); pigeonResult.quantity = [GetNullableObjectAtIndex(list, 3) integerValue]; pigeonResult.simulatesAskToBuyInSandbox = [GetNullableObjectAtIndex(list, 4) boolValue]; - pigeonResult.paymentDiscount = - [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -215,10 +213,10 @@ - (NSArray *)toList { @end @implementation SKErrorMessage -+ (instancetype)makeWithCode:(NSInteger)code - domain:(NSString *)domain - userInfo:(nullable NSDictionary *)userInfo { - SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; ++ (instancetype)makeWithCode:(NSInteger )code + domain:(NSString *)domain + userInfo:(nullable NSDictionary *)userInfo { + SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -245,11 +243,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger)timestamp { - SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger )timestamp { + SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -281,8 +279,9 @@ - (NSArray *)toList { @end @implementation SKStorefrontMessage -+ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier { - SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode + identifier:(NSString *)identifier { + SKStorefrontMessage* pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = countryCode; pigeonResult.identifier = identifier; return pigeonResult; @@ -306,8 +305,8 @@ - (NSArray *)toList { @implementation SKProductsResponseMessage + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { - SKProductsResponseMessage *pigeonResult = [[SKProductsResponseMessage alloc] init]; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { + SKProductsResponseMessage* pigeonResult = [[SKProductsResponseMessage alloc] init]; pigeonResult.products = products; pigeonResult.invalidProductIdentifiers = invalidProductIdentifiers; return pigeonResult; @@ -330,17 +329,16 @@ - (NSArray *)toList { @end @implementation SKProductMessage -+ (instancetype) - makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale ++ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts { - SKProductMessage *pigeonResult = [[SKProductMessage alloc] init]; + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts { + SKProductMessage* pigeonResult = [[SKProductMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.localizedTitle = localizedTitle; pigeonResult.localizedDescription = localizedDescription; @@ -357,14 +355,11 @@ + (SKProductMessage *)fromList:(NSArray *)list { pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); pigeonResult.localizedTitle = GetNullableObjectAtIndex(list, 1); pigeonResult.localizedDescription = GetNullableObjectAtIndex(list, 2); - pigeonResult.priceLocale = - [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; + pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; pigeonResult.subscriptionGroupIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.price = GetNullableObjectAtIndex(list, 5); - pigeonResult.subscriptionPeriod = - [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; - pigeonResult.introductoryPrice = - [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; + pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; + pigeonResult.introductoryPrice = [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; pigeonResult.discounts = GetNullableObjectAtIndex(list, 8); return pigeonResult; } @@ -388,9 +383,9 @@ - (NSArray *)toList { @implementation SKPriceLocaleMessage + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode { - SKPriceLocaleMessage *pigeonResult = [[SKPriceLocaleMessage alloc] init]; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode { + SKPriceLocaleMessage* pigeonResult = [[SKPriceLocaleMessage alloc] init]; pigeonResult.currencySymbol = currencySymbol; pigeonResult.currencyCode = currencyCode; pigeonResult.countryCode = countryCode; @@ -417,13 +412,13 @@ - (NSArray *)toList { @implementation SKProductDiscountMessage + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger)numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type { - SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger )numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type { + SKProductDiscountMessage* pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = price; pigeonResult.priceLocale = priceLocale; pigeonResult.numberOfPeriods = numberOfPeriods; @@ -436,12 +431,10 @@ + (instancetype)makeWithPrice:(NSString *)price + (SKProductDiscountMessage *)fromList:(NSArray *)list { SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = GetNullableObjectAtIndex(list, 0); - pigeonResult.priceLocale = - [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; pigeonResult.numberOfPeriods = [GetNullableObjectAtIndex(list, 2) integerValue]; pigeonResult.paymentMode = [GetNullableObjectAtIndex(list, 3) integerValue]; - pigeonResult.subscriptionPeriod = - [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; + pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; pigeonResult.identifier = GetNullableObjectAtIndex(list, 5); pigeonResult.type = [GetNullableObjectAtIndex(list, 6) integerValue]; return pigeonResult; @@ -463,17 +456,15 @@ - (NSArray *)toList { @end @implementation SKProductSubscriptionPeriodMessage -+ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit { - SKProductSubscriptionPeriodMessage *pigeonResult = - [[SKProductSubscriptionPeriodMessage alloc] init]; ++ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit { + SKProductSubscriptionPeriodMessage* pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = numberOfUnits; pigeonResult.unit = unit; return pigeonResult; } + (SKProductSubscriptionPeriodMessage *)fromList:(NSArray *)list { - SKProductSubscriptionPeriodMessage *pigeonResult = - [[SKProductSubscriptionPeriodMessage alloc] init]; + SKProductSubscriptionPeriodMessage *pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = [GetNullableObjectAtIndex(list, 0) integerValue]; pigeonResult.unit = [GetNullableObjectAtIndex(list, 1) integerValue]; return pigeonResult; @@ -494,25 +485,25 @@ @interface InAppPurchaseAPICodecReader : FlutterStandardReader @implementation InAppPurchaseAPICodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [SKErrorMessage fromList:[self readValue]]; - case 129: + case 129: return [SKPaymentDiscountMessage fromList:[self readValue]]; - case 130: + case 130: return [SKPaymentMessage fromList:[self readValue]]; - case 131: + case 131: return [SKPaymentTransactionMessage fromList:[self readValue]]; - case 132: + case 132: return [SKPriceLocaleMessage fromList:[self readValue]]; - case 133: + case 133: return [SKProductDiscountMessage fromList:[self readValue]]; - case 134: + case 134: return [SKProductMessage fromList:[self readValue]]; - case 135: + case 135: return [SKProductSubscriptionPeriodMessage fromList:[self readValue]]; - case 136: + case 136: return [SKProductsResponseMessage fromList:[self readValue]]; - case 137: + case 137: return [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -575,27 +566,22 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - InAppPurchaseAPICodecReaderWriter *readerWriter = - [[InAppPurchaseAPICodecReaderWriter alloc] init]; + InAppPurchaseAPICodecReaderWriter *readerWriter = [[InAppPurchaseAPICodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpInAppPurchaseAPI(id binaryMessenger, - NSObject *api) { +void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *api) { /// Returns if the current device is able to make payments { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(canMakePaymentsWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(canMakePaymentsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSNumber *output = [api canMakePaymentsWithError:&error]; @@ -606,14 +592,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSArray *output = [api transactionsWithError:&error]; @@ -624,14 +609,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; SKStorefrontMessage *output = [api storefrontWithError:&error]; @@ -642,15 +626,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(addPaymentPaymentMap:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", - api); + NSCAssert([api respondsToSelector:@selector(addPaymentPaymentMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); @@ -663,44 +645,35 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers: - completion:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(startProductRequestProductIdentifiers:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); - [api startProductRequestProductIdentifiers:arg_productIdentifiers - completion:^(SKProductsResponseMessage *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api startProductRequestProductIdentifiers:arg_productIdentifiers completion:^(SKProductsResponseMessage *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(finishTransactionFinishMap:error:)", - api); + NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(finishTransactionFinishMap:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api finishTransactionFinishMap:arg_finishMap error:&error]; callback(wrapResult(nil, error)); @@ -710,16 +683,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(restoreTransactionsApplicationUserName:error:)", - api); + NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(restoreTransactionsApplicationUserName:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_applicationUserName = GetNullableObjectAtIndex(args, 0); @@ -732,16 +702,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"presentCodeRedemptionSheet" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(presentCodeRedemptionSheetWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(presentCodeRedemptionSheetWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api presentCodeRedemptionSheetWithError:&error]; @@ -752,16 +719,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(retrieveReceiptDataWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(retrieveReceiptDataWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(retrieveReceiptDataWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(retrieveReceiptDataWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSString *output = [api retrieveReceiptDataWithError:&error]; @@ -772,39 +736,32 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(refreshReceiptReceiptProperties:completion:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(refreshReceiptReceiptProperties:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(refreshReceiptReceiptProperties:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(refreshReceiptReceiptProperties:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_receiptProperties = GetNullableObjectAtIndex(args, 0); - [api refreshReceiptReceiptProperties:arg_receiptProperties - completion:^(FlutterError *_Nullable error) { - callback(wrapResult(nil, error)); - }]; + [api refreshReceiptReceiptProperties:arg_receiptProperties completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"startObservingPaymentQueue" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startObservingPaymentQueueWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(startObservingPaymentQueueWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(startObservingPaymentQueueWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startObservingPaymentQueueWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api startObservingPaymentQueueWithError:&error]; @@ -815,16 +772,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"stopObservingPaymentQueue" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(stopObservingPaymentQueueWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(stopObservingPaymentQueueWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(stopObservingPaymentQueueWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(stopObservingPaymentQueueWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api stopObservingPaymentQueueWithError:&error]; @@ -835,16 +789,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"registerPaymentQueueDelegate" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(registerPaymentQueueDelegateWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(registerPaymentQueueDelegateWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(registerPaymentQueueDelegateWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(registerPaymentQueueDelegateWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api registerPaymentQueueDelegateWithError:&error]; @@ -855,16 +806,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"removePaymentQueueDelegate" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(removePaymentQueueDelegateWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(removePaymentQueueDelegateWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(removePaymentQueueDelegateWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(removePaymentQueueDelegateWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api removePaymentQueueDelegateWithError:&error]; @@ -875,16 +823,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." - @"showPriceConsentIfNeeded" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(showPriceConsentIfNeededWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to " - @"@selector(showPriceConsentIfNeededWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(showPriceConsentIfNeededWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(showPriceConsentIfNeededWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api showPriceConsentIfNeededWithError:&error]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec index 428c612543e9..d036cc30496e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec @@ -16,7 +16,12 @@ Downloaded by pub (not CocoaPods). # TODO(mvanbeusekom): update URL when in_app_purchase_storekit package is published. # Updating it before the package is published will cause a lint error and block the tree. s.documentation_url = 'https://pub.dev/packages/in_app_purchase' - s.source_files = 'Classes/**/*' + s.swift_version = '5.0' + s.source_files = 'Classes/**/*.{h,m,swift}' +# s.xcconfig = { +# 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', +# 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', +# } s.public_header_files = 'Classes/**/*.h' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 06e0b3ac947d..358b887c6f7d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; }; + F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */; }; + F2312E722BC8AE9B00380F55 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = F2312E712BC8AE9B00380F55 /* Tests.m */; }; F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; }; F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */; }; F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; }; @@ -78,6 +80,9 @@ A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppPurchasePluginTests.m; sourceTree = ""; }; A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStubs.swift; sourceTree = ""; }; + F2312E712BC8AE9B00380F55 /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIATransactionCacheTests.m; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; @@ -193,6 +198,9 @@ 688DE35021F2A5A100EA2684 /* TranslatorTests.m */, F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */, F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */, + F2312E712BC8AE9B00380F55 /* Tests.m */, + F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */, + F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; sourceTree = ""; @@ -271,6 +279,7 @@ }; A59001A321E69658004A3E5E = { CreatedOnToolsVersion = 10.0; + LastSwiftMigration = 1530; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; @@ -331,7 +340,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 67CBAA37FA50343E43E988F6 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -427,11 +436,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */, F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */, F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */, 6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */, 688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */, F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */, + F2312E722BC8AE9B00380F55 /* Tests.m in Sources */, A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */, 6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */, ); @@ -633,6 +644,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -650,6 +662,9 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Debug; @@ -660,6 +675,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -676,6 +692,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Release; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h new file mode 100644 index 000000000000..5de86f383434 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -0,0 +1,6 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "Stubs.h" +#import "Stubs.m" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift new file mode 100644 index 000000000000..09df45bf1cc1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift @@ -0,0 +1,28 @@ +// +// SwiftStubs.swift +// RunnerTests +// +// Created by Louise Hsu on 4/12/24. +// Copyright © 2024 The Flutter Authors. All rights reserved. +// + +import Foundation + +@testable import in_app_purchase_storekit +import StoreKitTest + +class InAppPurchasePluginStub : InAppPurchasePlugin { + override func getProductRequest(withIdentifiers productIdentifiers: Set) -> SKProductsRequest { + return SKProductRequestStub.init(productIdentifiers: productIdentifiers); + } + + override func getProduct(productID: String) -> SKProduct? { + if (productID == "") { + return nil; + } + return SKProductStub.init(productID: productID); + } + override func getRefreshReceiptRequest(properties: [String : Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m new file mode 100644 index 000000000000..bfea5d33c67b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m @@ -0,0 +1,835 @@ +// 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 +#import +#import "FIAPaymentQueueHandler.h" +#import "Stubs.h" +#import "RunnerTests-Swift.h" + +@import in_app_purchase_storekit; + +@interface InAppPurchasePluginTest : XCTestCase + +@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; +@property(strong, nonatomic) InAppPurchasePlugin *plugin; + +@end + +@implementation InAppPurchasePluginTest + +- (void)setUp { + self.receiptManagerStub = [FIAPReceiptManagerStub new]; + self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; +} + +- (void)tearDown { +} + +- (void)testCanMakePayments { + FlutterError *error; + NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; + XCTAssertTrue([result boolValue]); + XCTAssertNil(error); +} + +- (void)testPaymentQueueStorefront { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *storefrontMap = @{ + @"countryCode" : @"USA", + @"identifier" : @"unique_identifier", + }; + + OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; + + XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); + XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } +} + +- (void)testPaymentQueueStorefrontReturnsNil { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + + OCMStub(mockQueue.storefront).andReturn(nil); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; + + XCTAssertNil(resultMap); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } +} + +- (void)testGetProductResponse { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + XCTAssert( + [response isKindOfClass:[SKProductsResponseMessage class]]); + XCTAssertEqual(response.products.count, 1); + XCTAssertEqual(response.invalidProductIdentifiers.count, 0); + XCTAssertEqual(response.products[0].productIdentifier, @"123"); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testFinishTransactionSucceeds { + NSDictionary *args = @{ + @"transactionIdentifier" : @"567", + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = @[ paymentTransaction ]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testFinishTransactionSucceedsWithNilTransaction { + NSDictionary *args = @{ + @"transactionIdentifier" : [NSNull null], + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *transactionMap = @{ + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : paymentMap, + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testGetProductResponseWithRequestError { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, + nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects( + startProductRequestError.code, + @"storekit_getproductrequest_platform_error"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testGetProductResponseWithNoResponse { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + [NSNull null], nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects(startProductRequestError.code, + @"storekit_platform_no_response"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); + XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); + XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); + XCTAssertEqualObjects(argument, error.details); +} + +- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { + NSDictionary *argument = @{ + // stubbed function will return nil for an empty productIdentifier + @"productIdentifier" : @"", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue.", + error.message); + XCTAssertEqualObjects(argument, error.details); +} + +- (void)testAddPaymentSuccessWithoutPaymentDiscount { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertNil(error); + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + XCTAssert(payment != nil); + XCTAssertEqual(payment.productIdentifier, @"123"); + XCTAssert(payment.quantity == 1); + return YES; + }]]); +} + +- (void)testAddPaymentSuccessWithPaymentDiscount { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"identifier" : @"test_identifier", + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + XCTAssertNil(error); + OCMVerify( + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); +} + +- (void)testAddPaymentFailureWithInvalidPaymentDiscount { + // Support for payment discount is only available on iOS 12.2 and higher. + if (@available(iOS 12.2, *)) { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + id translator = OCMClassMock(FIAObjectTranslator.class); + + NSString *errorMsg = @"Some error occurred"; + OCMStub(ClassMethod([translator + getSKPaymentDiscountFromMap:[OCMArg any] + withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) + .andReturn(nil); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); + XCTAssertEqualObjects(@"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); + XCTAssertEqualObjects(argument, error.details); + OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); + } +} + +- (void)testAddPaymentWithNullSandboxArgument { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : [NSNull null], + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + return !payment.simulatesAskToBuyInSandbox; + }]]); +} + +- (void)testRestoreTransactions { + XCTestExpectation *expectation = + [self expectationWithDescription:@"result successfully restore transactions"]; + + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; + queue.testState = SKPaymentTransactionStatePurchased; + + __block BOOL callbackInvoked = NO; + self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + } + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:^() { + callbackInvoked = YES; + [expectation fulfill]; + } + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + + FlutterError *error; + [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; + + [self waitForExpectations:@[ expectation ] timeout:5]; + XCTAssertTrue(callbackInvoked); +} + +- (void)testRetrieveReceiptDataSuccess { + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + XCTAssertNotNil(result); + XCTAssert([result isKindOfClass:[NSString class]]); +} + +- (void)testRetrieveReceiptDataNil { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + XCTAssertNil(result); +} + +- (void)testRetrieveReceiptDataError { + self.receiptManagerStub.returnError = YES; + + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + + XCTAssertNil(result); + XCTAssertNotNil(error); + XCTAssert([error.code isKindOfClass:[NSString class]]); + NSDictionary *details = error.details; + XCTAssertNotNil(details[@"error"]); + NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; + XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); +} + +- (void)testRefreshReceiptRequest { + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin refreshReceiptReceiptProperties:nil + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testRefreshReceiptRequestWithParams { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testRefreshReceiptRequestWithError { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects( + error.code, @"storekit_refreshreceiptrequest_platform_error"); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +/// presentCodeRedemptionSheetWithError:error is only available on iOS +#if TARGET_OS_IOS +- (void)testPresentCodeRedemptionSheet { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin presentCodeRedemptionSheetWithError:&error]; + + OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); +} +#endif + +- (void)testGetPendingTransactions { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : [NSNull null], + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + @"originalTransaction" : [NSNull null], + }; + OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] + initWithMap:transactionMap] ]); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + FlutterError *error; + SKPaymentTransactionStub *original = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + SKPaymentTransactionMessage *originalPigeon = + [FIAObjectTranslator convertTransactionToPigeon:original]; + SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; + + XCTAssertEqualObjects([self paymentTransactionToList:result], + [self paymentTransactionToList:originalPigeon]); +} + +- (void)testStartObservingPaymentQueue { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin startObservingPaymentQueueWithError:&error]; + + OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); +} + +- (void)testStopObservingPaymentQueue { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin stopObservingPaymentQueueWithError:&error]; + + OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); +} + +#if TARGET_OS_IOS +- (void)testRegisterPaymentQueueDelegate { + if (@available(iOS 13, *)) { + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + self.plugin.registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + + id registrarMock = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + self.plugin.registrar = registrarMock; + + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub([registrarMock messenger]).andReturn(binaryMessengerMock); + + + // Verify the delegate is nil before we register one. + XCTAssertNil(self.plugin.paymentQueueHandler.delegate); + + FlutterError *error; + [self.plugin registerPaymentQueueDelegateWithError:&error]; + + // Verify the delegate is not nil after we registered one. + XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); + } +} +#endif + +- (void)testRemovePaymentQueueDelegate { + if (@available(iOS 13, *)) { + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); + + // Verify the delegate is not nil before removing it. + XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); + + FlutterError *error; + [self.plugin removePaymentQueueDelegateWithError:&error]; + + // Verify the delegate is nill after removing it. + XCTAssertNil(self.plugin.paymentQueueHandler.delegate); + } +} + +- (void)testHandleTransactionsUpdated { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsUpdated:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); +} + +- (void)testHandleTransactionsRemoved { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsRemoved:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); +} + +- (void)testHandleTransactionRestoreFailed { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSError *error = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; + [plugin handleTransactionRestoreFailed:error]; + OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" + arguments:[FIAObjectTranslator getMapFromNSError:error]]); +} + +- (void)testRestoreCompletedTransactionsFinished { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + [plugin restoreCompletedTransactionsFinished]; + OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" + arguments:nil]); +} + +- (void)testShouldAddStorePayment { + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *productMap = @{ + @"price" : @"1", + @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], + @"productIdentifier" : @"123", + @"localizedTitle" : @"title", + @"localizedDescription" : @"des", + }; + + SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; + SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSDictionary *args = @{ + @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], + @"product" : [FIAObjectTranslator getMapFromSKProduct:product] + }; + + BOOL result = [plugin shouldAddStorePaymentWithPayment:payment product:product]; + XCTAssertEqual(result, NO); + OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); +} + +#if TARGET_OS_IOS +- (void)testShowPriceConsentIfNeeded { + FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); + self.plugin.paymentQueueHandler = mockQueueHandler; + + FlutterError *error; + [self.plugin showPriceConsentIfNeededWithError:&error]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + if (@available(iOS 13.4, *)) { + OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); + } else { + OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); + } +#pragma clang diagnostic pop +} +#endif + +// The following methods are deserializer copied from Pigeon's output. + +- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { + return @[ + (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), + @(paymentTransaction.transactionState), + (paymentTransaction.originalTransaction + ? [self paymentTransactionToList:paymentTransaction.originalTransaction] + : [NSNull null]), + paymentTransaction.transactionTimeStamp ?: [NSNull null], + paymentTransaction.transactionIdentifier ?: [NSNull null], + (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), + ]; +} + +- (NSArray *)paymentToList:(SKPaymentMessage *)payment { + return @[ + payment.productIdentifier ?: [NSNull null], + payment.applicationUsername ?: [NSNull null], + payment.requestData ?: [NSNull null], + @(payment.quantity), + @(payment.simulatesAskToBuyInSandbox), + (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] + : [NSNull null]), + ]; +} + +- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { + return @[ + discount.identifier ?: [NSNull null], + discount.keyIdentifier ?: [NSNull null], + discount.nonce ?: [NSNull null], + discount.signature ?: [NSNull null], + @(discount.timestamp), + ]; +} + +- (NSArray *)errorToList:(SKErrorMessage *)error { + return @[ + @(error.code), + error.domain ?: [NSNull null], + error.userInfo ?: [NSNull null], + ]; +} +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 905903df0569..b0bc0df6de0e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -1,826 +1,825 @@ -// 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 -#import -#import "FIAPaymentQueueHandler.h" -#import "InAppPurchasePlugin+TestOnly.h" -#import "Stubs.h" - -@import in_app_purchase_storekit; - -@interface InAppPurchasePluginTest : XCTestCase - -@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; -@property(strong, nonatomic) InAppPurchasePlugin *plugin; - -@end - -@implementation InAppPurchasePluginTest - -- (void)setUp { - self.receiptManagerStub = [FIAPReceiptManagerStub new]; - self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; -} - -- (void)tearDown { -} - -- (void)testCanMakePayments { - FlutterError *error; - NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; - XCTAssertTrue([result boolValue]); - XCTAssertNil(error); -} - -- (void)testPaymentQueueStorefront { - if (@available(iOS 13, macOS 10.15, *)) { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary *storefrontMap = @{ - @"countryCode" : @"USA", - @"identifier" : @"unique_identifier", - }; - - OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - FlutterError *error; - SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; - - XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); - XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); - XCTAssertNil(error); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } -} - -- (void)testPaymentQueueStorefrontReturnsNil { - if (@available(iOS 13, macOS 10.15, *)) { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - - OCMStub(mockQueue.storefront).andReturn(nil); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - FlutterError *error; - SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; - - XCTAssertNil(resultMap); - XCTAssertNil(error); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } -} - -- (void)testGetProductResponse { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - XCTAssert( - [response isKindOfClass:[SKProductsResponseMessage class]]); - XCTAssertEqual(response.products.count, 1); - XCTAssertEqual(response.invalidProductIdentifiers.count, 0); - XCTAssertEqual(response.products[0].productIdentifier, @"123"); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testFinishTransactionSucceeds { - NSDictionary *args = @{ - @"transactionIdentifier" : @"567", - @"productIdentifier" : @"unique_identifier", - }; - - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = @[ paymentTransaction ]; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); - - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin finishTransactionFinishMap:args error:&error]; - - XCTAssertNil(error); -} - -- (void)testFinishTransactionSucceedsWithNilTransaction { - NSDictionary *args = @{ - @"transactionIdentifier" : [NSNull null], - @"productIdentifier" : @"unique_identifier", - }; - - NSDictionary *paymentMap = @{ - @"productIdentifier" : @"123", - @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", - @"quantity" : @(2), - @"applicationUsername" : @"app user name", - @"simulatesAskToBuyInSandbox" : @(NO) - }; - - NSDictionary *transactionMap = @{ - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : paymentMap, - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); - - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin finishTransactionFinishMap:args error:&error]; - - XCTAssertNil(error); -} - -- (void)testGetProductResponseWithRequestError { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *error = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, - nil])]); - - [plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNotNil(startProductRequestError); - XCTAssertEqualObjects( - startProductRequestError.code, - @"storekit_getproductrequest_platform_error"); - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testGetProductResponseWithNoResponse { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *error = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], - [NSNull null], nil])]); - - [plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNotNil(startProductRequestError); - XCTAssertEqualObjects(startProductRequestError.code, - @"storekit_platform_no_response"); - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); - XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); - XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " - @"Please either wait for it to be finished or finish it manually " - @"using `completePurchase` to avoid edge cases.", - error.message); - XCTAssertEqualObjects(argument, error.details); -} - -- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { - NSDictionary *argument = @{ - // stubbed function will return nil for an empty productIdentifier - @"productIdentifier" : @"", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); - XCTAssertEqualObjects( - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue.", - error.message); - XCTAssertEqualObjects(argument, error.details); -} - -- (void)testAddPaymentSuccessWithoutPaymentDiscount { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertNil(error); - OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - XCTAssert(payment != nil); - XCTAssertEqual(payment.productIdentifier, @"123"); - XCTAssert(payment.quantity == 1); - return YES; - }]]); -} - -- (void)testAddPaymentSuccessWithPaymentDiscount { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"identifier" : @"test_identifier", - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - XCTAssertNil(error); - OCMVerify( - times(1), - [mockHandler - addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *discount = payment.paymentDiscount; - - return [discount.identifier isEqual:@"test_identifier"] && - [discount.keyIdentifier isEqual:@"test_key_identifier"] && - [discount.nonce - isEqual:[[NSUUID alloc] - initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && - [discount.signature isEqual:@"test_signature"] && - [discount.timestamp isEqual:@(1635847102)]; - } - - return YES; - }]]); -} - -- (void)testAddPaymentFailureWithInvalidPaymentDiscount { - // Support for payment discount is only available on iOS 12.2 and higher. - if (@available(iOS 12.2, *)) { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - id translator = OCMClassMock(FIAObjectTranslator.class); - - NSString *errorMsg = @"Some error occurred"; - OCMStub(ClassMethod([translator - getSKPaymentDiscountFromMap:[OCMArg any] - withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) - .andReturn(nil); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); - XCTAssertEqualObjects(@"You have requested a payment and specified a " - @"payment discount with invalid properties. Some error occurred", - error.message); - XCTAssertEqualObjects(argument, error.details); - OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); - } -} - -- (void)testAddPaymentWithNullSandboxArgument { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : [NSNull null], - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - return !payment.simulatesAskToBuyInSandbox; - }]]); -} - -- (void)testRestoreTransactions { - XCTestExpectation *expectation = - [self expectationWithDescription:@"result successfully restore transactions"]; - - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - - __block BOOL callbackInvoked = NO; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:^() { - callbackInvoked = YES; - [expectation fulfill]; - } - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; - - FlutterError *error; - [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; - - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertTrue(callbackInvoked); -} - -- (void)testRetrieveReceiptDataSuccess { - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - XCTAssertNotNil(result); - XCTAssert([result isKindOfClass:[NSString class]]); -} - -- (void)testRetrieveReceiptDataNil { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - XCTAssertNil(result); -} - -- (void)testRetrieveReceiptDataError { - self.receiptManagerStub.returnError = YES; - - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - - XCTAssertNil(result); - XCTAssertNotNil(error); - XCTAssert([error.code isKindOfClass:[NSString class]]); - NSDictionary *details = error.details; - XCTAssertNotNil(details[@"error"]); - NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; - XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); -} - -- (void)testRefreshReceiptRequest { - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:nil - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testRefreshReceiptRequestWithParams { - NSDictionary *properties = @{ - @"isExpired" : @NO, - @"isRevoked" : @NO, - @"isVolumePurchase" : @NO, - }; - - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testRefreshReceiptRequestWithError { - NSDictionary *properties = @{ - @"isExpired" : @NO, - @"isRevoked" : @NO, - @"isVolumePurchase" : @NO, - }; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *recieptError = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], - recieptError, nil])]); - - [plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error.code, @"storekit_refreshreceiptrequest_platform_error"); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -/// presentCodeRedemptionSheetWithError:error is only available on iOS -#if TARGET_OS_IOS -- (void)testPresentCodeRedemptionSheet { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin presentCodeRedemptionSheetWithError:&error]; - - OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); -} -#endif - -- (void)testGetPendingTransactions { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : [NSNull null], - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - @"originalTransaction" : [NSNull null], - }; - OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] - initWithMap:transactionMap] ]); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - FlutterError *error; - SKPaymentTransactionStub *original = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - - SKPaymentTransactionMessage *originalPigeon = - [FIAObjectTranslator convertTransactionToPigeon:original]; - SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; - - XCTAssertEqualObjects([self paymentTransactionToList:result], - [self paymentTransactionToList:originalPigeon]); -} - -- (void)testStartObservingPaymentQueue { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin startObservingPaymentQueueWithError:&error]; - - OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); -} - -- (void)testStopObservingPaymentQueue { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin stopObservingPaymentQueueWithError:&error]; - - OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); -} - -#if TARGET_OS_IOS -- (void)testRegisterPaymentQueueDelegate { - if (@available(iOS 13, *)) { - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - // Verify the delegate is nil before we register one. - XCTAssertNil(self.plugin.paymentQueueHandler.delegate); - - FlutterError *error; - [self.plugin registerPaymentQueueDelegateWithError:&error]; - - // Verify the delegate is not nil after we registered one. - XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); - } -} -#endif - -- (void)testRemovePaymentQueueDelegate { - if (@available(iOS 13, *)) { - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); - - // Verify the delegate is not nil before removing it. - XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); - - FlutterError *error; - [self.plugin removePaymentQueueDelegateWithError:&error]; - - // Verify the delegate is nill after removing it. - XCTAssertNil(self.plugin.paymentQueueHandler.delegate); - } -} - -- (void)testHandleTransactionsUpdated { - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; - NSMutableArray *maps = [NSMutableArray new]; - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; - - [plugin handleTransactionsUpdated:array]; - OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); -} - -- (void)testHandleTransactionsRemoved { - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; - NSMutableArray *maps = [NSMutableArray new]; - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; - - [plugin handleTransactionsRemoved:array]; - OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); -} - -- (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - NSError *error; - [plugin handleTransactionRestoreFailed:error]; - OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" - arguments:[FIAObjectTranslator getMapFromNSError:error]]); -} - -- (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - [plugin restoreCompletedTransactionsFinished]; - OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" - arguments:nil]); -} - -- (void)testShouldAddStorePayment { - NSDictionary *paymentMap = @{ - @"productIdentifier" : @"123", - @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", - @"quantity" : @(2), - @"applicationUsername" : @"app user name", - @"simulatesAskToBuyInSandbox" : @(NO) - }; - - NSDictionary *productMap = @{ - @"price" : @"1", - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], - @"productIdentifier" : @"123", - @"localizedTitle" : @"title", - @"localizedDescription" : @"des", - }; - - SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; - SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - NSDictionary *args = @{ - @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], - @"product" : [FIAObjectTranslator getMapFromSKProduct:product] - }; - - BOOL result = [plugin shouldAddStorePayment:payment product:product]; - XCTAssertEqual(result, NO); - OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); -} - -#if TARGET_OS_IOS -- (void)testShowPriceConsentIfNeeded { - FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); - self.plugin.paymentQueueHandler = mockQueueHandler; - - FlutterError *error; - [self.plugin showPriceConsentIfNeededWithError:&error]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - if (@available(iOS 13.4, *)) { - OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); - } else { - OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); - } -#pragma clang diagnostic pop -} -#endif - -// The following methods are deserializer copied from Pigeon's output. - -- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { - return @[ - (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), - @(paymentTransaction.transactionState), - (paymentTransaction.originalTransaction - ? [self paymentTransactionToList:paymentTransaction.originalTransaction] - : [NSNull null]), - paymentTransaction.transactionTimeStamp ?: [NSNull null], - paymentTransaction.transactionIdentifier ?: [NSNull null], - (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), - ]; -} - -- (NSArray *)paymentToList:(SKPaymentMessage *)payment { - return @[ - payment.productIdentifier ?: [NSNull null], - payment.applicationUsername ?: [NSNull null], - payment.requestData ?: [NSNull null], - @(payment.quantity), - @(payment.simulatesAskToBuyInSandbox), - (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] - : [NSNull null]), - ]; -} - -- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { - return @[ - discount.identifier ?: [NSNull null], - discount.keyIdentifier ?: [NSNull null], - discount.nonce ?: [NSNull null], - discount.signature ?: [NSNull null], - @(discount.timestamp), - ]; -} - -- (NSArray *)errorToList:(SKErrorMessage *)error { - return @[ - @(error.code), - error.domain ?: [NSNull null], - error.userInfo ?: [NSNull null], - ]; -} -@end +//// 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 +//#import +//#import "FIAPaymentQueueHandler.h" +//#import "Stubs.h" +// +//@import in_app_purchase_storekit; +// +//@interface InAppPurchasePluginTest : XCTestCase +// +//@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; +//@property(strong, nonatomic) InAppPurchasePlugin *plugin; +// +//@end +// +//@implementation InAppPurchasePluginTest +// +//- (void)setUp { +// self.receiptManagerStub = [FIAPReceiptManager new]; +// self.plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:self.receiptManagerStub]; +//} +// +//- (void)tearDown { +//} +// +//- (void)testCanMakePayments { +// FlutterError *error; +// NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; +// XCTAssertTrue([result boolValue]); +// XCTAssertNil(error); +//} +// +//- (void)testPaymentQueueStorefront { +// if (@available(iOS 13, macOS 10.15, *)) { +// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); +// NSDictionary *storefrontMap = @{ +// @"countryCode" : @"USA", +// @"identifier" : @"unique_identifier", +// }; +// +// OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); +// +// self.plugin.paymentQueueHandler = +// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue +// transactionsUpdated:nil +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:nil +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// +// FlutterError *error; +// SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; +// +// XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); +// XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); +// XCTAssertNil(error); +// } else { +// NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); +// } +//} +// +//- (void)testPaymentQueueStorefrontReturnsNil { +// if (@available(iOS 13, macOS 10.15, *)) { +// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); +// +// OCMStub(mockQueue.storefront).andReturn(nil); +// +// self.plugin.paymentQueueHandler = +// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue +// transactionsUpdated:nil +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:nil +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// +// FlutterError *error; +// SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; +// +// XCTAssertNil(resultMap); +// XCTAssertNil(error); +// } else { +// NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); +// } +//} +// +//- (void)testGetProductResponse { +// NSArray *argument = @[ @"123" ]; +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// [self.plugin +// startProductRequestProductIdentifiers:argument +// completion:^(SKProductsResponseMessage *_Nullable response, +// FlutterError *_Nullable startProductRequestError) { +// XCTAssert( +// [response isKindOfClass:[SKProductsResponseMessage class]]); +// XCTAssertEqual(response.products.count, 1); +// XCTAssertEqual(response.invalidProductIdentifiers.count, 0); +// XCTAssertEqual(response.products[0].productIdentifier, @"123"); +// [expectation fulfill]; +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +//- (void)testFinishTransactionSucceeds { +// NSDictionary *args = @{ +// @"transactionIdentifier" : @"567", +// @"productIdentifier" : @"unique_identifier", +// }; +// +// NSDictionary *transactionMap = @{ +// @"transactionIdentifier" : @"567", +// @"transactionState" : @(SKPaymentTransactionStatePurchasing), +// @"payment" : [NSNull null], +// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" +// code:123 +// userInfo:@{}]], +// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), +// }; +// +// SKPaymentTransactionStub *paymentTransaction = +// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; +// NSArray *array = @[ paymentTransaction ]; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); +// +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// [self.plugin finishTransactionFinishMap:args error:&error]; +// +// XCTAssertNil(error); +//} +// +//- (void)testFinishTransactionSucceedsWithNilTransaction { +// NSDictionary *args = @{ +// @"transactionIdentifier" : [NSNull null], +// @"productIdentifier" : @"unique_identifier", +// }; +// +// NSDictionary *paymentMap = @{ +// @"productIdentifier" : @"123", +// @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", +// @"quantity" : @(2), +// @"applicationUsername" : @"app user name", +// @"simulatesAskToBuyInSandbox" : @(NO) +// }; +// +// NSDictionary *transactionMap = @{ +// @"transactionState" : @(SKPaymentTransactionStatePurchasing), +// @"payment" : paymentMap, +// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" +// code:123 +// userInfo:@{}]], +// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), +// }; +// +// SKPaymentTransactionStub *paymentTransaction = +// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); +// +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// [self.plugin finishTransactionFinishMap:args error:&error]; +// +// XCTAssertNil(error); +//} +// +//- (void)testGetProductResponseWithRequestError { +// NSArray *argument = @[ @"123" ]; +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// +// id mockHandler = OCMClassMock([FIAPRequestHandler class]); +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] +// initWithReceiptManager:nil +// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { +// return mockHandler; +// }]; +// +// NSError *error = [NSError errorWithDomain:@"errorDomain" +// code:0 +// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; +// +// OCMStub([mockHandler +// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, +// nil])]); +// +// [plugin +// startProductRequestProductIdentifiers:argument +// completion:^(SKProductsResponseMessage *_Nullable response, +// FlutterError *_Nullable startProductRequestError) { +// [expectation fulfill]; +// XCTAssertNotNil(error); +// XCTAssertNotNil(startProductRequestError); +// XCTAssertEqualObjects( +// startProductRequestError.code, +// @"storekit_getproductrequest_platform_error"); +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +//- (void)testGetProductResponseWithNoResponse { +// NSArray *argument = @[ @"123" ]; +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// +// id mockHandler = OCMClassMock([FIAPRequestHandler class]); +// +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] +// initWithReceiptManager:nil +// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { +// return mockHandler; +// }]; +// +// NSError *error = [NSError errorWithDomain:@"errorDomain" +// code:0 +// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; +// +// OCMStub([mockHandler +// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], +// [NSNull null], nil])]); +// +// [plugin +// startProductRequestProductIdentifiers:argument +// completion:^(SKProductsResponseMessage *_Nullable response, +// FlutterError *_Nullable startProductRequestError) { +// [expectation fulfill]; +// XCTAssertNotNil(error); +// XCTAssertNotNil(startProductRequestError); +// XCTAssertEqualObjects(startProductRequestError.code, +// @"storekit_platform_no_response"); +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +//- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { +// NSDictionary *argument = @{ +// @"productIdentifier" : @"123", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : @YES, +// }; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// +// OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); +// XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); +// XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " +// @"Please either wait for it to be finished or finish it manually " +// @"using `completePurchase` to avoid edge cases.", +// error.message); +// XCTAssertEqualObjects(argument, error.details); +//} +// +//- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { +// NSDictionary *argument = @{ +// // stubbed function will return nil for an empty productIdentifier +// @"productIdentifier" : @"", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : @YES, +// }; +// +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// +// XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); +// XCTAssertEqualObjects( +// @"You have requested a payment for an invalid product. Either the " +// @"`productIdentifier` of the payment is not valid or the product has not been " +// @"fetched before adding the payment to the payment queue.", +// error.message); +// XCTAssertEqualObjects(argument, error.details); +//} +// +//- (void)testAddPaymentSuccessWithoutPaymentDiscount { +// NSDictionary *argument = @{ +// @"productIdentifier" : @"123", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : @YES, +// }; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); +// self.plugin.paymentQueueHandler = mockHandler; +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// +// XCTAssertNil(error); +// OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { +// SKPayment *payment = obj; +// XCTAssert(payment != nil); +// XCTAssertEqual(payment.productIdentifier, @"123"); +// XCTAssert(payment.quantity == 1); +// return YES; +// }]]); +//} +// +//- (void)testAddPaymentSuccessWithPaymentDiscount { +// NSDictionary *argument = @{ +// @"productIdentifier" : @"123", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : @YES, +// @"paymentDiscount" : @{ +// @"identifier" : @"test_identifier", +// @"keyIdentifier" : @"test_key_identifier", +// @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", +// @"signature" : @"test_signature", +// @"timestamp" : @(1635847102), +// } +// }; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// XCTAssertNil(error); +// OCMVerify( +// times(1), +// [mockHandler +// addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { +// SKPayment *payment = obj; +// if (@available(iOS 12.2, *)) { +// SKPaymentDiscount *discount = payment.paymentDiscount; +// +// return [discount.identifier isEqual:@"test_identifier"] && +// [discount.keyIdentifier isEqual:@"test_key_identifier"] && +// [discount.nonce +// isEqual:[[NSUUID alloc] +// initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && +// [discount.signature isEqual:@"test_signature"] && +// [discount.timestamp isEqual:@(1635847102)]; +// } +// +// return YES; +// }]]); +//} +// +//- (void)testAddPaymentFailureWithInvalidPaymentDiscount { +// // Support for payment discount is only available on iOS 12.2 and higher. +// if (@available(iOS 12.2, *)) { +// NSDictionary *argument = @{ +// @"productIdentifier" : @"123", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : @YES, +// @"paymentDiscount" : @{ +// @"keyIdentifier" : @"test_key_identifier", +// @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", +// @"signature" : @"test_signature", +// @"timestamp" : @(1635847102), +// } +// }; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// id translator = OCMClassMock(FIAObjectTranslator.class); +// +// NSString *errorMsg = @"Some error occurred"; +// OCMStub(ClassMethod([translator +// getSKPaymentDiscountFromMap:[OCMArg any] +// withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) +// .andReturn(nil); +// self.plugin.paymentQueueHandler = mockHandler; +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// +// XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); +// XCTAssertEqualObjects(@"You have requested a payment and specified a " +// @"payment discount with invalid properties. Some error occurred", +// error.message); +// XCTAssertEqualObjects(argument, error.details); +// OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); +// } +//} +// +//- (void)testAddPaymentWithNullSandboxArgument { +// NSDictionary *argument = @{ +// @"productIdentifier" : @"123", +// @"quantity" : @(1), +// @"simulatesAskToBuyInSandbox" : [NSNull null], +// }; +// +// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); +// self.plugin.paymentQueueHandler = mockHandler; +// FlutterError *error; +// +// [self.plugin addPaymentPaymentMap:argument error:&error]; +// OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { +// SKPayment *payment = obj; +// return !payment.simulatesAskToBuyInSandbox; +// }]]); +//} +// +//- (void)testRestoreTransactions { +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"result successfully restore transactions"]; +// +// SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; +// queue.testState = SKPaymentTransactionStatePurchased; +// +// __block BOOL callbackInvoked = NO; +// self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue +// transactionsUpdated:^(NSArray *_Nonnull transactions) { +// } +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:^() { +// callbackInvoked = YES; +// [expectation fulfill]; +// } +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// [queue addTransactionObserver:self.plugin.paymentQueueHandler]; +// +// FlutterError *error; +// [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; +// +// [self waitForExpectations:@[ expectation ] timeout:5]; +// XCTAssertTrue(callbackInvoked); +//} +// +//- (void)testRetrieveReceiptDataSuccess { +// FlutterError *error; +// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; +// XCTAssertNotNil(result); +// XCTAssert([result isKindOfClass:[NSString class]]); +//} +// +//- (void)testRetrieveReceiptDataNil { +// NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); +// OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); +// FlutterError *error; +// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; +// XCTAssertNil(result); +//} +// +//- (void)testRetrieveReceiptDataError { +// self.receiptManagerStub.returnError = YES; +// +// FlutterError *error; +// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; +// +// XCTAssertNil(result); +// XCTAssertNotNil(error); +// XCTAssert([error.code isKindOfClass:[NSString class]]); +// NSDictionary *details = error.details; +// XCTAssertNotNil(details[@"error"]); +// NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; +// XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); +//} +// +//- (void)testRefreshReceiptRequest { +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// [self.plugin refreshReceiptReceiptProperties:nil +// completion:^(FlutterError *_Nullable error) { +// [expectation fulfill]; +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +//- (void)testRefreshReceiptRequestWithParams { +// NSDictionary *properties = @{ +// @"isExpired" : @NO, +// @"isRevoked" : @NO, +// @"isVolumePurchase" : @NO, +// }; +// +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// [self.plugin refreshReceiptReceiptProperties:properties +// completion:^(FlutterError *_Nullable error) { +// [expectation fulfill]; +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +//- (void)testRefreshReceiptRequestWithError { +// NSDictionary *properties = @{ +// @"isExpired" : @NO, +// @"isRevoked" : @NO, +// @"isVolumePurchase" : @NO, +// }; +// XCTestExpectation *expectation = +// [self expectationWithDescription:@"completion handler successfully called"]; +// +// id mockHandler = OCMClassMock([FIAPRequestHandler class]); +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] +// initWithReceiptManager:nil +// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { +// return mockHandler; +// }]; +// +// NSError *recieptError = [NSError errorWithDomain:@"errorDomain" +// code:0 +// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; +// +// OCMStub([mockHandler +// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], +// recieptError, nil])]); +// +// [plugin refreshReceiptReceiptProperties:properties +// completion:^(FlutterError *_Nullable error) { +// XCTAssertNotNil(error); +// XCTAssertEqualObjects( +// error.code, @"storekit_refreshreceiptrequest_platform_error"); +// [expectation fulfill]; +// }]; +// [self waitForExpectations:@[ expectation ] timeout:5]; +//} +// +///// presentCodeRedemptionSheetWithError:error is only available on iOS +//#if TARGET_OS_IOS +//- (void)testPresentCodeRedemptionSheet { +// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// [self.plugin presentCodeRedemptionSheetWithError:&error]; +// +// OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); +//} +//#endif +// +//- (void)testGetPendingTransactions { +// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); +// NSDictionary *transactionMap = @{ +// @"transactionIdentifier" : [NSNull null], +// @"transactionState" : @(SKPaymentTransactionStatePurchasing), +// @"payment" : [NSNull null], +// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" +// code:123 +// userInfo:@{}]], +// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), +// @"originalTransaction" : [NSNull null], +// }; +// OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] +// initWithMap:transactionMap] ]); +// +// self.plugin.paymentQueueHandler = +// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue +// transactionsUpdated:nil +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:nil +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// FlutterError *error; +// SKPaymentTransactionStub *original = +// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; +// +// SKPaymentTransactionMessage *originalPigeon = +// [FIAObjectTranslator convertTransactionToPigeon:original]; +// SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; +// +// XCTAssertEqualObjects([self paymentTransactionToList:result], +// [self paymentTransactionToList:originalPigeon]); +//} +// +//- (void)testStartObservingPaymentQueue { +// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// [self.plugin startObservingPaymentQueueWithError:&error]; +// +// OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); +//} +// +//- (void)testStopObservingPaymentQueue { +// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); +// self.plugin.paymentQueueHandler = mockHandler; +// +// FlutterError *error; +// [self.plugin stopObservingPaymentQueueWithError:&error]; +// +// OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); +//} +// +//#if TARGET_OS_IOS +//- (void)testRegisterPaymentQueueDelegate { +// if (@available(iOS 13, *)) { +// self.plugin.paymentQueueHandler = +// [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] +// transactionsUpdated:nil +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:nil +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// +// // Verify the delegate is nil before we register one. +// XCTAssertNil(self.plugin.paymentQueueHandler.delegate); +// +// FlutterError *error; +// [self.plugin registerPaymentQueueDelegateWithError:&error]; +// +// // Verify the delegate is not nil after we registered one. +// XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); +// } +//} +//#endif +// +//- (void)testRemovePaymentQueueDelegate { +// if (@available(iOS 13, *)) { +// self.plugin.paymentQueueHandler = +// [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] +// transactionsUpdated:nil +// transactionRemoved:nil +// restoreTransactionFailed:nil +// restoreCompletedTransactionsFinished:nil +// shouldAddStorePayment:nil +// updatedDownloads:nil +// transactionCache:OCMClassMock(FIATransactionCache.class)]; +// self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); +// +// // Verify the delegate is not nil before removing it. +// XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); +// +// FlutterError *error; +// [self.plugin removePaymentQueueDelegateWithError:&error]; +// +// // Verify the delegate is nill after removing it. +// XCTAssertNil(self.plugin.paymentQueueHandler.delegate); +// } +//} +// +//- (void)testHandleTransactionsUpdated { +// NSDictionary *transactionMap = @{ +// @"transactionIdentifier" : @"567", +// @"transactionState" : @(SKPaymentTransactionStatePurchasing), +// @"payment" : [NSNull null], +// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" +// code:123 +// userInfo:@{}]], +// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), +// }; +// +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; +// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); +// plugin.transactionObserverCallbackChannel = mockChannel; +// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +// +// SKPaymentTransactionStub *paymentTransaction = +// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; +// NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; +// NSMutableArray *maps = [NSMutableArray new]; +// [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; +// +// [plugin handleTransactionsUpdated:array]; +// OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); +//} +// +//- (void)testHandleTransactionsRemoved { +// NSDictionary *transactionMap = @{ +// @"transactionIdentifier" : @"567", +// @"transactionState" : @(SKPaymentTransactionStatePurchasing), +// @"payment" : [NSNull null], +// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" +// code:123 +// userInfo:@{}]], +// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), +// }; +// +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; +// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); +// plugin.transactionObserverCallbackChannel = mockChannel; +// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +// +// SKPaymentTransactionStub *paymentTransaction = +// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; +// NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; +// NSMutableArray *maps = [NSMutableArray new]; +// [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; +// +// [plugin handleTransactionsRemoved:array]; +// OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); +//} +// +//- (void)testHandleTransactionRestoreFailed { +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; +// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); +// plugin.transactionObserverCallbackChannel = mockChannel; +// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +// +// NSError *error; +// [plugin handleTransactionRestoreFailed:error]; +// OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" +// arguments:[FIAObjectTranslator getMapFromNSError:error]]); +//} +// +//- (void)testRestoreCompletedTransactionsFinished { +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; +// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); +// plugin.transactionObserverCallbackChannel = mockChannel; +// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +// +// [plugin restoreCompletedTransactionsFinished]; +// OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" +// arguments:nil]); +//} +// +//- (void)testShouldAddStorePayment { +// NSDictionary *paymentMap = @{ +// @"productIdentifier" : @"123", +// @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", +// @"quantity" : @(2), +// @"applicationUsername" : @"app user name", +// @"simulatesAskToBuyInSandbox" : @(NO) +// }; +// +// NSDictionary *productMap = @{ +// @"price" : @"1", +// @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], +// @"productIdentifier" : @"123", +// @"localizedTitle" : @"title", +// @"localizedDescription" : @"des", +// }; +// +// SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; +// SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; +// +// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; +// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); +// plugin.transactionObserverCallbackChannel = mockChannel; +// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +// +// NSDictionary *args = @{ +// @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], +// @"product" : [FIAObjectTranslator getMapFromSKProduct:product] +// }; +// +// BOOL result = [plugin shouldAddStorePayment:payment product:product]; +// XCTAssertEqual(result, NO); +// OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); +//} +// +//#if TARGET_OS_IOS +//- (void)testShowPriceConsentIfNeeded { +// FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); +// self.plugin.paymentQueueHandler = mockQueueHandler; +// +// FlutterError *error; +// [self.plugin showPriceConsentIfNeededWithError:&error]; +// +//#pragma clang diagnostic push +//#pragma clang diagnostic ignored "-Wpartial-availability" +// if (@available(iOS 13.4, *)) { +// OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); +// } else { +// OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); +// } +//#pragma clang diagnostic pop +//} +//#endif +// +//// The following methods are deserializer copied from Pigeon's output. +// +//- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { +// return @[ +// (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), +// @(paymentTransaction.transactionState), +// (paymentTransaction.originalTransaction +// ? [self paymentTransactionToList:paymentTransaction.originalTransaction] +// : [NSNull null]), +// paymentTransaction.transactionTimeStamp ?: [NSNull null], +// paymentTransaction.transactionIdentifier ?: [NSNull null], +// (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), +// ]; +//} +// +//- (NSArray *)paymentToList:(SKPaymentMessage *)payment { +// return @[ +// payment.productIdentifier ?: [NSNull null], +// payment.applicationUsername ?: [NSNull null], +// payment.requestData ?: [NSNull null], +// @(payment.quantity), +// @(payment.simulatesAskToBuyInSandbox), +// (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] +// : [NSNull null]), +// ]; +//} +// +//- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { +// return @[ +// discount.identifier ?: [NSNull null], +// discount.keyIdentifier ?: [NSNull null], +// discount.nonce ?: [NSNull null], +// discount.signature ?: [NSNull null], +// @(discount.timestamp), +// ]; +//} +// +//- (NSArray *)errorToList:(SKErrorMessage *)error { +// return @[ +// @(error.code), +// error.domain ?: [NSNull null], +// error.userInfo ?: [NSNull null], +// ]; +//} +//@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h index 2ef8e23181a7..daad506e79cd 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h @@ -20,6 +20,7 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) @interface SKProductStub : SKProduct - (instancetype)initWithMap:(NSDictionary *)map; +- (instancetype)initWithProductID:(NSString *)productIdentifier; @end @interface SKProductRequestStub : SKProductsRequest @@ -32,9 +33,6 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) - (instancetype)initWithMap:(NSDictionary *)map; @end -@interface InAppPurchasePluginStub : InAppPurchasePlugin -@end - @interface SKRequestStub : SKRequest @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m index b4dba710f026..8c8b2973ebc6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m @@ -144,28 +144,6 @@ - (instancetype)initWithMap:(NSDictionary *)map { @end -@interface InAppPurchasePluginStub () -@end - -@implementation InAppPurchasePluginStub - -- (SKProductRequestStub *)getProductRequestWithIdentifiers:(NSSet *)identifiers { - return [[SKProductRequestStub alloc] initWithProductIdentifiers:identifiers]; -} - -- (SKProduct *)getProduct:(NSString *)productID { - if ([productID isEqualToString:@""]) { - return nil; - } - return [[SKProductStub alloc] initWithProductID:productID]; -} - -- (SKReceiptRefreshRequestStub *)getRefreshReceiptRequest:(NSDictionary *)properties { - return [[SKReceiptRefreshRequestStub alloc] initWithReceiptProperties:properties]; -} - -@end - @interface SKPaymentQueueStub () @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index 7ce35bded2b2..ec163b4fd9a2 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.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 (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), 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 @@ -18,8 +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 []; } @@ -36,32 +35,27 @@ enum SKPaymentTransactionStateMessage { /// transaction to update to another state. Never complete a transaction that /// is still in a purchasing state. purchasing, - /// The user's payment has been succesfully processed. /// /// You should provide the user the content that they purchased. purchased, - /// The transaction failed. /// /// Check the [PaymentTransactionWrapper.error] property from /// [PaymentTransactionWrapper] for details. failed, - /// This transaction is restoring content previously purchased by the user. /// /// The previous transaction information can be obtained in /// [PaymentTransactionWrapper.originalTransaction] from /// [PaymentTransactionWrapper]. restored, - /// The transaction is in the queue but pending external action. Wait for /// another callback to get the final state. /// /// You should update your UI to indicate that you are waiting for the /// transaction to update to another state. deferred, - /// Indicates the transaction is in an unspecified state. unspecified, } @@ -69,7 +63,6 @@ enum SKPaymentTransactionStateMessage { enum SKProductDiscountTypeMessage { /// A constant indicating the discount type is an introductory offer. introductory, - /// A constant indicating the discount type is a promotional offer. subscription, } @@ -77,13 +70,10 @@ enum SKProductDiscountTypeMessage { enum SKProductDiscountPaymentModeMessage { /// Allows user to pay the discounted price at each payment period. payAsYouGo, - /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. payUpFront, - /// User pays nothing during the discounted period. freeTrial, - /// Unspecified mode. unspecified, } @@ -132,8 +122,7 @@ class SKPaymentTransactionMessage { result as List; return SKPaymentTransactionMessage( payment: SKPaymentMessage.decode(result[0]! as List), - transactionState: - SKPaymentTransactionStateMessage.values[result[1]! as int], + transactionState: SKPaymentTransactionStateMessage.values[result[1]! as int], originalTransaction: result[2] != null ? SKPaymentTransactionMessage.decode(result[2]! as List) : null, @@ -373,14 +362,12 @@ class SKProductMessage { subscriptionGroupIdentifier: result[4] as String?, price: result[5]! as String, subscriptionPeriod: result[6] != null - ? SKProductSubscriptionPeriodMessage.decode( - result[6]! as List) + ? SKProductSubscriptionPeriodMessage.decode(result[6]! as List) : null, introductoryPrice: result[7] != null ? SKProductDiscountMessage.decode(result[7]! as List) : null, - discounts: - (result[8] as List?)?.cast(), + discounts: (result[8] as List?)?.cast(), ); } } @@ -462,10 +449,8 @@ class SKProductDiscountMessage { price: result[0]! as String, priceLocale: SKPriceLocaleMessage.decode(result[1]! as List), numberOfPeriods: result[2]! as int, - paymentMode: - SKProductDiscountPaymentModeMessage.values[result[3]! as int], - subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode( - result[4]! as List), + paymentMode: SKProductDiscountPaymentModeMessage.values[result[3]! as int], + subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode(result[4]! as List), identifier: result[5] as String?, type: SKProductDiscountTypeMessage.values[result[6]! as int], ); @@ -540,25 +525,25 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -574,15 +559,12 @@ class InAppPurchaseAPI { : __pigeon_binaryMessenger = binaryMessenger; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _InAppPurchaseAPICodec(); + static const MessageCodec pigeonChannelCodec = _InAppPurchaseAPICodec(); /// Returns if the current device is able to make payments Future canMakePayments() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -608,10 +590,8 @@ class InAppPurchaseAPI { } Future> transactions() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -632,16 +612,13 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)! - .cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } Future storefront() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -667,10 +644,8 @@ class InAppPurchaseAPI { } Future addPayment(Map paymentMap) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -690,18 +665,15 @@ class InAppPurchaseAPI { } } - Future startProductRequest( - List productIdentifiers) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future startProductRequest(List productIdentifiers) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([productIdentifiers]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([productIdentifiers]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -720,11 +692,9 @@ class InAppPurchaseAPI { } } - Future finishTransaction(Map finishMap) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future finishTransaction(Map finishMap) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -745,16 +715,14 @@ class InAppPurchaseAPI { } Future restoreTransactions(String? applicationUserName) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([applicationUserName]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([applicationUserName]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -769,10 +737,8 @@ class InAppPurchaseAPI { } Future presentCodeRedemptionSheet() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -793,10 +759,8 @@ class InAppPurchaseAPI { } Future retrieveReceiptData() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -816,18 +780,15 @@ class InAppPurchaseAPI { } } - Future refreshReceipt( - {Map? receiptProperties}) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future refreshReceipt({Map? receiptProperties}) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([receiptProperties]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([receiptProperties]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -842,10 +803,8 @@ class InAppPurchaseAPI { } Future startObservingPaymentQueue() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -866,10 +825,8 @@ class InAppPurchaseAPI { } Future stopObservingPaymentQueue() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -890,10 +847,8 @@ class InAppPurchaseAPI { } Future registerPaymentQueueDelegate() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -914,10 +869,8 @@ class InAppPurchaseAPI { } Future removePaymentQueueDelegate() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -938,10 +891,8 @@ class InAppPurchaseAPI { } Future showPriceConsentIfNeeded() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index fe1042c8037e..6351c889cc38 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -238,7 +238,7 @@ abstract class InAppPurchaseAPI { SKProductsResponseMessage startProductRequest( List productIdentifiers); - void finishTransaction(Map finishMap); + void finishTransaction(Map finishMap); void restoreTransactions(String? applicationUserName); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index 65cd6e0bcc2d..df149267004a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/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 (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), 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 @@ -55,25 +55,25 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -82,10 +82,8 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } abstract class TestInAppPurchaseApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestInAppPurchaseApiCodec(); + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments bool canMakePayments(); @@ -96,10 +94,9 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - Future startProductRequest( - List productIdentifiers); + Future startProductRequest(List productIdentifiers); - void finishTransaction(Map finishMap); + void finishTransaction(Map finishMap); void restoreTransactions(String? applicationUserName); @@ -119,102 +116,76 @@ abstract class TestInAppPurchaseApi { void showPriceConsentIfNeeded(); - static void setup(TestInAppPurchaseApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { final bool output = api.canMakePayments(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { - final List output = - api.transactions(); + final List output = api.transactions(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { final SKStorefrontMessage output = api.storefront(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); final List args = (message as List?)!; - final Map? arg_paymentMap = - (args[0] as Map?)?.cast(); + final Map? arg_paymentMap = (args[0] as Map?)?.cast(); assert(arg_paymentMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null, expected non-null Map.'); try { @@ -222,93 +193,72 @@ abstract class TestInAppPurchaseApi { 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); final List args = (message as List?)!; - final List? arg_productIdentifiers = - (args[0] as List?)?.cast(); + final List? arg_productIdentifiers = (args[0] as List?)?.cast(); assert(arg_productIdentifiers != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null, expected non-null List.'); try { - final SKProductsResponseMessage output = - await api.startProductRequest(arg_productIdentifiers!); + final SKProductsResponseMessage output = await api.startProductRequest(arg_productIdentifiers!); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); final List args = (message as List?)!; - final Map? arg_finishMap = - (args[0] as Map?)?.cast(); + final Map? arg_finishMap = (args[0] as Map?)?.cast(); assert(arg_finishMap != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); try { api.finishTransaction(arg_finishMap!); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); final List args = (message as List?)!; final String? arg_applicationUserName = (args[0] as String?); try { @@ -316,214 +266,164 @@ abstract class TestInAppPurchaseApi { 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.presentCodeRedemptionSheet(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { final String? output = api.retrieveReceiptData(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt was null.'); final List args = (message as List?)!; - final Map? arg_receiptProperties = - (args[0] as Map?)?.cast(); + final Map? arg_receiptProperties = (args[0] as Map?)?.cast(); try { await api.refreshReceipt(receiptProperties: arg_receiptProperties); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.startObservingPaymentQueue(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.stopObservingPaymentQueue(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.registerPaymentQueueDelegate(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.removePaymentQueueDelegate(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { api.showPriceConsentIfNeeded(); 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())); } }); } From 839394b6674a0b083e2fe4e79fe4936b42b0a58f Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Apr 2024 11:47:58 -0700 Subject: [PATCH 02/29] formatting, clean up, comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 192 +- ...in_app_purchase_storekit-Bridging-Header.h | 5 +- .../darwin/Classes/messages.g.h | 189 +- .../darwin/Classes/messages.g.m | 353 ++-- .../ios/Runner.xcodeproj/project.pbxproj | 4 - .../RunnerTests/RunnerTests-Bridging-Header.h | 3 +- .../example/ios/RunnerTests/Tests.m | 835 --------- .../RunnerTests/InAppPurchasePluginTests.m | 1659 +++++++++-------- .../lib/src/messages.g.dart | 161 +- .../test/test_api.g.dart | 330 ++-- 10 files changed, 1529 insertions(+), 2202 deletions(-) delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index f4abee8b5e86..73c7265d01c7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -10,44 +10,6 @@ import FlutterMacOS extension FlutterError: Error {} @objcMembers public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { - public func startProductRequestProductIdentifiers(_ productIdentifiers: [String], completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void) { - let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) - let handler = handlerFactory!(request) - requestHandlers.insert(handler) - - handler.startProductRequest { response, startProductRequestError in - var error: FlutterError; - if let startProductRequestError = startProductRequestError { - error = FlutterError(code: "storekit_getproductrequest_platform_error", - message: startProductRequestError.localizedDescription, - details: startProductRequestError.localizedDescription) - completion(nil, error); - return - } - - guard let response = response else { - error = FlutterError(code: "storekit_platform_no_response", - message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", - details: productIdentifiers) - completion(nil, error); - return - } - - response.products.forEach { product in - self.productsCache[product.productIdentifier] = product - } - - if #available(iOS 12.2, *) { - if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response){ - completion(responseMessage, nil); - } - } - self.requestHandlers.remove(handler) - } - } - - - // Properties private(set) var productsCache: NSMutableDictionary = [:] private(set) var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? @@ -69,7 +31,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { SetUpInAppPurchaseAPI(registrar.messenger(), instance); } - @objc public init(receiptManager: FIAPReceiptManager) { self.receiptManager = receiptManager self.requestHandlers = Set(); @@ -80,13 +41,11 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { super.init() } - @objc public convenience init(receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler) { self.init(receiptManager: receiptManager) self.handlerFactory = handlerFactory; } - @objc public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()); self.registrar = registrar; @@ -111,79 +70,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } -// public func startProductRequestProductIdentifiers(_ productIdentifiers: [String]) async -> (SKProductsResponseMessage?, FlutterError?) { -// let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) -// let handler = handlerFactory!(request) -// requestHandlers.insert(handler) -// -// if #available(iOS 13.0, *) { -// let result: (SKProductsResponseMessage?, FlutterError?) = await withCheckedContinuation { continuation in -// handler.startProductRequest { [weak self] response, startProductRequestError in -// guard let self = self else { -// continuation.resume(returning: (nil, FlutterError(code: "internal_error", message: "Self is nil", details: nil))) -// return -// } -// -// if let startProductRequestError = startProductRequestError { -// let error = FlutterError(code: "storekit_getproductrequest_platform_error", -// message: startProductRequestError.localizedDescription, -// details: startProductRequestError.localizedDescription) -// continuation.resume(returning: (nil, error)) -// return -// } -// -// guard let response = response else { -// let error = FlutterError(code: "storekit_platform_no_response", -// message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", -// details: productIdentifiers) -// continuation.resume(returning: (nil, error)) -// return -// } -// -// response.products.forEach { product in -// self.productsCache[product.productIdentifier] = product -// } -// -// let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response); -// continuation.resume(returning: (responseMessage, nil)); -// -// self.requestHandlers.remove(handler) -// } -// } -// } else { -// // Fallback on earlier versions -// } -// return (nil, nil); -// } - - public func refreshReceiptReceiptProperties(_ receiptProperties: [String : Any]?, completion: @escaping (FlutterError?) -> Void) { - var request: SKReceiptRefreshRequest - if let receiptProperties = receiptProperties { - // If receiptProperties is not nil, this call is for testing. - var properties: [String: Any] = [:] - properties[SKReceiptPropertyIsExpired] = receiptProperties["isExpired"]! - properties[SKReceiptPropertyIsRevoked] = receiptProperties["isRevoked"]! - properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties["isVolumePurchase"]! - request = getRefreshReceiptRequest(properties: properties); - } else { - request = getRefreshReceiptRequest(properties: nil); - } - - let handler = handlerFactory!(request) - requestHandlers.insert(handler) - handler.startProductRequest { [weak self] response, error in - if let error = error { - let requestError = FlutterError(code: "storekit_refreshreceiptrequest_platform_error", - message: error.localizedDescription, - details: error.localizedDescription); - completion(requestError); - return - } - completion(nil); - self?.requestHandlers.remove(handler); - } - } - public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> NSNumber? { return SKPaymentQueue.canMakePayments() as NSNumber; } @@ -192,13 +78,9 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { let transactions = self.paymentQueueHandler?.getUnfinishedTransactions() ?? []; var transactionMaps: [SKPaymentTransactionMessage] = [] for transaction in transactions { - if #available(iOS 12.2, *) { - if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { - transactionMaps.append(map ) + if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { + transactionMaps.append(map); } - } else { - // throw version error? - } } return transactionMaps } @@ -214,6 +96,42 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return nil; } + public func startProductRequestProductIdentifiers(_ productIdentifiers: [String], completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void) { + let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) + let handler = handlerFactory!(request) + requestHandlers.insert(handler) + + handler.startProductRequest { response, startProductRequestError in + var error: FlutterError; + if let startProductRequestError = startProductRequestError { + error = FlutterError(code: "storekit_getproductrequest_platform_error", + message: startProductRequestError.localizedDescription, + details: startProductRequestError.localizedDescription) + completion(nil, error); + return + } + + guard let response = response else { + error = FlutterError(code: "storekit_platform_no_response", + message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", + details: productIdentifiers) + completion(nil, error); + return + } + + response.products.forEach { product in + self.productsCache[product.productIdentifier] = product + } + + if #available(iOS 12.2, *) { + if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response){ + completion(responseMessage, nil); + } + } + self.requestHandlers.remove(handler) + } + } + public func addPaymentPaymentMap(_ paymentMap: [String : Any], error: AutoreleasingUnsafeMutablePointer) { guard let productID = paymentMap["productIdentifier"] as? String else { @@ -297,6 +215,35 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return receiptData; } + public func refreshReceiptReceiptProperties(_ receiptProperties: [String : Any]?, completion: @escaping (FlutterError?) -> Void) { + var request: SKReceiptRefreshRequest + if let receiptProperties = receiptProperties { + // If receiptProperties is not nil, this call is for testing. + var properties: [String: Any] = [:] + properties[SKReceiptPropertyIsExpired] = receiptProperties["isExpired"]! + properties[SKReceiptPropertyIsRevoked] = receiptProperties["isRevoked"]! + properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties["isVolumePurchase"]! + request = getRefreshReceiptRequest(properties: properties); + } else { + request = getRefreshReceiptRequest(properties: nil); + } + + let handler = handlerFactory!(request) + requestHandlers.insert(handler) + handler.startProductRequest { [weak self] response, error in + if let error = error { + let requestError = FlutterError(code: "storekit_refreshreceiptrequest_platform_error", + message: error.localizedDescription, + details: error.localizedDescription); + completion(requestError); + return + } + completion(nil); + self?.requestHandlers.remove(handler); + } + } + + public func startObservingPaymentQueueWithError(_ error: AutoreleasingUnsafeMutablePointer) { paymentQueueHandler!.startObservingPaymentQueue(); } @@ -336,7 +283,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { #endif } - @objc public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { var maps: [[AnyHashable: Any]] = [] for transaction in transactions { @@ -346,7 +292,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) } - @objc public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { var maps: [[AnyHashable: Any]] = [] for transaction in transactions { @@ -356,17 +301,14 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) } - @objc public func handleTransactionRestoreFailed(_ error: NSError) { transactionObserverCallbackChannel!.invokeMethod("restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)); } - @objc public func restoreCompletedTransactionsFinished() { transactionObserverCallbackChannel!.invokeMethod("paymentQueueRestoreCompletedTransactionsFinished", arguments:nil); } - @objc public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { productsCache[product.productIdentifier] = product; transactionObserverCallbackChannel! diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h index 375e09e2584e..3f756006bce6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h @@ -1,11 +1,12 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // #import "FIAObjectTranslator.h" -#import "FIAPaymentQueueHandler.h" #import "FIAPPaymentQueueDelegate.h" #import "FIAPReceiptManager.h" #import "FIAPRequestHandler.h" +#import "FIAPaymentQueueHandler.h" #import "FIATransactionCache.h" #import "messages.g.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 13180f9ba797..28daef7657c6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -67,7 +67,8 @@ typedef NS_ENUM(NSUInteger, SKProductDiscountTypeMessage) { typedef NS_ENUM(NSUInteger, SKProductDiscountPaymentModeMessage) { /// Allows user to pay the discounted price at each payment period. SKProductDiscountPaymentModeMessagePayAsYouGo = 0, - /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + /// Allows user to pay the discounted price upfront and receive the product for the rest of time + /// that was paid for. SKProductDiscountPaymentModeMessagePayUpFront = 1, /// User pays nothing during the discounted period. SKProductDiscountPaymentModeMessageFreeTrial = 2, @@ -109,140 +110,140 @@ typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error; -@property(nonatomic, strong) SKPaymentMessage * payment; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error; +@property(nonatomic, strong) SKPaymentMessage *payment; @property(nonatomic, assign) SKPaymentTransactionStateMessage transactionState; -@property(nonatomic, strong, nullable) SKPaymentTransactionMessage * originalTransaction; -@property(nonatomic, strong, nullable) NSNumber * transactionTimeStamp; -@property(nonatomic, copy, nullable) NSString * transactionIdentifier; -@property(nonatomic, strong, nullable) SKErrorMessage * error; +@property(nonatomic, strong, nullable) SKPaymentTransactionMessage *originalTransaction; +@property(nonatomic, strong, nullable) NSNumber *transactionTimeStamp; +@property(nonatomic, copy, nullable) NSString *transactionIdentifier; +@property(nonatomic, strong, nullable) SKErrorMessage *error; @end @interface SKPaymentMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger )quantity - simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy, nullable) NSString * applicationUsername; -@property(nonatomic, copy, nullable) NSString * requestData; -@property(nonatomic, assign) NSInteger quantity; -@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger)quantity + simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy, nullable) NSString *applicationUsername; +@property(nonatomic, copy, nullable) NSString *requestData; +@property(nonatomic, assign) NSInteger quantity; +@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; +@property(nonatomic, strong, nullable) SKPaymentDiscountMessage *paymentDiscount; @end @interface SKErrorMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCode:(NSInteger )code - domain:(NSString *)domain - userInfo:(nullable NSDictionary *)userInfo; -@property(nonatomic, assign) NSInteger code; -@property(nonatomic, copy) NSString * domain; -@property(nonatomic, copy, nullable) NSDictionary * userInfo; ++ (instancetype)makeWithCode:(NSInteger)code + domain:(NSString *)domain + userInfo:(nullable NSDictionary *)userInfo; +@property(nonatomic, assign) NSInteger code; +@property(nonatomic, copy) NSString *domain; +@property(nonatomic, copy, nullable) NSDictionary *userInfo; @end @interface SKPaymentDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger )timestamp; -@property(nonatomic, copy) NSString * identifier; -@property(nonatomic, copy) NSString * keyIdentifier; -@property(nonatomic, copy) NSString * nonce; -@property(nonatomic, copy) NSString * signature; -@property(nonatomic, assign) NSInteger timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger)timestamp; +@property(nonatomic, copy) NSString *identifier; +@property(nonatomic, copy) NSString *keyIdentifier; +@property(nonatomic, copy) NSString *nonce; +@property(nonatomic, copy) NSString *signature; +@property(nonatomic, assign) NSInteger timestamp; @end @interface SKStorefrontMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCountryCode:(NSString *)countryCode - identifier:(NSString *)identifier; -@property(nonatomic, copy) NSString * countryCode; -@property(nonatomic, copy) NSString * identifier; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier; +@property(nonatomic, copy) NSString *countryCode; +@property(nonatomic, copy) NSString *identifier; @end @interface SKProductsResponseMessage : NSObject + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; -@property(nonatomic, copy, nullable) NSArray * products; -@property(nonatomic, copy, nullable) NSArray * invalidProductIdentifiers; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; +@property(nonatomic, copy, nullable) NSArray *products; +@property(nonatomic, copy, nullable) NSArray *invalidProductIdentifiers; @end @interface SKProductMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale - subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy) NSString * localizedTitle; -@property(nonatomic, copy) NSString * localizedDescription; -@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; -@property(nonatomic, copy, nullable) NSString * subscriptionGroupIdentifier; -@property(nonatomic, copy) NSString * price; -@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage * subscriptionPeriod; -@property(nonatomic, strong, nullable) SKProductDiscountMessage * introductoryPrice; -@property(nonatomic, copy, nullable) NSArray * discounts; + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale + subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier + price:(NSString *)price + subscriptionPeriod: + (nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy) NSString *localizedTitle; +@property(nonatomic, copy) NSString *localizedDescription; +@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; +@property(nonatomic, copy, nullable) NSString *subscriptionGroupIdentifier; +@property(nonatomic, copy) NSString *price; +@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage *subscriptionPeriod; +@property(nonatomic, strong, nullable) SKProductDiscountMessage *introductoryPrice; +@property(nonatomic, copy, nullable) NSArray *discounts; @end @interface SKPriceLocaleMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode; -///The currency symbol for the locale, e.g. $ for US locale. -@property(nonatomic, copy) NSString * currencySymbol; -///The currency code for the locale, e.g. USD for US locale. -@property(nonatomic, copy) NSString * currencyCode; -///The country code for the locale, e.g. US for US locale. -@property(nonatomic, copy) NSString * countryCode; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode; +/// The currency symbol for the locale, e.g. $ for US locale. +@property(nonatomic, copy) NSString *currencySymbol; +/// The currency code for the locale, e.g. USD for US locale. +@property(nonatomic, copy) NSString *currencyCode; +/// The country code for the locale, e.g. US for US locale. +@property(nonatomic, copy) NSString *countryCode; @end @interface SKProductDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger )numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type; -@property(nonatomic, copy) NSString * price; -@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; -@property(nonatomic, assign) NSInteger numberOfPeriods; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger)numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type; +@property(nonatomic, copy) NSString *price; +@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; +@property(nonatomic, assign) NSInteger numberOfPeriods; @property(nonatomic, assign) SKProductDiscountPaymentModeMessage paymentMode; -@property(nonatomic, strong) SKProductSubscriptionPeriodMessage * subscriptionPeriod; -@property(nonatomic, copy, nullable) NSString * identifier; +@property(nonatomic, strong) SKProductSubscriptionPeriodMessage *subscriptionPeriod; +@property(nonatomic, copy, nullable) NSString *identifier; @property(nonatomic, assign) SKProductDiscountTypeMessage type; @end @interface SKProductSubscriptionPeriodMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit; -@property(nonatomic, assign) NSInteger numberOfUnits; ++ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit; +@property(nonatomic, assign) NSInteger numberOfUnits; @property(nonatomic, assign) SKSubscriptionPeriodUnitMessage unit; @end @@ -255,16 +256,23 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// @return `nil` only when `error != nil`. - (nullable NSNumber *)canMakePaymentsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSArray *)transactionsWithError: + (FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion; -- (void)finishTransactionFinishMap:(NSDictionary *)finishMap error:(FlutterError *_Nullable *_Nonnull)error; -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers + completion:(void (^)(SKProductsResponseMessage *_Nullable, + FlutterError *_Nullable))completion; +- (void)finishTransactionFinishMap:(NSDictionary *)finishMap + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName + error:(FlutterError *_Nullable *_Nonnull)error; - (void)presentCodeRedemptionSheetWithError:(FlutterError *_Nullable *_Nonnull)error; - (nullable NSString *)retrieveReceiptDataWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties completion:(void (^)(FlutterError *_Nullable))completion; +- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties + completion:(void (^)(FlutterError *_Nullable))completion; - (void)startObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)stopObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)registerPaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; @@ -272,6 +280,7 @@ NSObject *InAppPurchaseAPIGetCodec(void); - (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *_Nullable api); +extern void SetUpInAppPurchaseAPI(id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 020b102fef0d..8e76474c8bf7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -132,12 +132,12 @@ - (NSArray *)toList; @implementation SKPaymentTransactionMessage + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error { - SKPaymentTransactionMessage* pigeonResult = [[SKPaymentTransactionMessage alloc] init]; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error { + SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = payment; pigeonResult.transactionState = transactionState; pigeonResult.originalTransaction = originalTransaction; @@ -150,7 +150,8 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; - pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.originalTransaction = + [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.error = [SKErrorMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; @@ -173,12 +174,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger )quantity - simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger)quantity + simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -194,7 +195,8 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); pigeonResult.quantity = [GetNullableObjectAtIndex(list, 3) integerValue]; pigeonResult.simulatesAskToBuyInSandbox = [GetNullableObjectAtIndex(list, 4) boolValue]; - pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = + [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -213,10 +215,10 @@ - (NSArray *)toList { @end @implementation SKErrorMessage -+ (instancetype)makeWithCode:(NSInteger )code - domain:(NSString *)domain - userInfo:(nullable NSDictionary *)userInfo { - SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; ++ (instancetype)makeWithCode:(NSInteger)code + domain:(NSString *)domain + userInfo:(nullable NSDictionary *)userInfo { + SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -243,11 +245,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger )timestamp { - SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger)timestamp { + SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -279,9 +281,8 @@ - (NSArray *)toList { @end @implementation SKStorefrontMessage -+ (instancetype)makeWithCountryCode:(NSString *)countryCode - identifier:(NSString *)identifier { - SKStorefrontMessage* pigeonResult = [[SKStorefrontMessage alloc] init]; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier { + SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = countryCode; pigeonResult.identifier = identifier; return pigeonResult; @@ -305,8 +306,8 @@ - (NSArray *)toList { @implementation SKProductsResponseMessage + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { - SKProductsResponseMessage* pigeonResult = [[SKProductsResponseMessage alloc] init]; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { + SKProductsResponseMessage *pigeonResult = [[SKProductsResponseMessage alloc] init]; pigeonResult.products = products; pigeonResult.invalidProductIdentifiers = invalidProductIdentifiers; return pigeonResult; @@ -329,16 +330,17 @@ - (NSArray *)toList { @end @implementation SKProductMessage -+ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale ++ (instancetype) + makeWithProductIdentifier:(NSString *)productIdentifier + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts { - SKProductMessage* pigeonResult = [[SKProductMessage alloc] init]; + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts { + SKProductMessage *pigeonResult = [[SKProductMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.localizedTitle = localizedTitle; pigeonResult.localizedDescription = localizedDescription; @@ -355,11 +357,14 @@ + (SKProductMessage *)fromList:(NSArray *)list { pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); pigeonResult.localizedTitle = GetNullableObjectAtIndex(list, 1); pigeonResult.localizedDescription = GetNullableObjectAtIndex(list, 2); - pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; + pigeonResult.priceLocale = + [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; pigeonResult.subscriptionGroupIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.price = GetNullableObjectAtIndex(list, 5); - pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; - pigeonResult.introductoryPrice = [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; + pigeonResult.subscriptionPeriod = + [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; + pigeonResult.introductoryPrice = + [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; pigeonResult.discounts = GetNullableObjectAtIndex(list, 8); return pigeonResult; } @@ -383,9 +388,9 @@ - (NSArray *)toList { @implementation SKPriceLocaleMessage + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode { - SKPriceLocaleMessage* pigeonResult = [[SKPriceLocaleMessage alloc] init]; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode { + SKPriceLocaleMessage *pigeonResult = [[SKPriceLocaleMessage alloc] init]; pigeonResult.currencySymbol = currencySymbol; pigeonResult.currencyCode = currencyCode; pigeonResult.countryCode = countryCode; @@ -412,13 +417,13 @@ - (NSArray *)toList { @implementation SKProductDiscountMessage + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger )numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type { - SKProductDiscountMessage* pigeonResult = [[SKProductDiscountMessage alloc] init]; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger)numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type { + SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = price; pigeonResult.priceLocale = priceLocale; pigeonResult.numberOfPeriods = numberOfPeriods; @@ -431,10 +436,12 @@ + (instancetype)makeWithPrice:(NSString *)price + (SKProductDiscountMessage *)fromList:(NSArray *)list { SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = GetNullableObjectAtIndex(list, 0); - pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.priceLocale = + [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; pigeonResult.numberOfPeriods = [GetNullableObjectAtIndex(list, 2) integerValue]; pigeonResult.paymentMode = [GetNullableObjectAtIndex(list, 3) integerValue]; - pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; + pigeonResult.subscriptionPeriod = + [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; pigeonResult.identifier = GetNullableObjectAtIndex(list, 5); pigeonResult.type = [GetNullableObjectAtIndex(list, 6) integerValue]; return pigeonResult; @@ -456,15 +463,17 @@ - (NSArray *)toList { @end @implementation SKProductSubscriptionPeriodMessage -+ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit { - SKProductSubscriptionPeriodMessage* pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; ++ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit { + SKProductSubscriptionPeriodMessage *pigeonResult = + [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = numberOfUnits; pigeonResult.unit = unit; return pigeonResult; } + (SKProductSubscriptionPeriodMessage *)fromList:(NSArray *)list { - SKProductSubscriptionPeriodMessage *pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; + SKProductSubscriptionPeriodMessage *pigeonResult = + [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = [GetNullableObjectAtIndex(list, 0) integerValue]; pigeonResult.unit = [GetNullableObjectAtIndex(list, 1) integerValue]; return pigeonResult; @@ -485,25 +494,25 @@ @interface InAppPurchaseAPICodecReader : FlutterStandardReader @implementation InAppPurchaseAPICodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [SKErrorMessage fromList:[self readValue]]; - case 129: + case 129: return [SKPaymentDiscountMessage fromList:[self readValue]]; - case 130: + case 130: return [SKPaymentMessage fromList:[self readValue]]; - case 131: + case 131: return [SKPaymentTransactionMessage fromList:[self readValue]]; - case 132: + case 132: return [SKPriceLocaleMessage fromList:[self readValue]]; - case 133: + case 133: return [SKProductDiscountMessage fromList:[self readValue]]; - case 134: + case 134: return [SKProductMessage fromList:[self readValue]]; - case 135: + case 135: return [SKProductSubscriptionPeriodMessage fromList:[self readValue]]; - case 136: + case 136: return [SKProductsResponseMessage fromList:[self readValue]]; - case 137: + case 137: return [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -566,22 +575,27 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - InAppPurchaseAPICodecReaderWriter *readerWriter = [[InAppPurchaseAPICodecReaderWriter alloc] init]; + InAppPurchaseAPICodecReaderWriter *readerWriter = + [[InAppPurchaseAPICodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *api) { +void SetUpInAppPurchaseAPI(id binaryMessenger, + NSObject *api) { /// Returns if the current device is able to make payments { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(canMakePaymentsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", api); + NSCAssert( + [api respondsToSelector:@selector(canMakePaymentsWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSNumber *output = [api canMakePaymentsWithError:&error]; @@ -592,13 +606,14 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", api); + NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSArray *output = [api transactionsWithError:&error]; @@ -609,13 +624,14 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", api); + NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; SKStorefrontMessage *output = [api storefrontWithError:&error]; @@ -626,13 +642,15 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(addPaymentPaymentMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(addPaymentPaymentMap:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); @@ -645,32 +663,41 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:completion:)", api); + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers: + completion:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startProductRequestProductIdentifiers:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); - [api startProductRequestProductIdentifiers:arg_productIdentifiers completion:^(SKProductsResponseMessage *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api startProductRequestProductIdentifiers:arg_productIdentifiers + completion:^(SKProductsResponseMessage *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(finishTransactionFinishMap:error:)", api); + NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(finishTransactionFinishMap:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); @@ -683,13 +710,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(restoreTransactionsApplicationUserName:error:)", api); + NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(restoreTransactionsApplicationUserName:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_applicationUserName = GetNullableObjectAtIndex(args, 0); @@ -702,13 +732,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"presentCodeRedemptionSheet" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(presentCodeRedemptionSheetWithError:)", api); + NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(presentCodeRedemptionSheetWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api presentCodeRedemptionSheetWithError:&error]; @@ -719,13 +752,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(retrieveReceiptDataWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(retrieveReceiptDataWithError:)", api); + NSCAssert( + [api respondsToSelector:@selector(retrieveReceiptDataWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(retrieveReceiptDataWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSString *output = [api retrieveReceiptDataWithError:&error]; @@ -736,32 +772,39 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(refreshReceiptReceiptProperties:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(refreshReceiptReceiptProperties:completion:)", api); + NSCAssert([api respondsToSelector:@selector(refreshReceiptReceiptProperties:completion:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(refreshReceiptReceiptProperties:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_receiptProperties = GetNullableObjectAtIndex(args, 0); - [api refreshReceiptReceiptProperties:arg_receiptProperties completion:^(FlutterError *_Nullable error) { - callback(wrapResult(nil, error)); - }]; + [api refreshReceiptReceiptProperties:arg_receiptProperties + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"startObservingPaymentQueue" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startObservingPaymentQueueWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startObservingPaymentQueueWithError:)", api); + NSCAssert([api respondsToSelector:@selector(startObservingPaymentQueueWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startObservingPaymentQueueWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api startObservingPaymentQueueWithError:&error]; @@ -772,13 +815,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"stopObservingPaymentQueue" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(stopObservingPaymentQueueWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(stopObservingPaymentQueueWithError:)", api); + NSCAssert([api respondsToSelector:@selector(stopObservingPaymentQueueWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(stopObservingPaymentQueueWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api stopObservingPaymentQueueWithError:&error]; @@ -789,13 +835,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"registerPaymentQueueDelegate" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(registerPaymentQueueDelegateWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(registerPaymentQueueDelegateWithError:)", api); + NSCAssert([api respondsToSelector:@selector(registerPaymentQueueDelegateWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(registerPaymentQueueDelegateWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api registerPaymentQueueDelegateWithError:&error]; @@ -806,13 +855,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"removePaymentQueueDelegate" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(removePaymentQueueDelegateWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(removePaymentQueueDelegateWithError:)", api); + NSCAssert([api respondsToSelector:@selector(removePaymentQueueDelegateWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(removePaymentQueueDelegateWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api removePaymentQueueDelegateWithError:&error]; @@ -823,13 +875,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"showPriceConsentIfNeeded" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(showPriceConsentIfNeededWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(showPriceConsentIfNeededWithError:)", api); + NSCAssert([api respondsToSelector:@selector(showPriceConsentIfNeededWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(showPriceConsentIfNeededWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api showPriceConsentIfNeededWithError:&error]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 358b887c6f7d..5b7ba6149797 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; }; F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */; }; - F2312E722BC8AE9B00380F55 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = F2312E712BC8AE9B00380F55 /* Tests.m */; }; F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; }; F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */; }; F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; }; @@ -82,7 +81,6 @@ E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStubs.swift; sourceTree = ""; }; - F2312E712BC8AE9B00380F55 /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIATransactionCacheTests.m; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; @@ -198,7 +196,6 @@ 688DE35021F2A5A100EA2684 /* TranslatorTests.m */, F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */, F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */, - F2312E712BC8AE9B00380F55 /* Tests.m */, F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */, F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */, ); @@ -442,7 +439,6 @@ 6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */, 688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */, F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */, - F2312E722BC8AE9B00380F55 /* Tests.m in Sources */, A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */, 6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */, ); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h index 5de86f383434..25e1271864bc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -1,5 +1,6 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // #import "Stubs.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m deleted file mode 100644 index bfea5d33c67b..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Tests.m +++ /dev/null @@ -1,835 +0,0 @@ -// 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 -#import -#import "FIAPaymentQueueHandler.h" -#import "Stubs.h" -#import "RunnerTests-Swift.h" - -@import in_app_purchase_storekit; - -@interface InAppPurchasePluginTest : XCTestCase - -@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; -@property(strong, nonatomic) InAppPurchasePlugin *plugin; - -@end - -@implementation InAppPurchasePluginTest - -- (void)setUp { - self.receiptManagerStub = [FIAPReceiptManagerStub new]; - self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; -} - -- (void)tearDown { -} - -- (void)testCanMakePayments { - FlutterError *error; - NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; - XCTAssertTrue([result boolValue]); - XCTAssertNil(error); -} - -- (void)testPaymentQueueStorefront { - if (@available(iOS 13, macOS 10.15, *)) { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary *storefrontMap = @{ - @"countryCode" : @"USA", - @"identifier" : @"unique_identifier", - }; - - OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - FlutterError *error; - SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; - - XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); - XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); - XCTAssertNil(error); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } -} - -- (void)testPaymentQueueStorefrontReturnsNil { - if (@available(iOS 13, macOS 10.15, *)) { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - - OCMStub(mockQueue.storefront).andReturn(nil); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - FlutterError *error; - SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; - - XCTAssertNil(resultMap); - XCTAssertNil(error); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } -} - -- (void)testGetProductResponse { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - XCTAssert( - [response isKindOfClass:[SKProductsResponseMessage class]]); - XCTAssertEqual(response.products.count, 1); - XCTAssertEqual(response.invalidProductIdentifiers.count, 0); - XCTAssertEqual(response.products[0].productIdentifier, @"123"); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testFinishTransactionSucceeds { - NSDictionary *args = @{ - @"transactionIdentifier" : @"567", - @"productIdentifier" : @"unique_identifier", - }; - - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = @[ paymentTransaction ]; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); - - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin finishTransactionFinishMap:args error:&error]; - - XCTAssertNil(error); -} - -- (void)testFinishTransactionSucceedsWithNilTransaction { - NSDictionary *args = @{ - @"transactionIdentifier" : [NSNull null], - @"productIdentifier" : @"unique_identifier", - }; - - NSDictionary *paymentMap = @{ - @"productIdentifier" : @"123", - @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", - @"quantity" : @(2), - @"applicationUsername" : @"app user name", - @"simulatesAskToBuyInSandbox" : @(NO) - }; - - NSDictionary *transactionMap = @{ - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : paymentMap, - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); - - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin finishTransactionFinishMap:args error:&error]; - - XCTAssertNil(error); -} - -- (void)testGetProductResponseWithRequestError { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *error = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, - nil])]); - - [plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNotNil(startProductRequestError); - XCTAssertEqualObjects( - startProductRequestError.code, - @"storekit_getproductrequest_platform_error"); - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testGetProductResponseWithNoResponse { - NSArray *argument = @[ @"123" ]; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *error = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], - [NSNull null], nil])]); - - [plugin - startProductRequestProductIdentifiers:argument - completion:^(SKProductsResponseMessage *_Nullable response, - FlutterError *_Nullable startProductRequestError) { - [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNotNil(startProductRequestError); - XCTAssertEqualObjects(startProductRequestError.code, - @"storekit_platform_no_response"); - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); - XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); - XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " - @"Please either wait for it to be finished or finish it manually " - @"using `completePurchase` to avoid edge cases.", - error.message); - XCTAssertEqualObjects(argument, error.details); -} - -- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { - NSDictionary *argument = @{ - // stubbed function will return nil for an empty productIdentifier - @"productIdentifier" : @"", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); - XCTAssertEqualObjects( - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue.", - error.message); - XCTAssertEqualObjects(argument, error.details); -} - -- (void)testAddPaymentSuccessWithoutPaymentDiscount { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertNil(error); - OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - XCTAssert(payment != nil); - XCTAssertEqual(payment.productIdentifier, @"123"); - XCTAssert(payment.quantity == 1); - return YES; - }]]); -} - -- (void)testAddPaymentSuccessWithPaymentDiscount { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"identifier" : @"test_identifier", - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - XCTAssertNil(error); - OCMVerify( - times(1), - [mockHandler - addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *discount = payment.paymentDiscount; - - return [discount.identifier isEqual:@"test_identifier"] && - [discount.keyIdentifier isEqual:@"test_key_identifier"] && - [discount.nonce - isEqual:[[NSUUID alloc] - initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && - [discount.signature isEqual:@"test_signature"] && - [discount.timestamp isEqual:@(1635847102)]; - } - - return YES; - }]]); -} - -- (void)testAddPaymentFailureWithInvalidPaymentDiscount { - // Support for payment discount is only available on iOS 12.2 and higher. - if (@available(iOS 12.2, *)) { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - id translator = OCMClassMock(FIAObjectTranslator.class); - - NSString *errorMsg = @"Some error occurred"; - OCMStub(ClassMethod([translator - getSKPaymentDiscountFromMap:[OCMArg any] - withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) - .andReturn(nil); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - - XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); - XCTAssertEqualObjects(@"You have requested a payment and specified a " - @"payment discount with invalid properties. Some error occurred", - error.message); - XCTAssertEqualObjects(argument, error.details); - OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); - } -} - -- (void)testAddPaymentWithNullSandboxArgument { - NSDictionary *argument = @{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : [NSNull null], - }; - - FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); - OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); - self.plugin.paymentQueueHandler = mockHandler; - FlutterError *error; - - [self.plugin addPaymentPaymentMap:argument error:&error]; - OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - return !payment.simulatesAskToBuyInSandbox; - }]]); -} - -- (void)testRestoreTransactions { - XCTestExpectation *expectation = - [self expectationWithDescription:@"result successfully restore transactions"]; - - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - - __block BOOL callbackInvoked = NO; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:^() { - callbackInvoked = YES; - [expectation fulfill]; - } - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; - - FlutterError *error; - [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; - - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertTrue(callbackInvoked); -} - -- (void)testRetrieveReceiptDataSuccess { - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - XCTAssertNotNil(result); - XCTAssert([result isKindOfClass:[NSString class]]); -} - -- (void)testRetrieveReceiptDataNil { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - XCTAssertNil(result); -} - -- (void)testRetrieveReceiptDataError { - self.receiptManagerStub.returnError = YES; - - FlutterError *error; - NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; - - XCTAssertNil(result); - XCTAssertNotNil(error); - XCTAssert([error.code isKindOfClass:[NSString class]]); - NSDictionary *details = error.details; - XCTAssertNotNil(details[@"error"]); - NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; - XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); -} - -- (void)testRefreshReceiptRequest { - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:nil - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testRefreshReceiptRequestWithParams { - NSDictionary *properties = @{ - @"isExpired" : @NO, - @"isRevoked" : @NO, - @"isVolumePurchase" : @NO, - }; - - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testRefreshReceiptRequestWithError { - NSDictionary *properties = @{ - @"isExpired" : @NO, - @"isRevoked" : @NO, - @"isVolumePurchase" : @NO, - }; - XCTestExpectation *expectation = - [self expectationWithDescription:@"completion handler successfully called"]; - - id mockHandler = OCMClassMock([FIAPRequestHandler class]); - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil - handlerFactory:^FIAPRequestHandler *(SKRequest *request) { - return mockHandler; - }]; - - NSError *recieptError = [NSError errorWithDomain:@"errorDomain" - code:0 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; - - OCMStub([mockHandler - startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], - recieptError, nil])]); - - [plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error.code, @"storekit_refreshreceiptrequest_platform_error"); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -/// presentCodeRedemptionSheetWithError:error is only available on iOS -#if TARGET_OS_IOS -- (void)testPresentCodeRedemptionSheet { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin presentCodeRedemptionSheetWithError:&error]; - - OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); -} -#endif - -- (void)testGetPendingTransactions { - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : [NSNull null], - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - @"originalTransaction" : [NSNull null], - }; - OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] - initWithMap:transactionMap] ]); - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - FlutterError *error; - SKPaymentTransactionStub *original = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - - SKPaymentTransactionMessage *originalPigeon = - [FIAObjectTranslator convertTransactionToPigeon:original]; - SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; - - XCTAssertEqualObjects([self paymentTransactionToList:result], - [self paymentTransactionToList:originalPigeon]); -} - -- (void)testStartObservingPaymentQueue { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin startObservingPaymentQueueWithError:&error]; - - OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); -} - -- (void)testStopObservingPaymentQueue { - FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); - self.plugin.paymentQueueHandler = mockHandler; - - FlutterError *error; - [self.plugin stopObservingPaymentQueueWithError:&error]; - - OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); -} - -#if TARGET_OS_IOS -- (void)testRegisterPaymentQueueDelegate { - if (@available(iOS 13, *)) { - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - - self.plugin.registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); - - id registrarMock = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); - self.plugin.registrar = registrarMock; - - id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); - OCMStub([registrarMock messenger]).andReturn(binaryMessengerMock); - - - // Verify the delegate is nil before we register one. - XCTAssertNil(self.plugin.paymentQueueHandler.delegate); - - FlutterError *error; - [self.plugin registerPaymentQueueDelegateWithError:&error]; - - // Verify the delegate is not nil after we registered one. - XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); - } -} -#endif - -- (void)testRemovePaymentQueueDelegate { - if (@available(iOS 13, *)) { - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); - - // Verify the delegate is not nil before removing it. - XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); - - FlutterError *error; - [self.plugin removePaymentQueueDelegateWithError:&error]; - - // Verify the delegate is nill after removing it. - XCTAssertNil(self.plugin.paymentQueueHandler.delegate); - } -} - -- (void)testHandleTransactionsUpdated { - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; - NSMutableArray *maps = [NSMutableArray new]; - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; - - [plugin handleTransactionsUpdated:array]; - OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); -} - -- (void)testHandleTransactionsRemoved { - NSDictionary *transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - }; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; - NSMutableArray *maps = [NSMutableArray new]; - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; - - [plugin handleTransactionsRemoved:array]; - OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); -} - -- (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - NSError *error = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; - [plugin handleTransactionRestoreFailed:error]; - OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" - arguments:[FIAObjectTranslator getMapFromNSError:error]]); -} - -- (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - [plugin restoreCompletedTransactionsFinished]; - OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" - arguments:nil]); -} - -- (void)testShouldAddStorePayment { - NSDictionary *paymentMap = @{ - @"productIdentifier" : @"123", - @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", - @"quantity" : @(2), - @"applicationUsername" : @"app user name", - @"simulatesAskToBuyInSandbox" : @(NO) - }; - - NSDictionary *productMap = @{ - @"price" : @"1", - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], - @"productIdentifier" : @"123", - @"localizedTitle" : @"title", - @"localizedDescription" : @"des", - }; - - SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; - SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; - FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); - plugin.transactionObserverCallbackChannel = mockChannel; - OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - - NSDictionary *args = @{ - @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], - @"product" : [FIAObjectTranslator getMapFromSKProduct:product] - }; - - BOOL result = [plugin shouldAddStorePaymentWithPayment:payment product:product]; - XCTAssertEqual(result, NO); - OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); -} - -#if TARGET_OS_IOS -- (void)testShowPriceConsentIfNeeded { - FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); - self.plugin.paymentQueueHandler = mockQueueHandler; - - FlutterError *error; - [self.plugin showPriceConsentIfNeededWithError:&error]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - if (@available(iOS 13.4, *)) { - OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); - } else { - OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); - } -#pragma clang diagnostic pop -} -#endif - -// The following methods are deserializer copied from Pigeon's output. - -- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { - return @[ - (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), - @(paymentTransaction.transactionState), - (paymentTransaction.originalTransaction - ? [self paymentTransactionToList:paymentTransaction.originalTransaction] - : [NSNull null]), - paymentTransaction.transactionTimeStamp ?: [NSNull null], - paymentTransaction.transactionIdentifier ?: [NSNull null], - (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), - ]; -} - -- (NSArray *)paymentToList:(SKPaymentMessage *)payment { - return @[ - payment.productIdentifier ?: [NSNull null], - payment.applicationUsername ?: [NSNull null], - payment.requestData ?: [NSNull null], - @(payment.quantity), - @(payment.simulatesAskToBuyInSandbox), - (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] - : [NSNull null]), - ]; -} - -- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { - return @[ - discount.identifier ?: [NSNull null], - discount.keyIdentifier ?: [NSNull null], - discount.nonce ?: [NSNull null], - discount.signature ?: [NSNull null], - @(discount.timestamp), - ]; -} - -- (NSArray *)errorToList:(SKErrorMessage *)error { - return @[ - @(error.code), - error.domain ?: [NSNull null], - error.userInfo ?: [NSNull null], - ]; -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index b0bc0df6de0e..71da8fe075f9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -1,825 +1,834 @@ -//// 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 -//#import -//#import "FIAPaymentQueueHandler.h" -//#import "Stubs.h" -// -//@import in_app_purchase_storekit; -// -//@interface InAppPurchasePluginTest : XCTestCase -// -//@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; -//@property(strong, nonatomic) InAppPurchasePlugin *plugin; -// -//@end -// -//@implementation InAppPurchasePluginTest -// -//- (void)setUp { -// self.receiptManagerStub = [FIAPReceiptManager new]; -// self.plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:self.receiptManagerStub]; -//} -// -//- (void)tearDown { -//} -// -//- (void)testCanMakePayments { -// FlutterError *error; -// NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; -// XCTAssertTrue([result boolValue]); -// XCTAssertNil(error); -//} -// -//- (void)testPaymentQueueStorefront { -// if (@available(iOS 13, macOS 10.15, *)) { -// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); -// NSDictionary *storefrontMap = @{ -// @"countryCode" : @"USA", -// @"identifier" : @"unique_identifier", -// }; -// -// OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); -// -// self.plugin.paymentQueueHandler = -// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue -// transactionsUpdated:nil -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:nil -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// -// FlutterError *error; -// SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; -// -// XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); -// XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); -// XCTAssertNil(error); -// } else { -// NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); -// } -//} -// -//- (void)testPaymentQueueStorefrontReturnsNil { -// if (@available(iOS 13, macOS 10.15, *)) { -// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); -// -// OCMStub(mockQueue.storefront).andReturn(nil); -// -// self.plugin.paymentQueueHandler = -// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue -// transactionsUpdated:nil -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:nil -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// -// FlutterError *error; -// SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; -// -// XCTAssertNil(resultMap); -// XCTAssertNil(error); -// } else { -// NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); -// } -//} -// -//- (void)testGetProductResponse { -// NSArray *argument = @[ @"123" ]; -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// [self.plugin -// startProductRequestProductIdentifiers:argument -// completion:^(SKProductsResponseMessage *_Nullable response, -// FlutterError *_Nullable startProductRequestError) { -// XCTAssert( -// [response isKindOfClass:[SKProductsResponseMessage class]]); -// XCTAssertEqual(response.products.count, 1); -// XCTAssertEqual(response.invalidProductIdentifiers.count, 0); -// XCTAssertEqual(response.products[0].productIdentifier, @"123"); -// [expectation fulfill]; -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -//- (void)testFinishTransactionSucceeds { -// NSDictionary *args = @{ -// @"transactionIdentifier" : @"567", -// @"productIdentifier" : @"unique_identifier", -// }; -// -// NSDictionary *transactionMap = @{ -// @"transactionIdentifier" : @"567", -// @"transactionState" : @(SKPaymentTransactionStatePurchasing), -// @"payment" : [NSNull null], -// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" -// code:123 -// userInfo:@{}]], -// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), -// }; -// -// SKPaymentTransactionStub *paymentTransaction = -// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; -// NSArray *array = @[ paymentTransaction ]; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); -// -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// [self.plugin finishTransactionFinishMap:args error:&error]; -// -// XCTAssertNil(error); -//} -// -//- (void)testFinishTransactionSucceedsWithNilTransaction { -// NSDictionary *args = @{ -// @"transactionIdentifier" : [NSNull null], -// @"productIdentifier" : @"unique_identifier", -// }; -// -// NSDictionary *paymentMap = @{ -// @"productIdentifier" : @"123", -// @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", -// @"quantity" : @(2), -// @"applicationUsername" : @"app user name", -// @"simulatesAskToBuyInSandbox" : @(NO) -// }; -// -// NSDictionary *transactionMap = @{ -// @"transactionState" : @(SKPaymentTransactionStatePurchasing), -// @"payment" : paymentMap, -// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" -// code:123 -// userInfo:@{}]], -// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), -// }; -// -// SKPaymentTransactionStub *paymentTransaction = -// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); -// -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// [self.plugin finishTransactionFinishMap:args error:&error]; -// -// XCTAssertNil(error); -//} -// -//- (void)testGetProductResponseWithRequestError { -// NSArray *argument = @[ @"123" ]; -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// -// id mockHandler = OCMClassMock([FIAPRequestHandler class]); -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] -// initWithReceiptManager:nil -// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { -// return mockHandler; -// }]; -// -// NSError *error = [NSError errorWithDomain:@"errorDomain" -// code:0 -// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; -// -// OCMStub([mockHandler -// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, -// nil])]); -// -// [plugin -// startProductRequestProductIdentifiers:argument -// completion:^(SKProductsResponseMessage *_Nullable response, -// FlutterError *_Nullable startProductRequestError) { -// [expectation fulfill]; -// XCTAssertNotNil(error); -// XCTAssertNotNil(startProductRequestError); -// XCTAssertEqualObjects( -// startProductRequestError.code, -// @"storekit_getproductrequest_platform_error"); -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -//- (void)testGetProductResponseWithNoResponse { -// NSArray *argument = @[ @"123" ]; -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// -// id mockHandler = OCMClassMock([FIAPRequestHandler class]); -// -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] -// initWithReceiptManager:nil -// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { -// return mockHandler; -// }]; -// -// NSError *error = [NSError errorWithDomain:@"errorDomain" -// code:0 -// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; -// -// OCMStub([mockHandler -// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], -// [NSNull null], nil])]); -// -// [plugin -// startProductRequestProductIdentifiers:argument -// completion:^(SKProductsResponseMessage *_Nullable response, -// FlutterError *_Nullable startProductRequestError) { -// [expectation fulfill]; -// XCTAssertNotNil(error); -// XCTAssertNotNil(startProductRequestError); -// XCTAssertEqualObjects(startProductRequestError.code, -// @"storekit_platform_no_response"); -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -//- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { -// NSDictionary *argument = @{ -// @"productIdentifier" : @"123", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : @YES, -// }; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// -// OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); -// XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); -// XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " -// @"Please either wait for it to be finished or finish it manually " -// @"using `completePurchase` to avoid edge cases.", -// error.message); -// XCTAssertEqualObjects(argument, error.details); -//} -// -//- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { -// NSDictionary *argument = @{ -// // stubbed function will return nil for an empty productIdentifier -// @"productIdentifier" : @"", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : @YES, -// }; -// -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// -// XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); -// XCTAssertEqualObjects( -// @"You have requested a payment for an invalid product. Either the " -// @"`productIdentifier` of the payment is not valid or the product has not been " -// @"fetched before adding the payment to the payment queue.", -// error.message); -// XCTAssertEqualObjects(argument, error.details); -//} -// -//- (void)testAddPaymentSuccessWithoutPaymentDiscount { -// NSDictionary *argument = @{ -// @"productIdentifier" : @"123", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : @YES, -// }; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); -// self.plugin.paymentQueueHandler = mockHandler; -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// -// XCTAssertNil(error); -// OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { -// SKPayment *payment = obj; -// XCTAssert(payment != nil); -// XCTAssertEqual(payment.productIdentifier, @"123"); -// XCTAssert(payment.quantity == 1); -// return YES; -// }]]); -//} -// -//- (void)testAddPaymentSuccessWithPaymentDiscount { -// NSDictionary *argument = @{ -// @"productIdentifier" : @"123", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : @YES, -// @"paymentDiscount" : @{ -// @"identifier" : @"test_identifier", -// @"keyIdentifier" : @"test_key_identifier", -// @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", -// @"signature" : @"test_signature", -// @"timestamp" : @(1635847102), -// } -// }; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// XCTAssertNil(error); -// OCMVerify( -// times(1), -// [mockHandler -// addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { -// SKPayment *payment = obj; -// if (@available(iOS 12.2, *)) { -// SKPaymentDiscount *discount = payment.paymentDiscount; -// -// return [discount.identifier isEqual:@"test_identifier"] && -// [discount.keyIdentifier isEqual:@"test_key_identifier"] && -// [discount.nonce -// isEqual:[[NSUUID alloc] -// initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && -// [discount.signature isEqual:@"test_signature"] && -// [discount.timestamp isEqual:@(1635847102)]; -// } -// -// return YES; -// }]]); -//} -// -//- (void)testAddPaymentFailureWithInvalidPaymentDiscount { -// // Support for payment discount is only available on iOS 12.2 and higher. -// if (@available(iOS 12.2, *)) { -// NSDictionary *argument = @{ -// @"productIdentifier" : @"123", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : @YES, -// @"paymentDiscount" : @{ -// @"keyIdentifier" : @"test_key_identifier", -// @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", -// @"signature" : @"test_signature", -// @"timestamp" : @(1635847102), -// } -// }; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// id translator = OCMClassMock(FIAObjectTranslator.class); -// -// NSString *errorMsg = @"Some error occurred"; -// OCMStub(ClassMethod([translator -// getSKPaymentDiscountFromMap:[OCMArg any] -// withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) -// .andReturn(nil); -// self.plugin.paymentQueueHandler = mockHandler; -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// -// XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); -// XCTAssertEqualObjects(@"You have requested a payment and specified a " -// @"payment discount with invalid properties. Some error occurred", -// error.message); -// XCTAssertEqualObjects(argument, error.details); -// OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); -// } -//} -// -//- (void)testAddPaymentWithNullSandboxArgument { -// NSDictionary *argument = @{ -// @"productIdentifier" : @"123", -// @"quantity" : @(1), -// @"simulatesAskToBuyInSandbox" : [NSNull null], -// }; -// -// FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); -// self.plugin.paymentQueueHandler = mockHandler; -// FlutterError *error; -// -// [self.plugin addPaymentPaymentMap:argument error:&error]; -// OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { -// SKPayment *payment = obj; -// return !payment.simulatesAskToBuyInSandbox; -// }]]); -//} -// -//- (void)testRestoreTransactions { -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"result successfully restore transactions"]; -// -// SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; -// queue.testState = SKPaymentTransactionStatePurchased; -// -// __block BOOL callbackInvoked = NO; -// self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue -// transactionsUpdated:^(NSArray *_Nonnull transactions) { -// } -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:^() { -// callbackInvoked = YES; -// [expectation fulfill]; -// } -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// [queue addTransactionObserver:self.plugin.paymentQueueHandler]; -// -// FlutterError *error; -// [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; -// -// [self waitForExpectations:@[ expectation ] timeout:5]; -// XCTAssertTrue(callbackInvoked); -//} -// -//- (void)testRetrieveReceiptDataSuccess { -// FlutterError *error; -// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; -// XCTAssertNotNil(result); -// XCTAssert([result isKindOfClass:[NSString class]]); -//} -// -//- (void)testRetrieveReceiptDataNil { -// NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); -// OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); -// FlutterError *error; -// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; -// XCTAssertNil(result); -//} -// -//- (void)testRetrieveReceiptDataError { -// self.receiptManagerStub.returnError = YES; -// -// FlutterError *error; -// NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; -// -// XCTAssertNil(result); -// XCTAssertNotNil(error); -// XCTAssert([error.code isKindOfClass:[NSString class]]); -// NSDictionary *details = error.details; -// XCTAssertNotNil(details[@"error"]); -// NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; -// XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); -//} -// -//- (void)testRefreshReceiptRequest { -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// [self.plugin refreshReceiptReceiptProperties:nil -// completion:^(FlutterError *_Nullable error) { -// [expectation fulfill]; -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -//- (void)testRefreshReceiptRequestWithParams { -// NSDictionary *properties = @{ -// @"isExpired" : @NO, -// @"isRevoked" : @NO, -// @"isVolumePurchase" : @NO, -// }; -// -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// [self.plugin refreshReceiptReceiptProperties:properties -// completion:^(FlutterError *_Nullable error) { -// [expectation fulfill]; -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -//- (void)testRefreshReceiptRequestWithError { -// NSDictionary *properties = @{ -// @"isExpired" : @NO, -// @"isRevoked" : @NO, -// @"isVolumePurchase" : @NO, -// }; -// XCTestExpectation *expectation = -// [self expectationWithDescription:@"completion handler successfully called"]; -// -// id mockHandler = OCMClassMock([FIAPRequestHandler class]); -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] -// initWithReceiptManager:nil -// handlerFactory:^FIAPRequestHandler *(SKRequest *request) { -// return mockHandler; -// }]; -// -// NSError *recieptError = [NSError errorWithDomain:@"errorDomain" -// code:0 -// userInfo:@{NSLocalizedDescriptionKey : @"description"}]; -// -// OCMStub([mockHandler -// startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], -// recieptError, nil])]); -// -// [plugin refreshReceiptReceiptProperties:properties -// completion:^(FlutterError *_Nullable error) { -// XCTAssertNotNil(error); -// XCTAssertEqualObjects( -// error.code, @"storekit_refreshreceiptrequest_platform_error"); -// [expectation fulfill]; -// }]; -// [self waitForExpectations:@[ expectation ] timeout:5]; -//} -// -///// presentCodeRedemptionSheetWithError:error is only available on iOS -//#if TARGET_OS_IOS -//- (void)testPresentCodeRedemptionSheet { -// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// [self.plugin presentCodeRedemptionSheetWithError:&error]; -// -// OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); -//} -//#endif -// -//- (void)testGetPendingTransactions { -// SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); -// NSDictionary *transactionMap = @{ -// @"transactionIdentifier" : [NSNull null], -// @"transactionState" : @(SKPaymentTransactionStatePurchasing), -// @"payment" : [NSNull null], -// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" -// code:123 -// userInfo:@{}]], -// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), -// @"originalTransaction" : [NSNull null], -// }; -// OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] -// initWithMap:transactionMap] ]); -// -// self.plugin.paymentQueueHandler = -// [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue -// transactionsUpdated:nil -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:nil -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// FlutterError *error; -// SKPaymentTransactionStub *original = -// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; -// -// SKPaymentTransactionMessage *originalPigeon = -// [FIAObjectTranslator convertTransactionToPigeon:original]; -// SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; -// -// XCTAssertEqualObjects([self paymentTransactionToList:result], -// [self paymentTransactionToList:originalPigeon]); -//} -// -//- (void)testStartObservingPaymentQueue { -// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// [self.plugin startObservingPaymentQueueWithError:&error]; -// -// OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); -//} -// -//- (void)testStopObservingPaymentQueue { -// FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); -// self.plugin.paymentQueueHandler = mockHandler; -// -// FlutterError *error; -// [self.plugin stopObservingPaymentQueueWithError:&error]; -// -// OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); -//} -// -//#if TARGET_OS_IOS -//- (void)testRegisterPaymentQueueDelegate { -// if (@available(iOS 13, *)) { -// self.plugin.paymentQueueHandler = -// [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] -// transactionsUpdated:nil -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:nil -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// -// // Verify the delegate is nil before we register one. -// XCTAssertNil(self.plugin.paymentQueueHandler.delegate); -// -// FlutterError *error; -// [self.plugin registerPaymentQueueDelegateWithError:&error]; -// -// // Verify the delegate is not nil after we registered one. -// XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); -// } -//} -//#endif -// -//- (void)testRemovePaymentQueueDelegate { -// if (@available(iOS 13, *)) { -// self.plugin.paymentQueueHandler = -// [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] -// transactionsUpdated:nil -// transactionRemoved:nil -// restoreTransactionFailed:nil -// restoreCompletedTransactionsFinished:nil -// shouldAddStorePayment:nil -// updatedDownloads:nil -// transactionCache:OCMClassMock(FIATransactionCache.class)]; -// self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); -// -// // Verify the delegate is not nil before removing it. -// XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); -// -// FlutterError *error; -// [self.plugin removePaymentQueueDelegateWithError:&error]; -// -// // Verify the delegate is nill after removing it. -// XCTAssertNil(self.plugin.paymentQueueHandler.delegate); -// } -//} -// -//- (void)testHandleTransactionsUpdated { -// NSDictionary *transactionMap = @{ -// @"transactionIdentifier" : @"567", -// @"transactionState" : @(SKPaymentTransactionStatePurchasing), -// @"payment" : [NSNull null], -// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" -// code:123 -// userInfo:@{}]], -// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), -// }; -// -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; -// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); -// plugin.transactionObserverCallbackChannel = mockChannel; -// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); -// -// SKPaymentTransactionStub *paymentTransaction = -// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; -// NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; -// NSMutableArray *maps = [NSMutableArray new]; -// [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; -// -// [plugin handleTransactionsUpdated:array]; -// OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); -//} -// -//- (void)testHandleTransactionsRemoved { -// NSDictionary *transactionMap = @{ -// @"transactionIdentifier" : @"567", -// @"transactionState" : @(SKPaymentTransactionStatePurchasing), -// @"payment" : [NSNull null], -// @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" -// code:123 -// userInfo:@{}]], -// @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), -// }; -// -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; -// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); -// plugin.transactionObserverCallbackChannel = mockChannel; -// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); -// -// SKPaymentTransactionStub *paymentTransaction = -// [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; -// NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; -// NSMutableArray *maps = [NSMutableArray new]; -// [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; -// -// [plugin handleTransactionsRemoved:array]; -// OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); -//} -// -//- (void)testHandleTransactionRestoreFailed { -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; -// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); -// plugin.transactionObserverCallbackChannel = mockChannel; -// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); -// -// NSError *error; -// [plugin handleTransactionRestoreFailed:error]; -// OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" -// arguments:[FIAObjectTranslator getMapFromNSError:error]]); -//} -// -//- (void)testRestoreCompletedTransactionsFinished { -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; -// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); -// plugin.transactionObserverCallbackChannel = mockChannel; -// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); -// -// [plugin restoreCompletedTransactionsFinished]; -// OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" -// arguments:nil]); -//} -// -//- (void)testShouldAddStorePayment { -// NSDictionary *paymentMap = @{ -// @"productIdentifier" : @"123", -// @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", -// @"quantity" : @(2), -// @"applicationUsername" : @"app user name", -// @"simulatesAskToBuyInSandbox" : @(NO) -// }; -// -// NSDictionary *productMap = @{ -// @"price" : @"1", -// @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], -// @"productIdentifier" : @"123", -// @"localizedTitle" : @"title", -// @"localizedDescription" : @"des", -// }; -// -// SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; -// SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; -// -// InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; -// FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); -// plugin.transactionObserverCallbackChannel = mockChannel; -// OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); -// -// NSDictionary *args = @{ -// @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], -// @"product" : [FIAObjectTranslator getMapFromSKProduct:product] -// }; -// -// BOOL result = [plugin shouldAddStorePayment:payment product:product]; -// XCTAssertEqual(result, NO); -// OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); -//} -// -//#if TARGET_OS_IOS -//- (void)testShowPriceConsentIfNeeded { -// FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); -// self.plugin.paymentQueueHandler = mockQueueHandler; -// -// FlutterError *error; -// [self.plugin showPriceConsentIfNeededWithError:&error]; -// -//#pragma clang diagnostic push -//#pragma clang diagnostic ignored "-Wpartial-availability" -// if (@available(iOS 13.4, *)) { -// OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); -// } else { -// OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); -// } -//#pragma clang diagnostic pop -//} -//#endif -// -//// The following methods are deserializer copied from Pigeon's output. -// -//- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { -// return @[ -// (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), -// @(paymentTransaction.transactionState), -// (paymentTransaction.originalTransaction -// ? [self paymentTransactionToList:paymentTransaction.originalTransaction] -// : [NSNull null]), -// paymentTransaction.transactionTimeStamp ?: [NSNull null], -// paymentTransaction.transactionIdentifier ?: [NSNull null], -// (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), -// ]; -//} -// -//- (NSArray *)paymentToList:(SKPaymentMessage *)payment { -// return @[ -// payment.productIdentifier ?: [NSNull null], -// payment.applicationUsername ?: [NSNull null], -// payment.requestData ?: [NSNull null], -// @(payment.quantity), -// @(payment.simulatesAskToBuyInSandbox), -// (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] -// : [NSNull null]), -// ]; -//} -// -//- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { -// return @[ -// discount.identifier ?: [NSNull null], -// discount.keyIdentifier ?: [NSNull null], -// discount.nonce ?: [NSNull null], -// discount.signature ?: [NSNull null], -// @(discount.timestamp), -// ]; -//} -// -//- (NSArray *)errorToList:(SKErrorMessage *)error { -// return @[ -// @(error.code), -// error.domain ?: [NSNull null], -// error.userInfo ?: [NSNull null], -// ]; -//} -//@end +// 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 +#import +#import "FIAPaymentQueueHandler.h" +#import "RunnerTests-Swift.h" +#import "Stubs.h" + +@import in_app_purchase_storekit; + +@interface InAppPurchasePluginTest : XCTestCase + +@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; +@property(strong, nonatomic) InAppPurchasePlugin *plugin; + +@end + +@implementation InAppPurchasePluginTest + +- (void)setUp { + self.receiptManagerStub = [FIAPReceiptManagerStub new]; + self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; +} + +- (void)tearDown { +} + +- (void)testCanMakePayments { + FlutterError *error; + NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; + XCTAssertTrue([result boolValue]); + XCTAssertNil(error); +} + +- (void)testPaymentQueueStorefront { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *storefrontMap = @{ + @"countryCode" : @"USA", + @"identifier" : @"unique_identifier", + }; + + OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; + + XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); + XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } +} + +- (void)testPaymentQueueStorefrontReturnsNil { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + + OCMStub(mockQueue.storefront).andReturn(nil); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; + + XCTAssertNil(resultMap); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } +} + +- (void)testGetProductResponse { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + XCTAssert( + [response isKindOfClass:[SKProductsResponseMessage class]]); + XCTAssertEqual(response.products.count, 1); + XCTAssertEqual(response.invalidProductIdentifiers.count, 0); + XCTAssertEqual(response.products[0].productIdentifier, @"123"); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testFinishTransactionSucceeds { + NSDictionary *args = @{ + @"transactionIdentifier" : @"567", + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = @[ paymentTransaction ]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testFinishTransactionSucceedsWithNilTransaction { + NSDictionary *args = @{ + @"transactionIdentifier" : [NSNull null], + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *transactionMap = @{ + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : paymentMap, + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testGetProductResponseWithRequestError { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, + nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects( + startProductRequestError.code, + @"storekit_getproductrequest_platform_error"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testGetProductResponseWithNoResponse { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + [NSNull null], nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects(startProductRequestError.code, + @"storekit_platform_no_response"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); + XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); + XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); + XCTAssertEqualObjects(argument, error.details); +} + +- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { + NSDictionary *argument = @{ + // stubbed function will return nil for an empty productIdentifier + @"productIdentifier" : @"", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue.", + error.message); + XCTAssertEqualObjects(argument, error.details); +} + +- (void)testAddPaymentSuccessWithoutPaymentDiscount { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertNil(error); + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + XCTAssert(payment != nil); + XCTAssertEqual(payment.productIdentifier, @"123"); + XCTAssert(payment.quantity == 1); + return YES; + }]]); +} + +- (void)testAddPaymentSuccessWithPaymentDiscount { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"identifier" : @"test_identifier", + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + XCTAssertNil(error); + OCMVerify( + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); +} + +- (void)testAddPaymentFailureWithInvalidPaymentDiscount { + // Support for payment discount is only available on iOS 12.2 and higher. + if (@available(iOS 12.2, *)) { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + id translator = OCMClassMock(FIAObjectTranslator.class); + + NSString *errorMsg = @"Some error occurred"; + OCMStub(ClassMethod([translator + getSKPaymentDiscountFromMap:[OCMArg any] + withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) + .andReturn(nil); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); + XCTAssertEqualObjects(@"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); + XCTAssertEqualObjects(argument, error.details); + OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); + } +} + +- (void)testAddPaymentWithNullSandboxArgument { + NSDictionary *argument = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : [NSNull null], + }; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + return !payment.simulatesAskToBuyInSandbox; + }]]); +} + +- (void)testRestoreTransactions { + XCTestExpectation *expectation = + [self expectationWithDescription:@"result successfully restore transactions"]; + + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; + queue.testState = SKPaymentTransactionStatePurchased; + + __block BOOL callbackInvoked = NO; + self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + } + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:^() { + callbackInvoked = YES; + [expectation fulfill]; + } + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + + FlutterError *error; + [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; + + [self waitForExpectations:@[ expectation ] timeout:5]; + XCTAssertTrue(callbackInvoked); +} + +- (void)testRetrieveReceiptDataSuccess { + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + XCTAssertNotNil(result); + XCTAssert([result isKindOfClass:[NSString class]]); +} + +- (void)testRetrieveReceiptDataNil { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + OCMStub(mockBundle.appStoreReceiptURL).andReturn(nil); + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + XCTAssertNil(result); +} + +- (void)testRetrieveReceiptDataError { + self.receiptManagerStub.returnError = YES; + + FlutterError *error; + NSString *result = [self.plugin retrieveReceiptDataWithError:&error]; + + XCTAssertNil(result); + XCTAssertNotNil(error); + XCTAssert([error.code isKindOfClass:[NSString class]]); + NSDictionary *details = error.details; + XCTAssertNotNil(details[@"error"]); + NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; + XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); +} + +- (void)testRefreshReceiptRequest { + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin refreshReceiptReceiptProperties:nil + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testRefreshReceiptRequestWithParams { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testRefreshReceiptRequestWithError { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects( + error.code, @"storekit_refreshreceiptrequest_platform_error"); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +/// presentCodeRedemptionSheetWithError:error is only available on iOS +#if TARGET_OS_IOS +- (void)testPresentCodeRedemptionSheet { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin presentCodeRedemptionSheetWithError:&error]; + + OCMVerify(times(1), [mockHandler presentCodeRedemptionSheet]); +} +#endif + +- (void)testGetPendingTransactions { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : [NSNull null], + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + @"originalTransaction" : [NSNull null], + }; + OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] + initWithMap:transactionMap] ]); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + FlutterError *error; + SKPaymentTransactionStub *original = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + SKPaymentTransactionMessage *originalPigeon = + [FIAObjectTranslator convertTransactionToPigeon:original]; + SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; + + XCTAssertEqualObjects([self paymentTransactionToList:result], + [self paymentTransactionToList:originalPigeon]); +} + +- (void)testStartObservingPaymentQueue { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin startObservingPaymentQueueWithError:&error]; + + OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); +} + +- (void)testStopObservingPaymentQueue { + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin stopObservingPaymentQueueWithError:&error]; + + OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); +} + +#if TARGET_OS_IOS +- (void)testRegisterPaymentQueueDelegate { + if (@available(iOS 13, *)) { + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + self.plugin.registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + + id registrarMock = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + self.plugin.registrar = registrarMock; + + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub([registrarMock messenger]).andReturn(binaryMessengerMock); + + // Verify the delegate is nil before we register one. + XCTAssertNil(self.plugin.paymentQueueHandler.delegate); + + FlutterError *error; + [self.plugin registerPaymentQueueDelegateWithError:&error]; + + // Verify the delegate is not nil after we registered one. + XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); + } +} +#endif + +- (void)testRemovePaymentQueueDelegate { + if (@available(iOS 13, *)) { + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueueStub new] + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); + + // Verify the delegate is not nil before removing it. + XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); + + FlutterError *error; + [self.plugin removePaymentQueueDelegateWithError:&error]; + + // Verify the delegate is nill after removing it. + XCTAssertNil(self.plugin.paymentQueueHandler.delegate); + } +} + +- (void)testHandleTransactionsUpdated { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsUpdated:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); +} + +- (void)testHandleTransactionsRemoved { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsRemoved:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); +} + +- (void)testHandleTransactionRestoreFailed { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSError *error = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; + [plugin handleTransactionRestoreFailed:error]; + OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" + arguments:[FIAObjectTranslator getMapFromNSError:error]]); +} + +- (void)testRestoreCompletedTransactionsFinished { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + [plugin restoreCompletedTransactionsFinished]; + OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" + arguments:nil]); +} + +- (void)testShouldAddStorePayment { + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *productMap = @{ + @"price" : @"1", + @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], + @"productIdentifier" : @"123", + @"localizedTitle" : @"title", + @"localizedDescription" : @"des", + }; + + SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; + SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSDictionary *args = @{ + @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], + @"product" : [FIAObjectTranslator getMapFromSKProduct:product] + }; + + BOOL result = [plugin shouldAddStorePaymentWithPayment:payment product:product]; + XCTAssertEqual(result, NO); + OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); +} + +#if TARGET_OS_IOS +- (void)testShowPriceConsentIfNeeded { + FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); + self.plugin.paymentQueueHandler = mockQueueHandler; + + FlutterError *error; + [self.plugin showPriceConsentIfNeededWithError:&error]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + if (@available(iOS 13.4, *)) { + OCMVerify(times(1), [mockQueueHandler showPriceConsentIfNeeded]); + } else { + OCMVerify(never(), [mockQueueHandler showPriceConsentIfNeeded]); + } +#pragma clang diagnostic pop +} +#endif + +// The following methods are deserializer copied from Pigeon's output. + +- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { + return @[ + (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), + @(paymentTransaction.transactionState), + (paymentTransaction.originalTransaction + ? [self paymentTransactionToList:paymentTransaction.originalTransaction] + : [NSNull null]), + paymentTransaction.transactionTimeStamp ?: [NSNull null], + paymentTransaction.transactionIdentifier ?: [NSNull null], + (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), + ]; +} + +- (NSArray *)paymentToList:(SKPaymentMessage *)payment { + return @[ + payment.productIdentifier ?: [NSNull null], + payment.applicationUsername ?: [NSNull null], + payment.requestData ?: [NSNull null], + @(payment.quantity), + @(payment.simulatesAskToBuyInSandbox), + (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] + : [NSNull null]), + ]; +} + +- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { + return @[ + discount.identifier ?: [NSNull null], + discount.keyIdentifier ?: [NSNull null], + discount.nonce ?: [NSNull null], + discount.signature ?: [NSNull null], + @(discount.timestamp), + ]; +} + +- (NSArray *)errorToList:(SKErrorMessage *)error { + return @[ + @(error.code), + error.domain ?: [NSNull null], + error.userInfo ?: [NSNull null], + ]; +} +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index ec163b4fd9a2..e136e05b5d1e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -18,7 +18,8 @@ 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 []; } @@ -35,27 +36,32 @@ enum SKPaymentTransactionStateMessage { /// transaction to update to another state. Never complete a transaction that /// is still in a purchasing state. purchasing, + /// The user's payment has been succesfully processed. /// /// You should provide the user the content that they purchased. purchased, + /// The transaction failed. /// /// Check the [PaymentTransactionWrapper.error] property from /// [PaymentTransactionWrapper] for details. failed, + /// This transaction is restoring content previously purchased by the user. /// /// The previous transaction information can be obtained in /// [PaymentTransactionWrapper.originalTransaction] from /// [PaymentTransactionWrapper]. restored, + /// The transaction is in the queue but pending external action. Wait for /// another callback to get the final state. /// /// You should update your UI to indicate that you are waiting for the /// transaction to update to another state. deferred, + /// Indicates the transaction is in an unspecified state. unspecified, } @@ -63,6 +69,7 @@ enum SKPaymentTransactionStateMessage { enum SKProductDiscountTypeMessage { /// A constant indicating the discount type is an introductory offer. introductory, + /// A constant indicating the discount type is a promotional offer. subscription, } @@ -70,10 +77,13 @@ enum SKProductDiscountTypeMessage { enum SKProductDiscountPaymentModeMessage { /// Allows user to pay the discounted price at each payment period. payAsYouGo, + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. payUpFront, + /// User pays nothing during the discounted period. freeTrial, + /// Unspecified mode. unspecified, } @@ -122,7 +132,8 @@ class SKPaymentTransactionMessage { result as List; return SKPaymentTransactionMessage( payment: SKPaymentMessage.decode(result[0]! as List), - transactionState: SKPaymentTransactionStateMessage.values[result[1]! as int], + transactionState: + SKPaymentTransactionStateMessage.values[result[1]! as int], originalTransaction: result[2] != null ? SKPaymentTransactionMessage.decode(result[2]! as List) : null, @@ -362,12 +373,14 @@ class SKProductMessage { subscriptionGroupIdentifier: result[4] as String?, price: result[5]! as String, subscriptionPeriod: result[6] != null - ? SKProductSubscriptionPeriodMessage.decode(result[6]! as List) + ? SKProductSubscriptionPeriodMessage.decode( + result[6]! as List) : null, introductoryPrice: result[7] != null ? SKProductDiscountMessage.decode(result[7]! as List) : null, - discounts: (result[8] as List?)?.cast(), + discounts: + (result[8] as List?)?.cast(), ); } } @@ -449,8 +462,10 @@ class SKProductDiscountMessage { price: result[0]! as String, priceLocale: SKPriceLocaleMessage.decode(result[1]! as List), numberOfPeriods: result[2]! as int, - paymentMode: SKProductDiscountPaymentModeMessage.values[result[3]! as int], - subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode(result[4]! as List), + paymentMode: + SKProductDiscountPaymentModeMessage.values[result[3]! as int], + subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode( + result[4]! as List), identifier: result[5] as String?, type: SKProductDiscountTypeMessage.values[result[6]! as int], ); @@ -525,25 +540,25 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -559,12 +574,15 @@ class InAppPurchaseAPI { : __pigeon_binaryMessenger = binaryMessenger; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _InAppPurchaseAPICodec(); + static const MessageCodec pigeonChannelCodec = + _InAppPurchaseAPICodec(); /// Returns if the current device is able to make payments Future canMakePayments() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -590,8 +608,10 @@ class InAppPurchaseAPI { } Future> transactions() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -612,13 +632,16 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)! + .cast(); } } Future storefront() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -644,8 +667,10 @@ class InAppPurchaseAPI { } Future addPayment(Map paymentMap) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -665,15 +690,18 @@ class InAppPurchaseAPI { } } - Future startProductRequest(List productIdentifiers) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + Future startProductRequest( + List productIdentifiers) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([productIdentifiers]) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([productIdentifiers]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -693,8 +721,10 @@ class InAppPurchaseAPI { } Future finishTransaction(Map finishMap) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -715,14 +745,16 @@ class InAppPurchaseAPI { } Future restoreTransactions(String? applicationUserName) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([applicationUserName]) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([applicationUserName]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -737,8 +769,10 @@ class InAppPurchaseAPI { } Future presentCodeRedemptionSheet() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -759,8 +793,10 @@ class InAppPurchaseAPI { } Future retrieveReceiptData() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -780,15 +816,18 @@ class InAppPurchaseAPI { } } - Future refreshReceipt({Map? receiptProperties}) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + Future refreshReceipt( + {Map? receiptProperties}) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([receiptProperties]) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([receiptProperties]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -803,8 +842,10 @@ class InAppPurchaseAPI { } Future startObservingPaymentQueue() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -825,8 +866,10 @@ class InAppPurchaseAPI { } Future stopObservingPaymentQueue() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -847,8 +890,10 @@ class InAppPurchaseAPI { } Future registerPaymentQueueDelegate() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -869,8 +914,10 @@ class InAppPurchaseAPI { } Future removePaymentQueueDelegate() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -891,8 +938,10 @@ class InAppPurchaseAPI { } Future showPriceConsentIfNeeded() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index df149267004a..6b0c9a77ab46 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -55,25 +55,25 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -82,8 +82,10 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } abstract class TestInAppPurchaseApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = _TestInAppPurchaseApiCodec(); + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = + _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments bool canMakePayments(); @@ -94,7 +96,8 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - Future startProductRequest(List productIdentifiers); + Future startProductRequest( + List productIdentifiers); void finishTransaction(Map finishMap); @@ -116,76 +119,102 @@ abstract class TestInAppPurchaseApi { void showPriceConsentIfNeeded(); - static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestInAppPurchaseApi? api, + {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { final bool output = api.canMakePayments(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { - final List output = api.transactions(); + final List output = + api.transactions(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { final SKStorefrontMessage output = api.storefront(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); final List args = (message as List?)!; - final Map? arg_paymentMap = (args[0] as Map?)?.cast(); + final Map? arg_paymentMap = + (args[0] as Map?)?.cast(); assert(arg_paymentMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null, expected non-null Map.'); try { @@ -193,49 +222,64 @@ abstract class TestInAppPurchaseApi { 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); final List args = (message as List?)!; - final List? arg_productIdentifiers = (args[0] as List?)?.cast(); + final List? arg_productIdentifiers = + (args[0] as List?)?.cast(); assert(arg_productIdentifiers != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null, expected non-null List.'); try { - final SKProductsResponseMessage output = await api.startProductRequest(arg_productIdentifiers!); + final SKProductsResponseMessage output = + await api.startProductRequest(arg_productIdentifiers!); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); final List args = (message as List?)!; - final Map? arg_finishMap = (args[0] as Map?)?.cast(); + final Map? arg_finishMap = + (args[0] as Map?)?.cast(); assert(arg_finishMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); try { @@ -243,22 +287,28 @@ abstract class TestInAppPurchaseApi { 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); final List args = (message as List?)!; final String? arg_applicationUserName = (args[0] as String?); try { @@ -266,164 +316,214 @@ abstract class TestInAppPurchaseApi { 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.presentCodeRedemptionSheet(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { final String? output = api.retrieveReceiptData(); return [output]; } 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt was null.'); final List args = (message as List?)!; - final Map? arg_receiptProperties = (args[0] as Map?)?.cast(); + final Map? arg_receiptProperties = + (args[0] as Map?)?.cast(); try { await api.refreshReceipt(receiptProperties: arg_receiptProperties); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.startObservingPaymentQueue(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.stopObservingPaymentQueue(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.registerPaymentQueueDelegate(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.removePaymentQueueDelegate(); 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())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.showPriceConsentIfNeeded(); 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())); } }); } From dfa4937bcf98c5ac90453f81112b59580c0e89e9 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Apr 2024 12:42:14 -0700 Subject: [PATCH 03/29] fix pod spec, licensing --- .../darwin/Classes/InAppPurchasePlugin.swift | 4 ++++ .../darwin/Classes/in_app_purchase_storekit.h | 9 +++------ .../darwin/in_app_purchase_storekit.podspec | 4 ---- .../example/ios/RunnerTests/SwiftStubs.swift | 10 +++------- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 73c7265d01c7..3f9aa558ff4d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -1,3 +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. + import Foundation import StoreKit diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h index f17a28b6d16d..74d30325ae46 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h @@ -1,9 +1,6 @@ -// -// in_app_purchase_storekit.h -// Pods -// -// Created by Louise Hsu on 4/11/24. -// +// 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. #ifndef in_app_purchase_storekit_h #define in_app_purchase_storekit_h diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec index d036cc30496e..892c3840caa8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec @@ -18,10 +18,6 @@ Downloaded by pub (not CocoaPods). s.documentation_url = 'https://pub.dev/packages/in_app_purchase' s.swift_version = '5.0' s.source_files = 'Classes/**/*.{h,m,swift}' -# s.xcconfig = { -# 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', -# 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', -# } s.public_header_files = 'Classes/**/*.h' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift index 09df45bf1cc1..f24f26e70853 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift @@ -1,10 +1,6 @@ -// -// SwiftStubs.swift -// RunnerTests -// -// Created by Louise Hsu on 4/12/24. -// Copyright © 2024 The Flutter Authors. All rights reserved. -// +// 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 Foundation From ccdcb280e90122805d1dc34b33f61ecd577ec877 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Apr 2024 12:45:39 -0700 Subject: [PATCH 04/29] version bump, fixed some typing --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 4 ++-- .../test/store_kit_wrappers/sk_methodchannel_apis_test.dart | 2 +- 4 files changed, 8 insertions(+), 4 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 114dc9e8df59..d58ddaad4b3b 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.15 + +* Converts main plugin class to Swift + ## 0.3.14 * Adds `countryCode` API. 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 d20cdec6ff15..a2b005787b3f 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.14 +version: 0.3.15 environment: sdk: ^3.2.3 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 f5a755d83b55..c1dd932d10f3 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 @@ -185,9 +185,9 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } @override - void finishTransaction(Map finishMap) { + void finishTransaction(Map finishMap) { finishedTransactions.add(createPurchasedTransaction( - finishMap['productIdentifier']!, finishMap['transactionIdentifier']!, + finishMap['productIdentifier']! as String, finishMap['transactionIdentifier']! as String, quantity: transactionList.first.payment.quantity)); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index 82775f6b2e21..f7fe5283d949 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -239,7 +239,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { [dummyTransactionMessage]; @override - void finishTransaction(Map finishMap) { + void finishTransaction(Map finishMap) { transactionsFinished.add(Map.from(finishMap)); } From e0c116756fb90c85dffd74f9323250a24ac35b5d Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 24 Apr 2024 10:42:04 -0700 Subject: [PATCH 05/29] format --- .../darwin/Classes/InAppPurchasePlugin.swift | 474 ++++++++++-------- .../example/ios/RunnerTests/SwiftStubs.swift | 20 +- .../test/fakes/fake_storekit_platform.dart | 3 +- 3 files changed, 281 insertions(+), 216 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 3f9aa558ff4d..4a6cfdf9c8f8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -6,9 +6,9 @@ import Foundation import StoreKit #if os(iOS) -import Flutter + import Flutter #elseif os(macOS) -import FlutterMacOS + import FlutterMacOS #endif extension FlutterError: Error {} @@ -18,7 +18,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { private(set) var productsCache: NSMutableDictionary = [:] private(set) var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? private(set) var receiptManager: FIAPReceiptManager? - private(set) var paymentQueueDelegate: Any? = nil; + private(set) var paymentQueueDelegate: Any? = nil // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler)? @@ -27,271 +27,329 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase", - binaryMessenger: registrar.messenger()) + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase", + binaryMessenger: registrar.messenger()) let instance = InAppPurchasePlugin(registrar: registrar) registrar.addMethodCallDelegate(instance, channel: channel) registrar.addApplicationDelegate(instance) - SetUpInAppPurchaseAPI(registrar.messenger(), instance); + SetUpInAppPurchaseAPI(registrar.messenger(), instance) } public init(receiptManager: FIAPReceiptManager) { self.receiptManager = receiptManager - self.requestHandlers = Set(); + self.requestHandlers = Set() self.productsCache = NSMutableDictionary() self.handlerFactory = { request in - return FIAPRequestHandler(request: request) + return FIAPRequestHandler(request: request) } super.init() } - public convenience init(receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler) { + public convenience init( + receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler + ) { self.init(receiptManager: receiptManager) - self.handlerFactory = handlerFactory; + self.handlerFactory = handlerFactory } public convenience init(registrar: FlutterPluginRegistrar) { - self.init(receiptManager: FIAPReceiptManager()); - self.registrar = registrar; + self.init(receiptManager: FIAPReceiptManager()) + self.registrar = registrar weak var weakSelf = self - self.paymentQueueHandler = FIAPaymentQueueHandler(queue: SKPaymentQueue.default(), transactionsUpdated: { (transactions: [SKPaymentTransaction]) -> () in - weakSelf!.handleTransactionsUpdated(transactions) - }, transactionRemoved: { (transactions: [SKPaymentTransaction]) -> () in - weakSelf!.handleTransactionsRemoved(transactions) - }, restoreTransactionFailed: { error in - weakSelf!.handleTransactionRestoreFailed(error as NSError) - }, restoreCompletedTransactionsFinished: { () -> () in - weakSelf!.restoreCompletedTransactionsFinished() - }, shouldAddStorePayment: { (payment: SKPayment, product: SKProduct) -> Bool in - return weakSelf!.shouldAddStorePayment(payment: payment, product: product) - }, updatedDownloads: {_ in - weakSelf!.updatedDownloads() - }, transactionCache: FIATransactionCache()) - - transactionObserverCallbackChannel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase", binaryMessenger: registrar.messenger()) + self.paymentQueueHandler = FIAPaymentQueueHandler( + queue: SKPaymentQueue.default(), + transactionsUpdated: { (transactions: [SKPaymentTransaction]) -> Void in + weakSelf!.handleTransactionsUpdated(transactions) + }, + transactionRemoved: { (transactions: [SKPaymentTransaction]) -> Void in + weakSelf!.handleTransactionsRemoved(transactions) + }, + restoreTransactionFailed: { error in + weakSelf!.handleTransactionRestoreFailed(error as NSError) + }, + restoreCompletedTransactionsFinished: { () -> Void in + weakSelf!.restoreCompletedTransactionsFinished() + }, + shouldAddStorePayment: { (payment: SKPayment, product: SKProduct) -> Bool in + return weakSelf!.shouldAddStorePayment(payment: payment, product: product) + }, + updatedDownloads: { _ in + weakSelf!.updatedDownloads() + }, transactionCache: FIATransactionCache()) + + transactionObserverCallbackChannel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase", binaryMessenger: registrar.messenger()) } - - public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> NSNumber? { - return SKPaymentQueue.canMakePayments() as NSNumber; + public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> NSNumber? + { + return SKPaymentQueue.canMakePayments() as NSNumber } - public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> [SKPaymentTransactionMessage]? { - let transactions = self.paymentQueueHandler?.getUnfinishedTransactions() ?? []; - var transactionMaps: [SKPaymentTransactionMessage] = [] - for transaction in transactions { - if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { - transactionMaps.append(map); - } + public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> [SKPaymentTransactionMessage]? + { + let transactions = self.paymentQueueHandler?.getUnfinishedTransactions() ?? [] + var transactionMaps: [SKPaymentTransactionMessage] = [] + for transaction in transactions { + if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { + transactionMaps.append(map) } - return transactionMaps } + return transactionMaps + } - public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) -> SKStorefrontMessage? { - if #available(iOS 13.0, *) { - let storefront = self.paymentQueueHandler?.storefront - if (storefront == nil) { - return nil; - } - return FIAObjectTranslator.convertStorefront(toPigeon: storefront); + public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> SKStorefrontMessage? + { + if #available(iOS 13.0, *) { + let storefront = self.paymentQueueHandler?.storefront + if storefront == nil { + return nil } - return nil; + return FIAObjectTranslator.convertStorefront(toPigeon: storefront) } + return nil + } - public func startProductRequestProductIdentifiers(_ productIdentifiers: [String], completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void) { + public func startProductRequestProductIdentifiers( + _ productIdentifiers: [String], + completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void + ) { let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) - let handler = handlerFactory!(request) - requestHandlers.insert(handler) - - handler.startProductRequest { response, startProductRequestError in - var error: FlutterError; - if let startProductRequestError = startProductRequestError { - error = FlutterError(code: "storekit_getproductrequest_platform_error", - message: startProductRequestError.localizedDescription, - details: startProductRequestError.localizedDescription) - completion(nil, error); - return - } - - guard let response = response else { - error = FlutterError(code: "storekit_platform_no_response", - message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", - details: productIdentifiers) - completion(nil, error); - return - } - - response.products.forEach { product in - self.productsCache[product.productIdentifier] = product - } - - if #available(iOS 12.2, *) { - if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response){ - completion(responseMessage, nil); - } - } - self.requestHandlers.remove(handler) - } - } + let handler = handlerFactory!(request) + requestHandlers.insert(handler) + handler.startProductRequest { response, startProductRequestError in + var error: FlutterError + if let startProductRequestError = startProductRequestError { + error = FlutterError( + code: "storekit_getproductrequest_platform_error", + message: startProductRequestError.localizedDescription, + details: startProductRequestError.localizedDescription) + completion(nil, error) + return + } - public func addPaymentPaymentMap(_ paymentMap: [String : Any], error: AutoreleasingUnsafeMutablePointer) { - guard let productID = paymentMap["productIdentifier"] as? String else { - error.pointee = FlutterError(code: "storekit_missing_product_identifier", - message: "The `productIdentifier` is missing from the payment map.", - details: paymentMap) - return; - } + guard let response = response else { + error = FlutterError( + code: "storekit_platform_no_response", + message: + "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", + details: productIdentifiers) + completion(nil, error) + return + } - guard let product = self.getProduct(productID: productID) else { - error.pointee = FlutterError(code: "storekit_invalid_payment_object", - message: "You have requested a payment for an invalid product. Either the `productIdentifier` of the payment is not valid or the product has not been fetched before adding the payment to the payment queue.", - details: paymentMap) - return; - } + response.products.forEach { product in + self.productsCache[product.productIdentifier] = product + } - let payment = SKMutablePayment(product: product) - payment.applicationUsername = paymentMap["applicationUsername"] as? String - payment.quantity = paymentMap["quantity"] as? Int ?? 1 - payment.simulatesAskToBuyInSandbox = paymentMap["simulatesAskToBuyInSandbox"] as? Bool ?? false - - if #available(iOS 12.2, *) { - if let paymentDiscountMap = paymentMap["paymentDiscount"] as? [String: Any], !paymentDiscountMap.isEmpty { - var invalidError: NSString? - if let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount(fromMap: paymentDiscountMap, withError: &invalidError) { - payment.paymentDiscount = paymentDiscount - } else if let invalidError = invalidError { - error.pointee = FlutterError(code: "storekit_invalid_payment_discount_object", - message: "You have requested a payment and specified a payment discount with invalid properties. \(invalidError)", - details: paymentMap) - return; - } - } + if #available(iOS 12.2, *) { + if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response) { + completion(responseMessage, nil) } - - guard self.paymentQueueHandler?.add(payment) == true else { - error.pointee = FlutterError(code: "storekit_duplicate_product_object", - message: "There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using `completePurchase` to avoid edge cases.", - details: paymentMap) - return; - }; + } + self.requestHandlers.remove(handler) + } } - public func finishTransactionFinishMap(_ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer) { - guard let transactionIdentifier = finishMap["transactionIdentifier"] as? String, - let productIdentifier = finishMap["productIdentifier"] as? String else { + public func addPaymentPaymentMap( + _ paymentMap: [String: Any], error: AutoreleasingUnsafeMutablePointer + ) { + guard let productID = paymentMap["productIdentifier"] as? String else { + error.pointee = FlutterError( + code: "storekit_missing_product_identifier", + message: "The `productIdentifier` is missing from the payment map.", + details: paymentMap) + return + } + + guard let product = self.getProduct(productID: productID) else { + error.pointee = FlutterError( + code: "storekit_invalid_payment_object", + message: + "You have requested a payment for an invalid product. Either the `productIdentifier` of the payment is not valid or the product has not been fetched before adding the payment to the payment queue.", + details: paymentMap) + return + } + + let payment = SKMutablePayment(product: product) + payment.applicationUsername = paymentMap["applicationUsername"] as? String + payment.quantity = paymentMap["quantity"] as? Int ?? 1 + payment.simulatesAskToBuyInSandbox = paymentMap["simulatesAskToBuyInSandbox"] as? Bool ?? false + + if #available(iOS 12.2, *) { + if let paymentDiscountMap = paymentMap["paymentDiscount"] as? [String: Any], + !paymentDiscountMap.isEmpty + { + var invalidError: NSString? + if let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: paymentDiscountMap, withError: &invalidError) + { + payment.paymentDiscount = paymentDiscount + } else if let invalidError = invalidError { + error.pointee = FlutterError( + code: "storekit_invalid_payment_discount_object", + message: + "You have requested a payment and specified a payment discount with invalid properties. \(invalidError)", + details: paymentMap) return + } } + } + + guard self.paymentQueueHandler?.add(payment) == true else { + error.pointee = FlutterError( + code: "storekit_duplicate_product_object", + message: + "There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using `completePurchase` to avoid edge cases.", + details: paymentMap) + return + } + } + + public func finishTransactionFinishMap( + _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer + ) { + guard let transactionIdentifier = finishMap["transactionIdentifier"] as? String, + let productIdentifier = finishMap["productIdentifier"] as? String + else { + return + } let pendingTransactions = paymentQueueHandler!.getUnfinishedTransactions() - for transaction in pendingTransactions { - // Check if the current transaction's identifier matches the provided one, - // or both identifiers are nil and the product identifiers match. - if transaction.transactionIdentifier == transactionIdentifier || - (transactionIdentifier == NSNull().description && - transaction.transactionIdentifier == nil && - transaction.payment.productIdentifier == productIdentifier) { - paymentQueueHandler!.finish(transaction); - } + for transaction in pendingTransactions { + // Check if the current transaction's identifier matches the provided one, + // or both identifiers are nil and the product identifiers match. + if transaction.transactionIdentifier == transactionIdentifier + || (transactionIdentifier == NSNull().description + && transaction.transactionIdentifier == nil + && transaction.payment.productIdentifier == productIdentifier) + { + paymentQueueHandler!.finish(transaction) } + } } - public func restoreTransactionsApplicationUserName(_ applicationUserName: String?, error: AutoreleasingUnsafeMutablePointer) { - paymentQueueHandler?.restoreTransactions(applicationUserName); + public func restoreTransactionsApplicationUserName( + _ applicationUserName: String?, error: AutoreleasingUnsafeMutablePointer + ) { + paymentQueueHandler?.restoreTransactions(applicationUserName) } - public func presentCodeRedemptionSheetWithError(_ error: AutoreleasingUnsafeMutablePointer) { -#if os(iOS) - paymentQueueHandler!.presentCodeRedemptionSheet() -#endif + public func presentCodeRedemptionSheetWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + paymentQueueHandler!.presentCodeRedemptionSheet() + #endif } - public func retrieveReceiptDataWithError(_ error: AutoreleasingUnsafeMutablePointer) -> String? { - var flutterError : FlutterError? = nil; - let receiptData : String? = receiptManager!.retrieveReceiptWithError(&flutterError); - if (receiptData == nil) { - error.pointee = flutterError; - return nil; + public func retrieveReceiptDataWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) -> String? { + var flutterError: FlutterError? = nil + let receiptData: String? = receiptManager!.retrieveReceiptWithError(&flutterError) + if receiptData == nil { + error.pointee = flutterError + return nil } - return receiptData; + return receiptData } - public func refreshReceiptReceiptProperties(_ receiptProperties: [String : Any]?, completion: @escaping (FlutterError?) -> Void) { + public func refreshReceiptReceiptProperties( + _ receiptProperties: [String: Any]?, completion: @escaping (FlutterError?) -> Void + ) { var request: SKReceiptRefreshRequest if let receiptProperties = receiptProperties { - // If receiptProperties is not nil, this call is for testing. - var properties: [String: Any] = [:] + // If receiptProperties is not nil, this call is for testing. + var properties: [String: Any] = [:] properties[SKReceiptPropertyIsExpired] = receiptProperties["isExpired"]! properties[SKReceiptPropertyIsRevoked] = receiptProperties["isRevoked"]! properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties["isVolumePurchase"]! - request = getRefreshReceiptRequest(properties: properties); + request = getRefreshReceiptRequest(properties: properties) } else { - request = getRefreshReceiptRequest(properties: nil); + request = getRefreshReceiptRequest(properties: nil) } let handler = handlerFactory!(request) requestHandlers.insert(handler) handler.startProductRequest { [weak self] response, error in - if let error = error { - let requestError = FlutterError(code: "storekit_refreshreceiptrequest_platform_error", - message: error.localizedDescription, - details: error.localizedDescription); - completion(requestError); - return - } - completion(nil); - self?.requestHandlers.remove(handler); + if let error = error { + let requestError = FlutterError( + code: "storekit_refreshreceiptrequest_platform_error", + message: error.localizedDescription, + details: error.localizedDescription) + completion(requestError) + return + } + completion(nil) + self?.requestHandlers.remove(handler) } } - - public func startObservingPaymentQueueWithError(_ error: AutoreleasingUnsafeMutablePointer) { - paymentQueueHandler!.startObservingPaymentQueue(); + public func startObservingPaymentQueueWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + paymentQueueHandler!.startObservingPaymentQueue() } - public func stopObservingPaymentQueueWithError(_ error: AutoreleasingUnsafeMutablePointer) { - paymentQueueHandler!.stopObservingPaymentQueue(); + public func stopObservingPaymentQueueWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + paymentQueueHandler!.stopObservingPaymentQueue() } - public func registerPaymentQueueDelegateWithError(_ error: AutoreleasingUnsafeMutablePointer) { -#if os(iOS) - if #available(iOS 13.0, *) { - let messenger = registrar?.messenger(); - paymentQueueDelegateCallbackChannel = FlutterMethodChannel(name: "plugins.flutter.io/in_app_purchase_payment_queue_delegate", - binaryMessenger: messenger!) - - paymentQueueDelegate = FIAPPaymentQueueDelegate(methodChannel: paymentQueueDelegateCallbackChannel!) - paymentQueueHandler!.delegate = (paymentQueueDelegate as! any SKPaymentQueueDelegate); - } -#endif + public func registerPaymentQueueDelegateWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.0, *) { + let messenger = registrar?.messenger() + paymentQueueDelegateCallbackChannel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase_payment_queue_delegate", + binaryMessenger: messenger!) + + paymentQueueDelegate = FIAPPaymentQueueDelegate( + methodChannel: paymentQueueDelegateCallbackChannel!) + paymentQueueHandler!.delegate = (paymentQueueDelegate as! any SKPaymentQueueDelegate) + } + #endif } - public func removePaymentQueueDelegateWithError(_ error: AutoreleasingUnsafeMutablePointer) { -#if os(iOS) - if #available(iOS 13.0, *) { - paymentQueueDelegateCallbackChannel = nil; - paymentQueueHandler!.delegate = nil; - paymentQueueDelegate = nil; - } -#endif + public func removePaymentQueueDelegateWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.0, *) { + paymentQueueDelegateCallbackChannel = nil + paymentQueueHandler!.delegate = nil + paymentQueueDelegate = nil + } + #endif } - public func showPriceConsentIfNeededWithError(_ error: AutoreleasingUnsafeMutablePointer) { -#if os(iOS) - if #available(iOS 13.4, *) { - paymentQueueHandler!.showPriceConsentIfNeeded(); - } -#endif + public func showPriceConsentIfNeededWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.4, *) { + paymentQueueHandler!.showPriceConsentIfNeeded() + } + #endif } public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { var maps: [[AnyHashable: Any]] = [] for transaction in transactions { - let map = FIAObjectTranslator.getMapFrom(transaction); - maps.append(map); + let map = FIAObjectTranslator.getMapFrom(transaction) + maps.append(map) } transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) } @@ -299,52 +357,56 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { var maps: [[AnyHashable: Any]] = [] for transaction in transactions { - let map = FIAObjectTranslator.getMapFrom(transaction); - maps.append(map); + let map = FIAObjectTranslator.getMapFrom(transaction) + maps.append(map) } transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) } public func handleTransactionRestoreFailed(_ error: NSError) { - transactionObserverCallbackChannel!.invokeMethod("restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)); + transactionObserverCallbackChannel!.invokeMethod( + "restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)) } public func restoreCompletedTransactionsFinished() { - transactionObserverCallbackChannel!.invokeMethod("paymentQueueRestoreCompletedTransactionsFinished", arguments:nil); + transactionObserverCallbackChannel!.invokeMethod( + "paymentQueueRestoreCompletedTransactionsFinished", arguments: nil) } public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { - productsCache[product.productIdentifier] = product; + productsCache[product.productIdentifier] = product transactionObserverCallbackChannel! - .invokeMethod("shouldAddStorePayment", arguments: [ - "payment": FIAObjectTranslator.getMapFrom(payment), - "product": FIAObjectTranslator.getMapFrom(product) - ]); - return false; + .invokeMethod( + "shouldAddStorePayment", + arguments: [ + "payment": FIAObjectTranslator.getMapFrom(payment), + "product": FIAObjectTranslator.getMapFrom(product), + ]) + return false } func updatedDownloads() { - NSLog("Received an updatedDownloads callback, but downloads are not supported."); + NSLog("Received an updatedDownloads callback, but downloads are not supported.") } public func canMakePayments() -> Bool { - return SKPaymentQueue.canMakePayments() as Bool; + return SKPaymentQueue.canMakePayments() as Bool } - func getProduct(productID : String) -> SKProduct? { - return self.productsCache[productID] as? SKProduct; + func getProduct(productID: String) -> SKProduct? { + return self.productsCache[productID] as? SKProduct } func getProductRequest(withIdentifiers productIdentifiers: Set) -> SKProductsRequest { - return SKProductsRequest(productIdentifiers: productIdentifiers); + return SKProductsRequest(productIdentifiers: productIdentifiers) } func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { - let value = dictionary[key] - return value is NSNull ? nil : value + let value = dictionary[key] + return value is NSNull ? nil : value } func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { - return SKReceiptRefreshRequest(receiptProperties: properties); + return SKReceiptRefreshRequest(receiptProperties: properties) } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift index f24f26e70853..b61b83b229c0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift @@ -3,22 +3,24 @@ // found in the LICENSE file. import Foundation +import StoreKitTest @testable import in_app_purchase_storekit -import StoreKitTest -class InAppPurchasePluginStub : InAppPurchasePlugin { - override func getProductRequest(withIdentifiers productIdentifiers: Set) -> SKProductsRequest { - return SKProductRequestStub.init(productIdentifiers: productIdentifiers); +class InAppPurchasePluginStub: InAppPurchasePlugin { + override func getProductRequest(withIdentifiers productIdentifiers: Set) + -> SKProductsRequest + { + return SKProductRequestStub.init(productIdentifiers: productIdentifiers) } override func getProduct(productID: String) -> SKProduct? { - if (productID == "") { - return nil; + if productID == "" { + return nil } - return SKProductStub.init(productID: productID); + return SKProductStub.init(productID: productID) } - override func getRefreshReceiptRequest(properties: [String : Any]?) -> SKReceiptRefreshRequest { - return SKReceiptRefreshRequest(receiptProperties: properties); + override func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) } } 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 c1dd932d10f3..082d62939d90 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 @@ -187,7 +187,8 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override void finishTransaction(Map finishMap) { finishedTransactions.add(createPurchasedTransaction( - finishMap['productIdentifier']! as String, finishMap['transactionIdentifier']! as String, + finishMap['productIdentifier']! as String, + finishMap['transactionIdentifier']! as String, quantity: transactionList.first.payment.quantity)); } From c3d956d6d3c3bc1e0a0947d6380093caa01faa85 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 24 Apr 2024 11:31:13 -0700 Subject: [PATCH 06/29] fix warnings --- .../RunnerTests/InAppPurchasePluginTests.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 71da8fe075f9..fa65b3f31433 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -184,7 +184,7 @@ - (void)testGetProductResponseWithRequestError { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -219,7 +219,7 @@ - (void)testGetProductResponseWithNoResponse { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -509,7 +509,7 @@ - (void)testRefreshReceiptRequestWithError { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -669,7 +669,7 @@ - (void)testHandleTransactionsUpdated { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -695,7 +695,7 @@ - (void)testHandleTransactionsRemoved { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -711,7 +711,7 @@ - (void)testHandleTransactionsRemoved { } - (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -723,7 +723,7 @@ - (void)testHandleTransactionRestoreFailed { } - (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -753,7 +753,7 @@ - (void)testShouldAddStorePayment { SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); From c8fd1ba8944e0650639b16682a4510901f775bba Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 24 Apr 2024 15:51:19 -0700 Subject: [PATCH 07/29] podspec --- .../darwin/Classes/InAppPurchasePlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 4a6cfdf9c8f8..55622a27b28b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -289,7 +289,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { completion(requestError) return } - completion(nil) + completion(nil); self?.requestHandlers.remove(handler) } } From 3d1a1ab67f97254ce5a7d5459c920e4173ddceae Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 24 Apr 2024 15:52:28 -0700 Subject: [PATCH 08/29] podspec --- .../darwin/in_app_purchase_storekit.podspec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec index 892c3840caa8..fed7865a6158 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec @@ -25,4 +25,8 @@ Downloaded by pub (not CocoaPods). s.osx.deployment_target = '10.15' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.resource_bundles = {'in_app_purchase_storekit_privacy' => ['Resources/PrivacyInfo.xcprivacy']} + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } end From 2d09254e03cc81196a6756ed4d6cc2c43d3e2d57 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 24 Apr 2024 16:48:15 -0700 Subject: [PATCH 09/29] fix macos not building? --- .../darwin/Classes/InAppPurchasePlugin.swift | 18 +++++++++++++++--- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 55622a27b28b..513b91351f4b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -27,13 +27,19 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { +#if os(iOS) + let messenger = registrar.messenger(); +#endif +#if os(macOS) + let messenger = registrar.messenger; +#endif let channel = FlutterMethodChannel( name: "plugins.flutter.io/in_app_purchase", - binaryMessenger: registrar.messenger()) + binaryMessenger: messenger) let instance = InAppPurchasePlugin(registrar: registrar) registrar.addMethodCallDelegate(instance, channel: channel) registrar.addApplicationDelegate(instance) - SetUpInAppPurchaseAPI(registrar.messenger(), instance) + SetUpInAppPurchaseAPI(messenger, instance) } public init(receiptManager: FIAPReceiptManager) { @@ -80,8 +86,14 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { weakSelf!.updatedDownloads() }, transactionCache: FIATransactionCache()) +#if os(iOS) + let messenger = registrar.messenger(); +#endif +#if os(macOS) + let messenger = registrar.messenger; +#endif transactionObserverCallbackChannel = FlutterMethodChannel( - name: "plugins.flutter.io/in_app_purchase", binaryMessenger: registrar.messenger()) + name: "plugins.flutter.io/in_app_purchase", binaryMessenger: messenger) } public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 761737047418..36bbffc5c3e2 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -279,7 +279,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1400; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index dd0635273098..53f3a37403a8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Wed, 24 Apr 2024 16:55:39 -0700 Subject: [PATCH 10/29] add stubs for mac --- .../RunnerTests/RunnerTests-Bridging-Header.h | 1 - .../macos/Runner.xcodeproj/project.pbxproj | 15 ++++++++++- .../RunnerTests/RunnerTests-Bridging-Header.h | 6 +++++ .../example/macos/RunnerTests/Stubs.swift | 26 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h index 25e1271864bc..e49302b1225f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -4,4 +4,3 @@ // #import "Stubs.h" -#import "Stubs.m" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 36bbffc5c3e2..7b5059e5278d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; A2C6CD5797E6A6721FDBCA1C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DEEA66738F64D983F76848 /* Pods_Runner.framework */; }; C51E64432925727D7AC7BBFF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */; }; + F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; F79BDC102905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC0F2905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m */; }; F79BDC122905FBF700E3999D /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */; }; F79BDC142905FBFE00E3999D /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC132905FBFE00E3999D /* InAppPurchasePluginTests.m */; }; @@ -91,6 +92,8 @@ 9A4FEABF1DEF0D106FEB7974 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; B6C8FD76BB3278AA51FED870 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + F2C3A7402BD9D33D000D35F2 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F79BDC0F2905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIAPPaymentQueueDeleteTests.m; path = ../../shared/RunnerTests/FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIATransactionCacheTests.m; path = ../../shared/RunnerTests/FIATransactionCacheTests.m; sourceTree = ""; }; @@ -223,6 +226,8 @@ F79BDC1D2905FC3900E3999D /* TranslatorTests.m */, F79BDC192905FC1F00E3999D /* ProductRequestHandlerTests.m */, F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */, + F2C3A7402BD9D33D000D35F2 /* Stubs.swift */, + F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; sourceTree = ""; @@ -298,7 +303,7 @@ }; F700DD0128E652A10004836B = { CreatedOnToolsVersion = 14.0.1; - LastSwiftMigration = 1400; + LastSwiftMigration = 1530; TestTargetID = 33CC10EC2044A3C60003C045; }; }; @@ -483,6 +488,7 @@ F79BDC102905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m in Sources */, F79BDC142905FBFE00E3999D /* InAppPurchasePluginTests.m in Sources */, F79BDC122905FBF700E3999D /* FIATransactionCacheTests.m in Sources */, + F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -771,6 +777,9 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Debug; @@ -801,6 +810,8 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Release; @@ -831,6 +842,8 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Profile; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h new file mode 100644 index 000000000000..e49302b1225f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h @@ -0,0 +1,6 @@ +// +// Use this file to import your target's public headers that you would like to +// expose to Swift. +// + +#import "Stubs.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift new file mode 100644 index 000000000000..b61b83b229c0 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift @@ -0,0 +1,26 @@ +// 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 Foundation +import StoreKitTest + +@testable import in_app_purchase_storekit + +class InAppPurchasePluginStub: InAppPurchasePlugin { + override func getProductRequest(withIdentifiers productIdentifiers: Set) + -> SKProductsRequest + { + return SKProductRequestStub.init(productIdentifiers: productIdentifiers) + } + + override func getProduct(productID: String) -> SKProduct? { + if productID == "" { + return nil + } + return SKProductStub.init(productID: productID) + } + override func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } +} From 6c94568f1008010024b999d4112d1dc8927ac818 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 25 Apr 2024 15:25:58 -0700 Subject: [PATCH 11/29] fix macos tests not launcing --- .../macos/Runner.xcodeproj/project.pbxproj | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 7b5059e5278d..17ff9ab1945e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,8 +26,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - A2C6CD5797E6A6721FDBCA1C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DEEA66738F64D983F76848 /* Pods_Runner.framework */; }; - C51E64432925727D7AC7BBFF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */; }; + 45146735C2BCBA6C4526CAA0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */; }; + 4B50788839EDAFEFFC4A752E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */; }; F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; F79BDC102905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC0F2905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m */; }; F79BDC122905FBF700E3999D /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */; }; @@ -69,6 +69,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -82,16 +83,15 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 36DEEA66738F64D983F76848 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 62F1680C5AE033907C1DF7AB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7172FBE7DF73E41E9FC6E6D7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 8D6AA81A407E58E8954F145B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - 9A4FEABF1DEF0D106FEB7974 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - B6C8FD76BB3278AA51FED870 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + DAB7E6DD38EB6FA87E605270 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; F2C3A7402BD9D33D000D35F2 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -111,7 +111,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A2C6CD5797E6A6721FDBCA1C /* Pods_Runner.framework in Frameworks */, + 45146735C2BCBA6C4526CAA0 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -119,7 +119,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C51E64432925727D7AC7BBFF /* Pods_RunnerTests.framework in Frameworks */, + 4B50788839EDAFEFFC4A752E /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -129,12 +129,12 @@ 09D47623A8E19B84FF0453EE /* Pods */ = { isa = PBXGroup; children = ( - B6C8FD76BB3278AA51FED870 /* Pods-Runner.debug.xcconfig */, - 9A4FEABF1DEF0D106FEB7974 /* Pods-Runner.release.xcconfig */, - 62F1680C5AE033907C1DF7AB /* Pods-Runner.profile.xcconfig */, - 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */, - 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */, - 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */, + 8D6AA81A407E58E8954F145B /* Pods-Runner.debug.xcconfig */, + 7172FBE7DF73E41E9FC6E6D7 /* Pods-Runner.release.xcconfig */, + DAB7E6DD38EB6FA87E605270 /* Pods-Runner.profile.xcconfig */, + 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */, + 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */, + BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -156,8 +156,8 @@ 33CEB47122A05771004F2AC0 /* Flutter */, F700DD0328E652A10004836B /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, 09D47623A8E19B84FF0453EE /* Pods */, + E89234C8EA67B35E702D54F6 /* Frameworks */, ); sourceTree = ""; }; @@ -205,11 +205,11 @@ path = Runner; sourceTree = ""; }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { + E89234C8EA67B35E702D54F6 /* Frameworks */ = { isa = PBXGroup; children = ( - 36DEEA66738F64D983F76848 /* Pods_Runner.framework */, - EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */, + 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */, + 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -239,13 +239,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 377E3E3C5CA24E98C4B6A4BB /* [CP] Check Pods Manifest.lock */, + F83C62E1BF4D0A86747FA7CF /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 23A80E9A6DAA80757416464A /* [CP] Embed Pods Frameworks */, + 2C0BDBFDB384E45F10121440 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -261,11 +261,11 @@ isa = PBXNativeTarget; buildConfigurationList = F700DD0B28E652A10004836B /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 959FA4942EA5DA018C52D3DA /* [CP] Check Pods Manifest.lock */, + 75DE29BFD3B3C1D676C22160 /* [CP] Check Pods Manifest.lock */, F700DCFE28E652A10004836B /* Sources */, F700DCFF28E652A10004836B /* Frameworks */, F700DD0028E652A10004836B /* Resources */, - 1FAA0D39365CA43DED71E657 /* [CP] Embed Pods Frameworks */, + E318947BE753B7BBEFC3782A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -348,24 +348,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1FAA0D39365CA43DED71E657 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 23A80E9A6DAA80757416464A /* [CP] Embed Pods Frameworks */ = { + 2C0BDBFDB384E45F10121440 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -420,7 +403,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 377E3E3C5CA24E98C4B6A4BB /* [CP] Check Pods Manifest.lock */ = { + 75DE29BFD3B3C1D676C22160 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -435,14 +418,31 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 959FA4942EA5DA018C52D3DA /* [CP] Check Pods Manifest.lock */ = { + E318947BE753B7BBEFC3782A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F83C62E1BF4D0A86747FA7CF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -457,7 +457,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -752,7 +752,7 @@ }; F700DD0828E652A10004836B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -786,7 +786,7 @@ }; F700DD0928E652A10004836B /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -818,7 +818,7 @@ }; F700DD0A28E652A10004836B /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; From 1c7d79b75f82281ce0c96a55f4ffbd95a7982890 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 30 Apr 2024 15:45:42 -0700 Subject: [PATCH 12/29] remove OTHER_SWIFT_FLAGS (macos tests) --- .../example/macos/Runner.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 17ff9ab1945e..c990b4a8d850 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -773,7 +773,6 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; From 7d144c0449a6199a71b5c5f9e782497941193d5a Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 1 May 2024 11:12:53 -0700 Subject: [PATCH 13/29] fix refreshReceipt test to not trigger authentication --- .../RunnerTests/InAppPurchasePluginTests.m | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index fa65b3f31433..20454a0d469a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -475,7 +475,24 @@ - (void)testRetrieveReceiptDataError { - (void)testRefreshReceiptRequest { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:nil + + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:_receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:nil completion:^(FlutterError *_Nullable error) { [expectation fulfill]; }]; @@ -491,10 +508,28 @@ - (void)testRefreshReceiptRequestWithParams { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:properties + + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:_receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:properties completion:^(FlutterError *_Nullable error) { [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; } @@ -632,7 +667,6 @@ - (void)testRegisterPaymentQueueDelegate { XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); } } -#endif - (void)testRemovePaymentQueueDelegate { if (@available(iOS 13, *)) { @@ -657,6 +691,7 @@ - (void)testRemovePaymentQueueDelegate { XCTAssertNil(self.plugin.paymentQueueHandler.delegate); } } +#endif - (void)testHandleTransactionsUpdated { NSDictionary *transactionMap = @{ From 941952d819db63383e916f2285156f6648460ba8 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 1 May 2024 11:19:30 -0700 Subject: [PATCH 14/29] remove the worst swift flag known to man --- .../example/macos/Runner.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index c990b4a8d850..e4eb1fae6005 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -805,7 +805,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -837,7 +836,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; From be4e742e7e346e6facfa991a51b074b74d5a9e99 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 2 May 2024 14:23:27 -0700 Subject: [PATCH 15/29] pr comments, enforce access lvls, rewrite some tests, copyright, format --- .../darwin/Classes/InAppPurchasePlugin.swift | 119 ++++++++---------- ...in_app_purchase_storekit-Bridging-Header.h | 7 +- .../darwin/Classes/in_app_purchase_storekit.h | 5 + .../RunnerTests/RunnerTests-Bridging-Header.h | 7 +- .../RunnerTests/RunnerTests-Bridging-Header.h | 7 +- .../RunnerTests/InAppPurchasePluginTests.m | 29 +++-- 6 files changed, 85 insertions(+), 89 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 513b91351f4b..812d86eb9891 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -11,28 +11,27 @@ import StoreKit import FlutterMacOS #endif -extension FlutterError: Error {} @objcMembers public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // Properties - private(set) var productsCache: NSMutableDictionary = [:] - private(set) var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? - private(set) var receiptManager: FIAPReceiptManager? - private(set) var paymentQueueDelegate: Any? = nil + private let receiptManager: FIAPReceiptManager + private var productsCache: NSMutableDictionary = [:] + private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? + private var paymentQueueDelegate: Any? = nil // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, private var requestHandlers = Set() - private var handlerFactory: ((SKRequest) -> FIAPRequestHandler)? + private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) public var registrar: FlutterPluginRegistrar? public var paymentQueueHandler: FIAPaymentQueueHandler? public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { -#if os(iOS) - let messenger = registrar.messenger(); -#endif -#if os(macOS) - let messenger = registrar.messenger; -#endif + #if os(iOS) + let messenger = registrar.messenger() + #endif + #if os(macOS) + let messenger = registrar.messenger + #endif let channel = FlutterMethodChannel( name: "plugins.flutter.io/in_app_purchase", binaryMessenger: messenger) @@ -53,7 +52,8 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } public convenience init( - receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler + receiptManager: FIAPReceiptManager, + handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler ) { self.init(receiptManager: receiptManager) self.handlerFactory = handlerFactory @@ -63,35 +63,32 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { self.init(receiptManager: FIAPReceiptManager()) self.registrar = registrar - weak var weakSelf = self - self.paymentQueueHandler = FIAPaymentQueueHandler( queue: SKPaymentQueue.default(), - transactionsUpdated: { (transactions: [SKPaymentTransaction]) -> Void in - weakSelf!.handleTransactionsUpdated(transactions) + transactionsUpdated: { [weak self] (transactions: [SKPaymentTransaction]) -> Void in + self?.handleTransactionsUpdated(transactions) }, - transactionRemoved: { (transactions: [SKPaymentTransaction]) -> Void in - weakSelf!.handleTransactionsRemoved(transactions) + transactionRemoved: { [weak self] (transactions: [SKPaymentTransaction]) -> Void in + self?.handleTransactionsRemoved(transactions) }, - restoreTransactionFailed: { error in - weakSelf!.handleTransactionRestoreFailed(error as NSError) + restoreTransactionFailed: { [weak self] error in + self?.handleTransactionRestoreFailed(error as NSError) }, - restoreCompletedTransactionsFinished: { () -> Void in - weakSelf!.restoreCompletedTransactionsFinished() + restoreCompletedTransactionsFinished: { [weak self] () -> Void in + self?.restoreCompletedTransactionsFinished() }, - shouldAddStorePayment: { (payment: SKPayment, product: SKProduct) -> Bool in - return weakSelf!.shouldAddStorePayment(payment: payment, product: product) + shouldAddStorePayment: { [weak self] (payment: SKPayment, product: SKProduct) -> Bool in + return self?.shouldAddStorePayment(payment: payment, product: product) ?? false }, - updatedDownloads: { _ in - weakSelf!.updatedDownloads() + updatedDownloads: { [weak self] _ in + self?.updatedDownloads() }, transactionCache: FIATransactionCache()) - -#if os(iOS) - let messenger = registrar.messenger(); -#endif -#if os(macOS) - let messenger = registrar.messenger; -#endif + #if os(iOS) + let messenger = registrar.messenger() + #endif + #if os(macOS) + let messenger = registrar.messenger + #endif transactionObserverCallbackChannel = FlutterMethodChannel( name: "plugins.flutter.io/in_app_purchase", binaryMessenger: messenger) } @@ -105,12 +102,10 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> [SKPaymentTransactionMessage]? { - let transactions = self.paymentQueueHandler?.getUnfinishedTransactions() ?? [] + let transactions = self.paymentQueueHandler!.getUnfinishedTransactions() var transactionMaps: [SKPaymentTransactionMessage] = [] - for transaction in transactions { - if let map = FIAObjectTranslator.convertTransaction(toPigeon: transaction) { - transactionMaps.append(map) - } + transactionMaps = transactions.compactMap { + FIAObjectTranslator.convertTransaction(toPigeon: $0) } return transactionMaps } @@ -120,7 +115,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { { if #available(iOS 13.0, *) { let storefront = self.paymentQueueHandler?.storefront - if storefront == nil { + guard let storefront else { return nil } return FIAObjectTranslator.convertStorefront(toPigeon: storefront) @@ -133,13 +128,12 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void ) { let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) - let handler = handlerFactory!(request) + let handler = handlerFactory(request) requestHandlers.insert(handler) - handler.startProductRequest { response, startProductRequestError in - var error: FlutterError + handler.startProductRequest { [weak self] response, startProductRequestError in if let startProductRequestError = startProductRequestError { - error = FlutterError( + let error = FlutterError( code: "storekit_getproductrequest_platform_error", message: startProductRequestError.localizedDescription, details: startProductRequestError.localizedDescription) @@ -148,7 +142,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } guard let response = response else { - error = FlutterError( + let error = FlutterError( code: "storekit_platform_no_response", message: "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", @@ -157,8 +151,8 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return } - response.products.forEach { product in - self.productsCache[product.productIdentifier] = product + for product in response.products { + self?.productsCache[product.productIdentifier] = product } if #available(iOS 12.2, *) { @@ -166,7 +160,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { completion(responseMessage, nil) } } - self.requestHandlers.remove(handler) + self?.requestHandlers.remove(handler) } } @@ -215,7 +209,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } } - guard self.paymentQueueHandler?.add(payment) == true else { + if !self.paymentQueueHandler!.add(payment) { error.pointee = FlutterError( code: "storekit_duplicate_product_object", message: @@ -267,7 +261,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { _ error: AutoreleasingUnsafeMutablePointer ) -> String? { var flutterError: FlutterError? = nil - let receiptData: String? = receiptManager!.retrieveReceiptWithError(&flutterError) + let receiptData: String? = receiptManager.retrieveReceiptWithError(&flutterError) if receiptData == nil { error.pointee = flutterError return nil @@ -281,16 +275,17 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { var request: SKReceiptRefreshRequest if let receiptProperties = receiptProperties { // If receiptProperties is not nil, this call is for testing. - var properties: [String: Any] = [:] - properties[SKReceiptPropertyIsExpired] = receiptProperties["isExpired"]! - properties[SKReceiptPropertyIsRevoked] = receiptProperties["isRevoked"]! - properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties["isVolumePurchase"]! + let properties = [ + SKReceiptPropertyIsExpired: receiptProperties["isExpired"]!, + SKReceiptPropertyIsRevoked: receiptProperties["isRevoked"]!, + SKReceiptPropertyIsVolumePurchase: receiptProperties["isVolumePurchase"]!, + ] request = getRefreshReceiptRequest(properties: properties) } else { request = getRefreshReceiptRequest(properties: nil) } - let handler = handlerFactory!(request) + let handler = handlerFactory(request) requestHandlers.insert(handler) handler.startProductRequest { [weak self] response, error in if let error = error { @@ -301,7 +296,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { completion(requestError) return } - completion(nil); + completion(nil) self?.requestHandlers.remove(handler) } } @@ -358,19 +353,15 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { - var maps: [[AnyHashable: Any]] = [] - for transaction in transactions { - let map = FIAObjectTranslator.getMapFrom(transaction) - maps.append(map) + let maps = transactions.map { transaction in + FIAObjectTranslator.getMapFrom(transaction) } transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) } public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { - var maps: [[AnyHashable: Any]] = [] - for transaction in transactions { - let map = FIAObjectTranslator.getMapFrom(transaction) - maps.append(map) + let maps = transactions.map { transaction in + FIAObjectTranslator.getMapFrom(transaction) } transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h index 3f756006bce6..f57780d9516f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h @@ -1,7 +1,6 @@ -// -// Use this file to import your target's public headers that you would like to -// expose to Swift. -// +// 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 "FIAObjectTranslator.h" #import "FIAPPaymentQueueDelegate.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h index 74d30325ae46..cf22ea3a6ee0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This has to be here b/c of this cocoapods issue - +// https://github.com/CocoaPods/CocoaPods/issues/3767 +// Without this file, the generated "in_app_purchase_storekit-Swift.h" will keep +// trying to import an "in_app_purchase_storekit.h" which doesn't exist. + #ifndef in_app_purchase_storekit_h #define in_app_purchase_storekit_h diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h index e49302b1225f..f85b226b93f6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -1,6 +1,5 @@ -// -// Use this file to import your target's public headers that you would like to -// expose to Swift. -// +// 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 "Stubs.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h index e49302b1225f..f85b226b93f6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/RunnerTests-Bridging-Header.h @@ -1,6 +1,5 @@ -// -// Use this file to import your target's public headers that you would like to -// expose to Swift. -// +// 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 "Stubs.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 20454a0d469a..5ed28529584c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -476,7 +476,6 @@ - (void)testRefreshReceiptRequest { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub @@ -493,9 +492,9 @@ - (void)testRefreshReceiptRequest { recieptError, nil])]); [plugin refreshReceiptReceiptProperties:nil - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5]; } @@ -509,7 +508,6 @@ - (void)testRefreshReceiptRequestWithParams { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub @@ -526,9 +524,9 @@ - (void)testRefreshReceiptRequestWithParams { recieptError, nil])]); [plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5]; } @@ -704,7 +702,8 @@ - (void)testHandleTransactionsUpdated { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = + [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -730,7 +729,8 @@ - (void)testHandleTransactionsRemoved { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = + [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -746,7 +746,8 @@ - (void)testHandleTransactionsRemoved { } - (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = + [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -758,7 +759,8 @@ - (void)testHandleTransactionRestoreFailed { } - (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = + [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -788,7 +790,8 @@ - (void)testShouldAddStorePayment { SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = + [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); From 6939e77dd5675912eb7caef4c7d0385aa868679b Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 2 May 2024 14:37:48 -0700 Subject: [PATCH 16/29] remove @objcMembers to make it a lil more granular --- .../darwin/Classes/InAppPurchasePlugin.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 812d86eb9891..1c642055f211 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -11,7 +11,6 @@ import StoreKit import FlutterMacOS #endif -@objcMembers public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // Properties private let receiptManager: FIAPReceiptManager @@ -21,8 +20,11 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) + @objc public var registrar: FlutterPluginRegistrar? + @objc public var paymentQueueHandler: FIAPaymentQueueHandler? + @objc public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { @@ -41,6 +43,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { SetUpInAppPurchaseAPI(messenger, instance) } + @objc public init(receiptManager: FIAPReceiptManager) { self.receiptManager = receiptManager self.requestHandlers = Set() @@ -51,6 +54,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { super.init() } + @objc public convenience init( receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler @@ -352,6 +356,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { #endif } + @objc public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { let maps = transactions.map { transaction in FIAObjectTranslator.getMapFrom(transaction) @@ -359,6 +364,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) } + @objc public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { let maps = transactions.map { transaction in FIAObjectTranslator.getMapFrom(transaction) @@ -366,16 +372,19 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) } + @objc public func handleTransactionRestoreFailed(_ error: NSError) { transactionObserverCallbackChannel!.invokeMethod( "restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)) } + @objc public func restoreCompletedTransactionsFinished() { transactionObserverCallbackChannel!.invokeMethod( "paymentQueueRestoreCompletedTransactionsFinished", arguments: nil) } + @objc public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { productsCache[product.productIdentifier] = product transactionObserverCallbackChannel! From 2fa16fcbd4934cf62bce3946e687429a1212f9ec Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 2 May 2024 16:29:06 -0700 Subject: [PATCH 17/29] fix weird null check --- .../darwin/Classes/InAppPurchasePlugin.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 1c642055f211..321f050db7cc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -226,8 +226,8 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func finishTransactionFinishMap( _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer ) { - guard let transactionIdentifier = finishMap["transactionIdentifier"] as? String, - let productIdentifier = finishMap["productIdentifier"] as? String + let transactionIdentifier = (finishMap["transactionIdentifier"] ?? "") as? String + guard let productIdentifier = finishMap["productIdentifier"] as? String else { return } @@ -235,10 +235,11 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { let pendingTransactions = paymentQueueHandler!.getUnfinishedTransactions() for transaction in pendingTransactions { - // Check if the current transaction's identifier matches the provided one, - // or both identifiers are nil and the product identifiers match. + // If the user cancels the purchase dialog we won't have a transactionIdentifier. + // So if it is null AND a transaction in the pendingTransactions list has + // also a null transactionIdentifier we check for equal product identifiers. if transaction.transactionIdentifier == transactionIdentifier - || (transactionIdentifier == NSNull().description + || (transactionIdentifier == "" && transaction.transactionIdentifier == nil && transaction.payment.productIdentifier == productIdentifier) { From 74de10af17274ab3d93a264b215ba19ec20e7ba6 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 2 May 2024 16:34:54 -0700 Subject: [PATCH 18/29] =?UTF-8?q?pubspec=20=F0=9F=92=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a2b005787b3f..b960bebd082c 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.15 +version: 0.3.16 environment: sdk: ^3.2.3 From b300519b60588d257abd6a655172d82e999875e3 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 7 May 2024 16:50:54 -0700 Subject: [PATCH 19/29] pr comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 101 +++++++++--------- .../darwin/Classes/in_app_purchase_storekit.h | 1 - .../darwin/Classes/messages.g.h | 2 +- .../darwin/Classes/messages.g.m | 2 +- 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 321f050db7cc..9346e89596c6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -26,6 +26,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public var paymentQueueHandler: FIAPaymentQueueHandler? @objc public var transactionObserverCallbackChannel: FlutterMethodChannel? + // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again public static func register(with registrar: FlutterPluginRegistrar) { #if os(iOS) @@ -47,14 +48,14 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public init(receiptManager: FIAPReceiptManager) { self.receiptManager = receiptManager self.requestHandlers = Set() - self.productsCache = NSMutableDictionary() - self.handlerFactory = { request in - return FIAPRequestHandler(request: request) + self.handlerFactory = { + FIAPRequestHandler(request: $0) } super.init() } @objc + // TODO(louisehsu): this initializer is for tests only. Make this private after the tests are moved to swift. public convenience init( receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler @@ -63,16 +64,17 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { self.handlerFactory = handlerFactory } + // This is the designated initializer public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()) self.registrar = registrar self.paymentQueueHandler = FIAPaymentQueueHandler( queue: SKPaymentQueue.default(), - transactionsUpdated: { [weak self] (transactions: [SKPaymentTransaction]) -> Void in + transactionsUpdated: { [weak self] transactions in self?.handleTransactionsUpdated(transactions) }, - transactionRemoved: { [weak self] (transactions: [SKPaymentTransaction]) -> Void in + transactionRemoved: { [weak self] transactions in self?.handleTransactionsRemoved(transactions) }, restoreTransactionFailed: { [weak self] error in @@ -86,7 +88,8 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { }, updatedDownloads: { [weak self] _ in self?.updatedDownloads() - }, transactionCache: FIATransactionCache()) + }, + transactionCache: FIATransactionCache()) #if os(iOS) let messenger = registrar.messenger() #endif @@ -106,22 +109,17 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> [SKPaymentTransactionMessage]? { - let transactions = self.paymentQueueHandler!.getUnfinishedTransactions() - var transactionMaps: [SKPaymentTransactionMessage] = [] - transactionMaps = transactions.compactMap { + + let transactions = getPaymentQueueHandler().getUnfinishedTransactions() + return transactions.compactMap { FIAObjectTranslator.convertTransaction(toPigeon: $0) } - return transactionMaps } public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) -> SKStorefrontMessage? { - if #available(iOS 13.0, *) { - let storefront = self.paymentQueueHandler?.storefront - guard let storefront else { - return nil - } + if #available(iOS 13.0, *), let storefront = getPaymentQueueHandler().storefront { return FIAObjectTranslator.convertStorefront(toPigeon: storefront) } return nil @@ -213,26 +211,25 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } } - if !self.paymentQueueHandler!.add(payment) { + if !getPaymentQueueHandler().add(payment) { error.pointee = FlutterError( code: "storekit_duplicate_product_object", message: "There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using `completePurchase` to avoid edge cases.", details: paymentMap) - return } } public func finishTransactionFinishMap( _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer ) { - let transactionIdentifier = (finishMap["transactionIdentifier"] ?? "") as? String - guard let productIdentifier = finishMap["productIdentifier"] as? String + guard let productIdentifier = finishMap["productIdentifier"] as? String, + let transactionIdentifier = (finishMap["transactionIdentifier"] ?? "") as? String else { return } - let pendingTransactions = paymentQueueHandler!.getUnfinishedTransactions() + let pendingTransactions = getPaymentQueueHandler().getUnfinishedTransactions() for transaction in pendingTransactions { // If the user cancels the purchase dialog we won't have a transactionIdentifier. @@ -243,7 +240,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { && transaction.transactionIdentifier == nil && transaction.payment.productIdentifier == productIdentifier) { - paymentQueueHandler!.finish(transaction) + getPaymentQueueHandler().finish(transaction) } } } @@ -251,14 +248,14 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func restoreTransactionsApplicationUserName( _ applicationUserName: String?, error: AutoreleasingUnsafeMutablePointer ) { - paymentQueueHandler?.restoreTransactions(applicationUserName) + getPaymentQueueHandler().restoreTransactions(applicationUserName) } public func presentCodeRedemptionSheetWithError( _ error: AutoreleasingUnsafeMutablePointer ) { #if os(iOS) - paymentQueueHandler!.presentCodeRedemptionSheet() + getPaymentQueueHandler().presentCodeRedemptionSheet() #endif } @@ -266,30 +263,19 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { _ error: AutoreleasingUnsafeMutablePointer ) -> String? { var flutterError: FlutterError? = nil - let receiptData: String? = receiptManager.retrieveReceiptWithError(&flutterError) - if receiptData == nil { + if let receiptData = receiptManager.retrieveReceiptWithError(&flutterError) { + return receiptData + } else { error.pointee = flutterError return nil } - return receiptData } public func refreshReceiptReceiptProperties( _ receiptProperties: [String: Any]?, completion: @escaping (FlutterError?) -> Void ) { - var request: SKReceiptRefreshRequest - if let receiptProperties = receiptProperties { - // If receiptProperties is not nil, this call is for testing. - let properties = [ - SKReceiptPropertyIsExpired: receiptProperties["isExpired"]!, - SKReceiptPropertyIsRevoked: receiptProperties["isRevoked"]!, - SKReceiptPropertyIsVolumePurchase: receiptProperties["isVolumePurchase"]!, - ] - request = getRefreshReceiptRequest(properties: properties) - } else { - request = getRefreshReceiptRequest(properties: nil) - } - + let properties = receiptProperties?.compactMapValues { $0 } ?? [:] + let request = getRefreshReceiptRequest(properties: properties.isEmpty ? nil : properties) let handler = handlerFactory(request) requestHandlers.insert(handler) handler.startProductRequest { [weak self] response, error in @@ -309,13 +295,13 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func startObservingPaymentQueueWithError( _ error: AutoreleasingUnsafeMutablePointer ) { - paymentQueueHandler!.startObservingPaymentQueue() + getPaymentQueueHandler().startObservingPaymentQueue() } public func stopObservingPaymentQueueWithError( _ error: AutoreleasingUnsafeMutablePointer ) { - paymentQueueHandler!.stopObservingPaymentQueue() + getPaymentQueueHandler().stopObservingPaymentQueue() } public func registerPaymentQueueDelegateWithError( @@ -330,7 +316,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { paymentQueueDelegate = FIAPPaymentQueueDelegate( methodChannel: paymentQueueDelegateCallbackChannel!) - paymentQueueHandler!.delegate = (paymentQueueDelegate as! any SKPaymentQueueDelegate) + getPaymentQueueHandler().delegate = (paymentQueueDelegate as! SKPaymentQueueDelegate) } #endif } @@ -341,7 +327,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { #if os(iOS) if #available(iOS 13.0, *) { paymentQueueDelegateCallbackChannel = nil - paymentQueueHandler!.delegate = nil + getPaymentQueueHandler().delegate = nil paymentQueueDelegate = nil } #endif @@ -352,23 +338,23 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { ) { #if os(iOS) if #available(iOS 13.4, *) { - paymentQueueHandler!.showPriceConsentIfNeeded() + getPaymentQueueHandler().showPriceConsentIfNeeded() } #endif } @objc public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { - let maps = transactions.map { transaction in - FIAObjectTranslator.getMapFrom(transaction) + let maps = transactions.map { + FIAObjectTranslator.getMapFrom($0) } transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) } @objc public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { - let maps = transactions.map { transaction in - FIAObjectTranslator.getMapFrom(transaction) + let maps = transactions.map { + FIAObjectTranslator.getMapFrom($0) } transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) } @@ -398,7 +384,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return false } - func updatedDownloads() { + public func updatedDownloads() { NSLog("Received an updatedDownloads callback, but downloads are not supported.") } @@ -406,6 +392,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return SKPaymentQueue.canMakePayments() as Bool } + // Methods to allow for testing func getProduct(productID: String) -> SKProduct? { return self.productsCache[productID] as? SKProduct } @@ -414,12 +401,22 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return SKProductsRequest(productIdentifiers: productIdentifiers) } - func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { + func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } + + // Private convenience methods + private func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { let value = dictionary[key] return value is NSNull ? nil : value } - func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { - return SKReceiptRefreshRequest(receiptProperties: properties) + private func getPaymentQueueHandler() -> FIAPaymentQueueHandler { + guard let paymentQueueHandler = self.paymentQueueHandler else { + fatalError( + "paymentQueueHandler can't be nil. Please ensure you're using init(registrar: FlutterPluginRegistrar)" + ) + } + return paymentQueueHandler } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h index cf22ea3a6ee0..7abed15966e8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h @@ -6,7 +6,6 @@ // https://github.com/CocoaPods/CocoaPods/issues/3767 // Without this file, the generated "in_app_purchase_storekit-Swift.h" will keep // trying to import an "in_app_purchase_storekit.h" which doesn't exist. - #ifndef in_app_purchase_storekit_h #define in_app_purchase_storekit_h diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 28daef7657c6..79bdffc76afe 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -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 (v16.0.5), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 8e76474c8bf7..f013f720511c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -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 (v16.0.5), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" From b96d5928e7d5f1ce84ab9a0f16ad616a6cfd1330 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 8 May 2024 10:43:37 -0700 Subject: [PATCH 20/29] comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 9346e89596c6..c0db0ff570c7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -12,21 +12,20 @@ import StoreKit #endif public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { - // Properties private let receiptManager: FIAPReceiptManager private var productsCache: NSMutableDictionary = [:] private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? - private var paymentQueueDelegate: Any? = nil // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, + private var paymentQueueDelegate: Any? = nil private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) + // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again @objc public var registrar: FlutterPluginRegistrar? @objc public var paymentQueueHandler: FIAPaymentQueueHandler? @objc public var transactionObserverCallbackChannel: FlutterMethodChannel? - // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again public static func register(with registrar: FlutterPluginRegistrar) { #if os(iOS) From ed4467f50ae8ee9635cb4dcc3133f986a4ea9dbe Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 8 May 2024 11:12:51 -0700 Subject: [PATCH 21/29] comment --- .../darwin/Classes/InAppPurchasePlugin.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index c0db0ff570c7..f20c0acb077a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -16,6 +16,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { private var productsCache: NSMutableDictionary = [:] private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, + // FIAPPaymentQueueDelegateonly gets set/used in registerPaymentQueueDelegateWithError or removePaymentQueueDelegateWithError, which both are ios13+ only private var paymentQueueDelegate: Any? = nil private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) From 59c5e82c1e99562a287346cadbd0f33e87c9e8d5 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 8 May 2024 17:02:33 -0700 Subject: [PATCH 22/29] some pr comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index f20c0acb077a..e9ae4a7d4159 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -17,10 +17,10 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, // FIAPPaymentQueueDelegateonly gets set/used in registerPaymentQueueDelegateWithError or removePaymentQueueDelegateWithError, which both are ios13+ only - private var paymentQueueDelegate: Any? = nil + private var paymentQueueDelegate: Any? private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) - // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again + // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again and remove all instances of @objc @objc public var registrar: FlutterPluginRegistrar? @objc @@ -45,26 +45,26 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } @objc - public init(receiptManager: FIAPReceiptManager) { - self.receiptManager = receiptManager - self.requestHandlers = Set() - self.handlerFactory = { + // This is the default init used by the other inits. Shouldn't be directly called. + public init( + receiptManager: FIAPReceiptManager, + handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler = { FIAPRequestHandler(request: $0) } + ) { + self.receiptManager = receiptManager + self.requestHandlers = Set() + self.handlerFactory = handlerFactory super.init() } - @objc // TODO(louisehsu): this initializer is for tests only. Make this private after the tests are moved to swift. - public convenience init( - receiptManager: FIAPReceiptManager, - handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler - ) { - self.init(receiptManager: receiptManager) - self.handlerFactory = handlerFactory + @objc + public convenience init(receiptManager: FIAPReceiptManager) { + self.init(receiptManager: receiptManager, handlerFactory: { FIAPRequestHandler(request: $0) }) } - // This is the designated initializer + // This is the actual designated initializer that should be used public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()) self.registrar = registrar @@ -80,10 +80,10 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { restoreTransactionFailed: { [weak self] error in self?.handleTransactionRestoreFailed(error as NSError) }, - restoreCompletedTransactionsFinished: { [weak self] () -> Void in + restoreCompletedTransactionsFinished: { [weak self] in self?.restoreCompletedTransactionsFinished() }, - shouldAddStorePayment: { [weak self] (payment: SKPayment, product: SKProduct) -> Bool in + shouldAddStorePayment: { [weak self] (payment, product) -> Bool in return self?.shouldAddStorePayment(payment: payment, product: product) ?? false }, updatedDownloads: { [weak self] _ in @@ -100,6 +100,8 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { name: "plugins.flutter.io/in_app_purchase", binaryMessenger: messenger) } + // MARK: - Pigeon Functions + public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> NSNumber? { @@ -109,11 +111,11 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) -> [SKPaymentTransactionMessage]? { - - let transactions = getPaymentQueueHandler().getUnfinishedTransactions() - return transactions.compactMap { - FIAObjectTranslator.convertTransaction(toPigeon: $0) - } + return getPaymentQueueHandler() + .getUnfinishedTransactions() + .compactMap { + FIAObjectTranslator.convertTransaction(toPigeon: $0) + } } public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) @@ -134,6 +136,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { requestHandlers.insert(handler) handler.startProductRequest { [weak self] response, startProductRequestError in + guard let self = self else { return } if let startProductRequestError = startProductRequestError { let error = FlutterError( code: "storekit_getproductrequest_platform_error", @@ -154,7 +157,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } for product in response.products { - self?.productsCache[product.productIdentifier] = product + self.productsCache[product.productIdentifier] = product } if #available(iOS 12.2, *) { @@ -162,7 +165,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { completion(responseMessage, nil) } } - self?.requestHandlers.remove(handler) + self.requestHandlers.remove(handler) } } @@ -309,14 +312,20 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { ) { #if os(iOS) if #available(iOS 13.0, *) { - let messenger = registrar?.messenger() + guard let messenger = registrar?.messenger() else { + fatalError("registrar.messenger can not be nil.") + } paymentQueueDelegateCallbackChannel = FlutterMethodChannel( name: "plugins.flutter.io/in_app_purchase_payment_queue_delegate", - binaryMessenger: messenger!) + binaryMessenger: messenger) + guard let unwrappedChannel = paymentQueueDelegateCallbackChannel else { + fatalError("registrar.messenger can not be nil.") + } paymentQueueDelegate = FIAPPaymentQueueDelegate( - methodChannel: paymentQueueDelegateCallbackChannel!) - getPaymentQueueHandler().delegate = (paymentQueueDelegate as! SKPaymentQueueDelegate) + methodChannel: unwrappedChannel) + + getPaymentQueueHandler().delegate = paymentQueueDelegate as? SKPaymentQueueDelegate } #endif } @@ -348,7 +357,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { let maps = transactions.map { FIAObjectTranslator.getMapFrom($0) } - transactionObserverCallbackChannel!.invokeMethod("updatedTransactions", arguments: maps) + transactionObserverCallbackChannel?.invokeMethod("updatedTransactions", arguments: maps) } @objc @@ -356,25 +365,25 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { let maps = transactions.map { FIAObjectTranslator.getMapFrom($0) } - transactionObserverCallbackChannel!.invokeMethod("removedTransactions", arguments: maps) + transactionObserverCallbackChannel?.invokeMethod("removedTransactions", arguments: maps) } @objc public func handleTransactionRestoreFailed(_ error: NSError) { - transactionObserverCallbackChannel!.invokeMethod( + transactionObserverCallbackChannel?.invokeMethod( "restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)) } @objc public func restoreCompletedTransactionsFinished() { - transactionObserverCallbackChannel!.invokeMethod( + transactionObserverCallbackChannel?.invokeMethod( "paymentQueueRestoreCompletedTransactionsFinished", arguments: nil) } @objc public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { productsCache[product.productIdentifier] = product - transactionObserverCallbackChannel! + transactionObserverCallbackChannel? .invokeMethod( "shouldAddStorePayment", arguments: [ @@ -388,11 +397,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { NSLog("Received an updatedDownloads callback, but downloads are not supported.") } - public func canMakePayments() -> Bool { - return SKPaymentQueue.canMakePayments() as Bool - } - - // Methods to allow for testing + // MARK: - Methods to allow for testing func getProduct(productID: String) -> SKProduct? { return self.productsCache[productID] as? SKProduct } @@ -405,7 +410,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { return SKReceiptRefreshRequest(receiptProperties: properties) } - // Private convenience methods + // MARK: - Private convenience methods private func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { let value = dictionary[key] return value is NSNull ? nil : value From f53c6061706012a2a608cdb082b755bab4f374ee Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 9 May 2024 15:42:10 -0700 Subject: [PATCH 23/29] pr comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index e9ae4a7d4159..965dcf278fba 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -24,8 +24,10 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { @objc public var registrar: FlutterPluginRegistrar? @objc + // This property is optional, as it requires self to exist to be initialized. public var paymentQueueHandler: FIAPaymentQueueHandler? @objc + // This property is optional, as it is set when the plugin is registered. public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { @@ -45,7 +47,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } @objc - // This is the default init used by the other inits. Shouldn't be directly called. public init( receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler = { @@ -58,13 +59,13 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { super.init() } - // TODO(louisehsu): this initializer is for tests only. Make this private after the tests are moved to swift. + // This init is used for tests @objc public convenience init(receiptManager: FIAPReceiptManager) { self.init(receiptManager: receiptManager, handlerFactory: { FIAPRequestHandler(request: $0) }) } - // This is the actual designated initializer that should be used + // This init gets called when the plugin gets registered public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()) self.registrar = registrar From c1fb882e901de899fdfda280c5fd0260b76ea643 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 9 May 2024 17:13:16 -0700 Subject: [PATCH 24/29] more pr comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 965dcf278fba..ce074a051cd1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -16,7 +16,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { private var productsCache: NSMutableDictionary = [:] private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, - // FIAPPaymentQueueDelegateonly gets set/used in registerPaymentQueueDelegateWithError or removePaymentQueueDelegateWithError, which both are ios13+ only + // FIAPPaymentQueueDelegate only gets set/used in registerPaymentQueueDelegateWithError or removePaymentQueueDelegateWithError, which both are ios13+ only private var paymentQueueDelegate: Any? private var requestHandlers = Set() private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) @@ -27,7 +27,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // This property is optional, as it requires self to exist to be initialized. public var paymentQueueHandler: FIAPaymentQueueHandler? @objc - // This property is optional, as it is set when the plugin is registered. + // This property is optional, as it needs to be set during plugin registration, and can't be directly initialized. public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { @@ -54,7 +54,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } ) { self.receiptManager = receiptManager - self.requestHandlers = Set() self.handlerFactory = handlerFactory super.init() } @@ -65,7 +64,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { self.init(receiptManager: receiptManager, handlerFactory: { FIAPRequestHandler(request: $0) }) } - // This init gets called when the plugin gets registered + // This init gets called during plugin registration public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()) self.registrar = registrar @@ -355,18 +354,20 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { @objc public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { - let maps = transactions.map { + let translatedTransactions = transactions.map { FIAObjectTranslator.getMapFrom($0) } - transactionObserverCallbackChannel?.invokeMethod("updatedTransactions", arguments: maps) + transactionObserverCallbackChannel?.invokeMethod( + "updatedTransactions", arguments: translatedTransactions) } @objc public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { - let maps = transactions.map { + let translatedTransactions = transactions.map { FIAObjectTranslator.getMapFrom($0) } - transactionObserverCallbackChannel?.invokeMethod("removedTransactions", arguments: maps) + transactionObserverCallbackChannel?.invokeMethod( + "removedTransactions", arguments: translatedTransactions) } @objc @@ -384,13 +385,12 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { @objc public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { productsCache[product.productIdentifier] = product - transactionObserverCallbackChannel? - .invokeMethod( - "shouldAddStorePayment", - arguments: [ - "payment": FIAObjectTranslator.getMapFrom(payment), - "product": FIAObjectTranslator.getMapFrom(product), - ]) + transactionObserverCallbackChannel?.invokeMethod( + "shouldAddStorePayment", + arguments: [ + "payment": FIAObjectTranslator.getMapFrom(payment), + "product": FIAObjectTranslator.getMapFrom(product), + ]) return false } @@ -398,7 +398,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { NSLog("Received an updatedDownloads callback, but downloads are not supported.") } - // MARK: - Methods to allow for testing + // MARK: - Methods exposed for testing func getProduct(productID: String) -> SKProduct? { return self.productsCache[productID] as? SKProduct } From 99120390069e23cbf428a96b4d4686f9198fac5f Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 13 May 2024 11:54:47 -0700 Subject: [PATCH 25/29] remove extra init --- .../darwin/Classes/InAppPurchasePlugin.swift | 6 --- .../RunnerTests/InAppPurchasePluginTests.m | 41 ++++++++++++++----- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index ce074a051cd1..aac1713cffd4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -58,12 +58,6 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { super.init() } - // This init is used for tests - @objc - public convenience init(receiptManager: FIAPReceiptManager) { - self.init(receiptManager: receiptManager, handlerFactory: { FIAPRequestHandler(request: $0) }) - } - // This init gets called during plugin registration public convenience init(registrar: FlutterPluginRegistrar) { self.init(receiptManager: FIAPReceiptManager()) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 5ed28529584c..8839bd294023 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -21,7 +21,11 @@ @implementation InAppPurchasePluginTest - (void)setUp { self.receiptManagerStub = [FIAPReceiptManagerStub new]; - self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; + self.plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; } - (void)tearDown { @@ -702,8 +706,11 @@ - (void)testHandleTransactionsUpdated { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = - [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -729,8 +736,11 @@ - (void)testHandleTransactionsRemoved { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = - [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -746,8 +756,11 @@ - (void)testHandleTransactionsRemoved { } - (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = - [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -759,8 +772,11 @@ - (void)testHandleTransactionRestoreFailed { } - (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = - [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -790,8 +806,11 @@ - (void)testShouldAddStorePayment { SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - InAppPurchasePlugin *plugin = - [[InAppPurchasePlugin alloc] initWithReceiptManager:_receiptManagerStub]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); From 1afb1b98e44d900a6cd66ce44dea57f8027a2c7b Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 13 May 2024 11:59:09 -0700 Subject: [PATCH 26/29] comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index aac1713cffd4..2cdebb54e722 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -47,6 +47,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } @objc + // This is the designated initializer public init( receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler = { From 51c47db283b2aa680066e0e150f606e26d4d94e4 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 13 May 2024 13:24:36 -0700 Subject: [PATCH 27/29] comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 2cdebb54e722..3f6a6dd2ef17 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -23,11 +23,11 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again and remove all instances of @objc @objc public var registrar: FlutterPluginRegistrar? - @objc // This property is optional, as it requires self to exist to be initialized. - public var paymentQueueHandler: FIAPaymentQueueHandler? @objc + public var paymentQueueHandler: FIAPaymentQueueHandler? // This property is optional, as it needs to be set during plugin registration, and can't be directly initialized. + @objc public var transactionObserverCallbackChannel: FlutterMethodChannel? public static func register(with registrar: FlutterPluginRegistrar) { @@ -47,7 +47,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } @objc - // This is the designated initializer + // This init is used for tests public init( receiptManager: FIAPReceiptManager, handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler = { From 44fbb4a27b7b49e3b81399563e62479aff68846c Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 14 May 2024 11:37:58 -0700 Subject: [PATCH 28/29] pr cmments --- .../darwin/Classes/InAppPurchasePlugin.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 3f6a6dd2ef17..cc061a66259d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -218,15 +218,23 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { } } + // TODO(louisehsu): Once tests and pigeon are migrated to Swift, ensure the param type is [String:String] instead of [String:String?] public func finishTransactionFinishMap( _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer ) { - guard let productIdentifier = finishMap["productIdentifier"] as? String, - let transactionIdentifier = (finishMap["transactionIdentifier"] ?? "") as? String - else { - return - } + // casting [String:Any] into [String:String?] will auto convert `NSNull` into nil `String` + // TODO(louisehsu): This is a workaround for objc pigeon's NSNull support. Once we move to swift pigeon, this can be removed. + let castedFinishMap: [String: String] = finishMap.mapValues { value in + if let _ = value as? NSNull { + return "" + } else if let stringValue = value as? String { + return stringValue + } + fatalError("This dict should only contain either NSNull or String") + } + let productIdentifier = castedFinishMap["productIdentifier"] + let transactionIdentifier = castedFinishMap["transactionIdentifier"] let pendingTransactions = getPaymentQueueHandler().getUnfinishedTransactions() for transaction in pendingTransactions { From 7e48e2af8399245a8ab39d88adde4e0f02df8826 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 14 May 2024 15:26:19 -0700 Subject: [PATCH 29/29] pr comments --- .../darwin/Classes/InAppPurchasePlugin.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index cc061a66259d..511fc264c0a5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -223,11 +223,10 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer ) { - // casting [String:Any] into [String:String?] will auto convert `NSNull` into nil `String` // TODO(louisehsu): This is a workaround for objc pigeon's NSNull support. Once we move to swift pigeon, this can be removed. - let castedFinishMap: [String: String] = finishMap.mapValues { value in + let castedFinishMap: [String: String] = finishMap.compactMapValues { value in if let _ = value as? NSNull { - return "" + return nil } else if let stringValue = value as? String { return stringValue } @@ -242,7 +241,7 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { // So if it is null AND a transaction in the pendingTransactions list has // also a null transactionIdentifier we check for equal product identifiers. if transaction.transactionIdentifier == transactionIdentifier - || (transactionIdentifier == "" + || (transactionIdentifier == nil && transaction.transactionIdentifier == nil && transaction.payment.productIdentifier == productIdentifier) {