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 a1f3079f997b..63e8cf5ca752 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,10 @@ +## 0.4.11+3 + +* Migrates the StoreKit 1 core (`FIAPaymentQueueHandler`, `FIAObjectTranslator`, + `FIAPReceiptManager`, `FIAPRequestHandler`, `FIAPPaymentQueueDelegate`, + `FIATransactionCache`, and their protocol shims) from Objective-C to Swift. + No functional changes. + ## 0.4.11+2 * Updates pigeon dev_dependency to ^27.3.2 for analyzer 14 compatibility. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAObjectTranslator.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAObjectTranslator.swift new file mode 100644 index 000000000000..4e8b6739782b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAObjectTranslator.swift @@ -0,0 +1,401 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +#if canImport(in_app_purchase_storekit_objc) + import in_app_purchase_storekit_objc +#endif + +public class FIAObjectTranslator: NSObject { + // MARK: - SKProduct Coders + + public static func getMapFrom(_ product: SKProduct) -> [String: Any] { + return [ + "discounts": getMapArrayFrom(product.discounts), + "introductoryPrice": product.introductoryPrice.map { getMapFrom($0) } ?? NSNull(), + "localizedDescription": product.localizedDescription, + "localizedTitle": product.localizedTitle, + "productIdentifier": product.productIdentifier, + "price": product.price.description, + "subscriptionGroupIdentifier": product.subscriptionGroupIdentifier ?? NSNull(), + "subscriptionPeriod": product.subscriptionPeriod.map { getMapFrom($0) } ?? NSNull(), + "priceLocale": getMapFrom(product.priceLocale), + ] + } + + public static func getMapFrom(_ period: SKProductSubscriptionPeriod) -> [String: Any] { + return ["numberOfUnits": period.numberOfUnits, "unit": period.unit.rawValue] + } + + static func getMapArrayFrom(_ productDiscounts: [SKProductDiscount]) -> [Any] { + return productDiscounts.map { getMapFrom($0) } + } + + public static func getMapFrom(_ discount: SKProductDiscount) -> [String: Any] { + return [ + "identifier": discount.identifier ?? NSNull(), + "numberOfPeriods": discount.numberOfPeriods, + "paymentMode": discount.paymentMode.rawValue, + "price": discount.price.description, + "subscriptionPeriod": getMapFrom(discount.subscriptionPeriod), + "type": discount.type.rawValue, + "priceLocale": getMapFrom(discount.priceLocale), + ] + } + + public static func getMapFrom(_ productResponse: SKProductsResponse) -> [String: Any] { + let productsMapArray = productResponse.products.map { getMapFrom($0) } + return [ + "products": productsMapArray, + "invalidProductIdentifiers": productResponse.invalidProductIdentifiers, + ] + } + + public static func getMapFrom(_ payment: SKPayment) -> [String: Any] { + return [ + "applicationUsername": payment.applicationUsername ?? NSNull(), + "productIdentifier": payment.productIdentifier, + "quantity": payment.quantity, + "requestData": payment.requestData.flatMap { String(data: $0, encoding: .utf8) } ?? NSNull(), + "simulatesAskToBuyInSandbox": payment.simulatesAskToBuyInSandbox, + ] + } + + // This intentionally only exposes fields that there has been a demonstrated + // need for; see discussion in https://github.com/flutter/plugins/pull/3897. + public static func getMapFrom(_ locale: Locale) -> [String: Any] { + let nsLocale = locale as NSLocale + return [ + "currencySymbol": nsLocale.object(forKey: .currencySymbol) ?? NSNull(), + "currencyCode": nsLocale.object(forKey: .currencyCode) ?? NSNull(), + "countryCode": nsLocale.object(forKey: .countryCode) ?? NSNull(), + ] + } + + public static func getSKMutablePayment(fromMap map: [String: Any]) -> SKMutablePayment { + let payment = SKMutablePayment() + payment.productIdentifier = map["productIdentifier"] as? String ?? "" + if let utf8String = map["requestData"] as? String { + payment.requestData = utf8String.data(using: .utf8) + } + payment.quantity = (map["quantity"] as? NSNumber)?.intValue ?? 0 + payment.applicationUsername = map["applicationUsername"] as? String + payment.simulatesAskToBuyInSandbox = + (map["simulatesAskToBuyInSandbox"] as? NSNumber)?.boolValue ?? false + return payment + } + + public static func getMapFrom(_ transaction: SKPaymentTransaction) -> [String: Any] { + return [ + "error": transaction.error.map { getMapFrom($0 as NSError) } ?? NSNull(), + "payment": (transaction.value(forKey: "payment") as? SKPayment).map { getMapFrom($0) } + ?? NSNull(), + "originalTransaction": transaction.original.map { getMapFrom($0) } ?? NSNull(), + "transactionTimeStamp": transaction.transactionDate?.timeIntervalSince1970 ?? NSNull(), + "transactionIdentifier": transaction.transactionIdentifier ?? NSNull(), + "transactionState": transaction.transactionState.rawValue, + ] + } + + public static func getMapFrom(_ error: NSError) -> [String: Any] { + return [ + "code": error.code, + "domain": error.domain, + "userInfo": encodeNSErrorUserInfo(error.userInfo), + ] + } + + static func encodeNSErrorUserInfo(_ value: Any) -> Any { + switch value { + case let error as NSError: + return getMapFrom(error) + case let url as URL: + return url.absoluteString + case is NSNumber, is String: + return value + case let array as [Any]: + return array.map { encodeNSErrorUserInfo($0) } + case let dictionary as [AnyHashable: Any]: + var errors: [AnyHashable: Any] = [:] + for (key, dictValue) in dictionary { + errors[key] = encodeNSErrorUserInfo(dictValue) + } + return errors + default: + return + "Unable to encode native userInfo object of type \(type(of: value)) to map. Please submit an issue at " + + "https://github.com/flutter/flutter/issues/new with the title " + + "\"[in_app_purchase_storekit] " + + "Unable to encode userInfo of type \(type(of: value))\" and add reproduction steps and the error " + + "details in " + + "the description field." + } + } + + public static func getMapFrom(_ storefront: SKStorefront) -> [String: Any] { + return ["countryCode": storefront.countryCode, "identifier": storefront.identifier] + } + + public static func getMapFrom( + _ storefront: SKStorefront, andSKPaymentTransaction transaction: SKPaymentTransaction + ) -> [String: Any] { + return [ + "storefront": getMapFrom(storefront), + "transaction": getMapFrom(transaction), + ] + } + + public static func getSKPaymentDiscount( + fromMap map: [String: Any]?, withError error: inout NSString? + ) + -> SKPaymentDiscount? + { + guard let map = map, !map.isEmpty else { + return nil + } + + let identifier = map["identifier"] as? String + let keyIdentifier = map["keyIdentifier"] as? String + let nonce = map["nonce"] as? String + let signature = map["signature"] as? String + let timestamp = map["timestamp"] as? NSNumber + + guard let identifier = identifier, !identifier.isEmpty else { + error = "When specifying a payment discount the 'identifier' field is mandatory." + return nil + } + + guard let keyIdentifier = keyIdentifier, !keyIdentifier.isEmpty else { + error = "When specifying a payment discount the 'keyIdentifier' field is mandatory." + return nil + } + + guard let nonce = nonce, !nonce.isEmpty else { + error = "When specifying a payment discount the 'nonce' field is mandatory." + return nil + } + + guard let signature = signature, !signature.isEmpty else { + error = "When specifying a payment discount the 'signature' field is mandatory." + return nil + } + + guard let timestamp = timestamp, timestamp.int64Value > 0 else { + error = "When specifying a payment discount the 'timestamp' field is mandatory." + return nil + } + + guard let nonceUUID = UUID(uuidString: nonce) else { + error = "When specifying a payment discount the 'nonce' field is mandatory." + return nil + } + + return SKPaymentDiscount( + identifier: identifier, keyIdentifier: keyIdentifier, nonce: nonceUUID, signature: signature, + timestamp: timestamp) + } + + // MARK: - Pigeon message translators + + public static func convertTransaction(toPigeon transaction: SKPaymentTransaction?) + -> FIASKPaymentTransactionMessage? + { + guard let transaction = transaction else { + return nil + } + let paymentMessage = + convertPayment(toPigeon: transaction.value(forKey: "payment") as? SKPayment) + ?? FIASKPaymentMessage.make( + withProductIdentifier: "", applicationUsername: nil, requestData: nil, quantity: 0, + simulatesAskToBuyInSandbox: false, paymentDiscount: nil) + return FIASKPaymentTransactionMessage.make( + withPayment: paymentMessage, + transactionState: convertTransactionStateToPigeon(transaction.transactionState), + originalTransaction: transaction.original.flatMap { + convertTransaction(toPigeon: $0) + }, + transactionTimeStamp: NSNumber( + value: transaction.transactionDate?.timeIntervalSince1970 ?? 0), + transactionIdentifier: transaction.transactionIdentifier, + error: convertSKError(toPigeon: transaction.error as NSError?)) + } + + public static func convertSKError(toPigeon error: NSError?) -> FIASKErrorMessage? { + guard let error = error else { + return nil + } + + var userInfo: [String: Any] = [:] + for (key, value) in error.userInfo { + userInfo[key as? String ?? "\(key)"] = encodeNSErrorUserInfo(value) + } + + return FIASKErrorMessage.make(withCode: error.code, domain: error.domain, userInfo: userInfo) + } + + static func convertTransactionStateToPigeon(_ state: SKPaymentTransactionState) + -> FIASKPaymentTransactionStateMessage + { + switch state { + case .purchasing: + return .purchasing + case .purchased: + return .purchased + case .failed: + return .failed + case .restored: + return .restored + case .deferred: + return .deferred + @unknown default: + return .purchasing + } + } + + public static func convertPayment(toPigeon payment: SKPayment?) -> FIASKPaymentMessage? { + guard let payment = payment else { + return nil + } + return FIASKPaymentMessage.make( + withProductIdentifier: payment.productIdentifier, + applicationUsername: payment.applicationUsername, + requestData: payment.requestData.flatMap { String(data: $0, encoding: .utf8) }, + quantity: payment.quantity, + simulatesAskToBuyInSandbox: payment.simulatesAskToBuyInSandbox, + paymentDiscount: convertPaymentDiscount(toPigeon: payment.paymentDiscount)) + } + + public static func convertPaymentDiscount(toPigeon discount: SKPaymentDiscount?) + -> FIASKPaymentDiscountMessage? + { + guard let discount = discount else { + return nil + } + return FIASKPaymentDiscountMessage.make( + withIdentifier: discount.identifier, keyIdentifier: discount.keyIdentifier, + nonce: discount.nonce.uuidString, signature: discount.signature, + timestamp: discount.timestamp.intValue) + } + + public static func convertStorefront(toPigeon storefront: SKStorefront?) + -> FIASKStorefrontMessage? + { + guard let storefront = storefront else { + return nil + } + return FIASKStorefrontMessage.make( + withCountryCode: storefront.countryCode, identifier: storefront.identifier) + } + + public static func convertSKProductSubscriptionPeriod( + toPigeon period: SKProductSubscriptionPeriod? + ) -> FIASKProductSubscriptionPeriodMessage? { + guard let period = period else { + return nil + } + + let unit: FIASKSubscriptionPeriodUnitMessage + switch period.unit { + case .day: + unit = .day + case .week: + unit = .week + case .month: + unit = .month + case .year: + unit = .year + @unknown default: + unit = .day + } + + return FIASKProductSubscriptionPeriodMessage.make( + withNumberOfUnits: period.numberOfUnits, unit: unit) + } + + public static func convertProductDiscount(toPigeon productDiscount: SKProductDiscount?) + -> FIASKProductDiscountMessage? + { + guard let productDiscount = productDiscount else { + return nil + } + + let paymentMode: FIASKProductDiscountPaymentModeMessage + switch productDiscount.paymentMode { + case .freeTrial: + paymentMode = .freeTrial + case .payAsYouGo: + paymentMode = .payAsYouGo + case .payUpFront: + paymentMode = .payUpFront + @unknown default: + paymentMode = .payAsYouGo + } + + let type: FIASKProductDiscountTypeMessage + switch productDiscount.type { + case .introductory: + type = .introductory + case .subscription: + type = .subscription + @unknown default: + type = .introductory + } + + return FIASKProductDiscountMessage.make( + withPrice: productDiscount.price.description, + priceLocale: convertNSLocale(toPigeon: productDiscount.priceLocale)!, + numberOfPeriods: productDiscount.numberOfPeriods, + paymentMode: paymentMode, + subscriptionPeriod: convertSKProductSubscriptionPeriod( + toPigeon: productDiscount.subscriptionPeriod)!, + identifier: productDiscount.identifier, + type: type) + } + + public static func convertNSLocale(toPigeon locale: Locale?) -> FIASKPriceLocaleMessage? { + guard let locale = locale else { + return nil + } + let nsLocale = locale as NSLocale + return FIASKPriceLocaleMessage.make( + withCurrencySymbol: nsLocale.object(forKey: .currencySymbol) as? String ?? "", + currencyCode: nsLocale.object(forKey: .currencyCode) as? String ?? "", + countryCode: nsLocale.object(forKey: .countryCode) as? String ?? "") + } + + public static func convertProduct(toPigeon product: SKProduct?) -> FIASKProductMessage? { + guard let product = product else { + return nil + } + + let pigeonProductDiscounts = product.discounts.map { convertProductDiscount(toPigeon: $0)! } + + return FIASKProductMessage.make( + withProductIdentifier: product.productIdentifier, + localizedTitle: product.localizedTitle, + localizedDescription: product.localizedDescription, + priceLocale: convertNSLocale(toPigeon: product.priceLocale)!, + subscriptionGroupIdentifier: product.subscriptionGroupIdentifier, + price: product.price.description, + subscriptionPeriod: convertSKProductSubscriptionPeriod(toPigeon: product.subscriptionPeriod), + introductoryPrice: convertProductDiscount(toPigeon: product.introductoryPrice), + discounts: pigeonProductDiscounts) + } + + public static func convertProductsResponse(toPigeon productsResponse: SKProductsResponse?) + -> FIASKProductsResponseMessage? + { + guard let productsResponse = productsResponse else { + return nil + } + + let pigeonProducts = productsResponse.products.map { convertProduct(toPigeon: $0)! } + + return FIASKProductsResponseMessage.make( + withProducts: pigeonProducts, + invalidProductIdentifiers: productsResponse.invalidProductIdentifiers) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPPaymentQueueDelegate.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPPaymentQueueDelegate.swift new file mode 100644 index 000000000000..d19b6a1c6f2f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPPaymentQueueDelegate.swift @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +@available(iOS 13, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +public class FIAPPaymentQueueDelegate: NSObject, SKPaymentQueueDelegate { + /// The designated Flutter method channel that handles if a transaction should be continued + private let callbackChannel: FLTMethodChannelProtocol + + public init(methodChannel: FLTMethodChannelProtocol) { + self.callbackChannel = methodChannel + } + + public func paymentQueue( + _ paymentQueue: SKPaymentQueue, shouldContinue transaction: SKPaymentTransaction, + in storefront: SKStorefront + ) -> Bool { + // Default return value for this method is true (see + // https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate/3521328-paymentqueueshouldshowpriceconse?language=objc) + var shouldContinue = true + let semaphore = DispatchSemaphore(value: 0) + callbackChannel.invokeMethod( + "shouldContinueTransaction", + arguments: FIAObjectTranslator.getMapFrom(storefront, andSKPaymentTransaction: transaction), + result: { result in + // When result is a valid instance of NSNumber use it to determine + // if the transaction should continue. Otherwise use the default + // value. + if let result = result as? NSNumber { + shouldContinue = result.boolValue + } + + semaphore.signal() + }) + + // The client should respond within 1 second otherwise continue + // with default value. + _ = semaphore.wait(timeout: .now() + 1) + + return shouldContinue + } + + #if os(iOS) + public func paymentQueueShouldShowPriceConsent(_ paymentQueue: SKPaymentQueue) -> Bool { + // Default return value for this method is true (see + // https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate/3521328-paymentqueueshouldshowpriceconse?language=objc) + var shouldShowPriceConsent = true + let semaphore = DispatchSemaphore(value: 0) + callbackChannel.invokeMethod( + "shouldShowPriceConsent", arguments: nil, + result: { result in + // When result is a valid instance of NSNumber use it to determine + // if the transaction should continue. Otherwise use the default + // value. + if let result = result as? NSNumber { + shouldShowPriceConsent = result.boolValue + } + + semaphore.signal() + }) + + // The client should respond within 1 second otherwise continue + // with default value. + _ = semaphore.wait(timeout: .now() + 1) + + return shouldShowPriceConsent + } + #endif +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPReceiptManager.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPReceiptManager.swift new file mode 100644 index 000000000000..40c4e6680e45 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPReceiptManager.swift @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +public class FIAPReceiptManager: NSObject { + public override init() {} + + public func retrieveReceiptWithError(_ flutterError: inout FlutterError?) -> String? { + guard let receiptURL = receiptURL else { + return nil + } + var receiptError: NSError? + let receipt = getReceiptData(receiptURL, error: &receiptError) + guard let receipt = receipt, receiptError == nil else { + let errorMap = FIAObjectTranslator.getMapFrom(receiptError ?? NSError()) + flutterError = FlutterError( + code: "\(errorMap["code"] ?? "")", + message: errorMap["domain"] as? String, + details: errorMap["userInfo"]) + return nil + } + return receipt.base64EncodedString() + } + + /// Gets the receipt file data from the location of the url. Can be nil if + /// there is an error. This method is defined so it can be overridden for testing. + @objc(getReceiptData:error:) + public func getReceiptData(_ url: URL, error: NSErrorPointer) -> Data? { + do { + return try Data(contentsOf: url, options: .mappedIfSafe) + } catch let dataError as NSError { + error?.pointee = dataError + return nil + } + } + + /// Gets the app store receipt url. Can be nil if + /// there is an error. This property is defined so it can be overridden for testing. + @objc public var receiptURL: URL? { + return Bundle.main.appStoreReceiptURL + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPRequestHandler.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPRequestHandler.swift new file mode 100644 index 000000000000..74b1e2f76351 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPRequestHandler.swift @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +public class FIAPRequestHandler: NSObject, FLTRequestHandlerProtocol { + private var completion: ((SKProductsResponse?, Error?) -> Void)? + private let request: SKRequest + + public init(request: SKRequest) { + self.request = request + super.init() + request.delegate = self + } + + public func startProductRequest( + completionHandler: @escaping (SKProductsResponse?, Error?) -> Void + ) { + completion = completionHandler + request.start() + } +} + +extension FIAPRequestHandler: SKProductsRequestDelegate { + public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) + { + if let completion = completion { + completion(response, nil) + // set the completion to nil here so completion won't be triggered again in + // requestDidFinish for SKProductRequest. + self.completion = nil + } + } +} + +extension FIAPRequestHandler: SKRequestDelegate { + public func requestDidFinish(_ request: SKRequest) { + completion?(nil, nil) + } + + public func request(_ request: SKRequest, didFailWithError error: Error) { + completion?(nil, error) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPaymentQueueHandler.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPaymentQueueHandler.swift new file mode 100644 index 000000000000..45ee580cec8d --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIAPaymentQueueHandler.swift @@ -0,0 +1,259 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +#if canImport(in_app_purchase_storekit_objc) + import in_app_purchase_storekit_objc +#endif + +/// A Swift port of the legacy Objective-C `FIAPaymentQueueHandler`. +/// +/// The "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks are only called while actively observing transactions. To start +/// observing transactions send the "startObservingPaymentQueue" message. +/// Sending the "stopObservingPaymentQueue" message will stop actively +/// observing transactions. When transactions are not observed they are cached +/// to the "transactionCache" and will be delivered via the +/// "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks as soon as the "startObservingPaymentQueue" message arrives. +/// +/// Note: cached transactions that are not processed when the application is +/// killed will be delivered again by the App Store as soon as the application +/// starts again. +public class FIAPaymentQueueHandler: NSObject, SKPaymentTransactionObserver, + FLTPaymentQueueHandlerProtocol +{ + + /// The SKPaymentQueue (wrapper) instance connected to the App Store and + /// responsible for processing transactions. + private let queue: FLTPaymentQueueProtocol + + /// Callback method that is called each time the App Store indicates transactions are updated. + private let transactionsUpdated: TransactionsUpdated? + + /// Callback method that is called each time the App Store indicates transactions are removed. + private let transactionsRemoved: TransactionsRemoved? + + /// Callback method that is called each time the App Store indicates transactions failed to + /// restore. + private let restoreTransactionFailed: RestoreTransactionFailed? + + /// Callback method that is called each time the App Store indicates restoring of transactions + /// has finished. + private let paymentQueueRestoreCompletedTransactionsFinished: + RestoreCompletedTransactionsFinished? + + /// Callback method that is called each time an in-app purchase has been initiated from the App + /// Store. + private let shouldAddStorePayment: ShouldAddStorePayment? + + /// Callback method that is called each time the App Store indicates downloads are updated. + private let updatedDownloads: UpdatedDownloads? + + /// The transaction cache responsible for caching transactions. + /// + /// Keeps track of transactions that arrive when the Flutter client is not + /// actively observing for transactions. + private let transactionCache: FLTTransactionCacheProtocol + + /// Indicates if the Flutter client is observing transactions. + /// + /// When the client is not observing, transactions are cached and send to + /// the client as soon as it starts observing. The Flutter client can start + /// observing by sending a startObservingPaymentQueue message and stop by + /// sending a stopObservingPaymentQueue message. + private var observingTransactions = false + + /// An object that provides information needed to complete transactions. + public weak var delegate: SKPaymentQueueDelegate? + + /// Creates a new FIAPaymentQueueHandler. + /// + /// - Parameters: + /// - queue: The SKPaymentQueue instance connected to the App Store and + /// responsible for processing transactions. + /// - transactionsUpdated: Callback method that is called each time the App + /// Store indicates transactions are updated. + /// - transactionRemoved: Callback method that is called each time the App + /// Store indicates transactions are removed. + /// - restoreTransactionFailed: Callback method that is called each time + /// the App Store indicates transactions failed to restore. + /// - restoreCompletedTransactionsFinished: Callback method that is called + /// each time the App Store indicates restoring of transactions has + /// finished. + /// - shouldAddStorePayment: Callback method that is called each time an + /// in-app purchase has been initiated from the App Store. + /// - updatedDownloads: Callback method that is called each time the App + /// Store indicates downloads are updated. + /// - transactionCache: An empty FIATransactionCache instance that is + /// responsible for keeping track of transactions that arrive when not + /// actively observing transactions. + public required init( + queue: FLTPaymentQueueProtocol, + transactionsUpdated: TransactionsUpdated?, + transactionRemoved: TransactionsRemoved?, + restoreTransactionFailed: RestoreTransactionFailed?, + restoreCompletedTransactionsFinished: RestoreCompletedTransactionsFinished?, + shouldAddStorePayment: ShouldAddStorePayment?, + updatedDownloads: UpdatedDownloads?, + transactionCache: FLTTransactionCacheProtocol + ) { + self.queue = queue + self.transactionsUpdated = transactionsUpdated + self.transactionsRemoved = transactionRemoved + self.restoreTransactionFailed = restoreTransactionFailed + self.paymentQueueRestoreCompletedTransactionsFinished = restoreCompletedTransactionsFinished + self.shouldAddStorePayment = shouldAddStorePayment + self.updatedDownloads = updatedDownloads + self.transactionCache = transactionCache + super.init() + + self.queue.add(self) + self.queue.delegate = self.delegate + } + + public func startObservingPaymentQueue() { + observingTransactions = true + processCachedTransactions() + } + + public func stopObservingPaymentQueue() { + // When the client stops observing transaction, the transaction observer is + // not removed from the SKPaymentQueue. The FIAPaymentQueueHandler will cache + // transactions in memory when the client is not observing, allowing the app + // to process these transactions if it starts observing again during the same + // lifetime of the app. + // + // If the app is killed, cached transactions will be removed from memory; + // however, the App Store will re-deliver the transactions as soon as the app + // is started again, since the cached transactions have not been acknowledged + // by the client (by sending the `finishTransaction` message). + observingTransactions = false + } + + private func processCachedTransactions() { + var cachedObjects = transactionCache.getObjectsFor(.updatedTransactions) + if cachedObjects.count != 0 { + transactionsUpdated?(cachedObjects as! [SKPaymentTransaction]) + } + + cachedObjects = transactionCache.getObjectsFor(.updatedDownloads) + if cachedObjects.count != 0 { + updatedDownloads?(cachedObjects as! [SKDownload]) + } + + cachedObjects = transactionCache.getObjectsFor(.removedTransactions) + if cachedObjects.count != 0 { + transactionsRemoved?(cachedObjects as! [SKPaymentTransaction]) + } + + transactionCache.clear() + } + + public func add(_ payment: SKPayment) -> Bool { + for transaction in queue.transactions { + if transaction.payment.productIdentifier == payment.productIdentifier { + return false + } + } + queue.add(payment) + return true + } + + public func finish(_ transaction: SKPaymentTransaction) { + queue.finish(transaction) + } + + public func restoreTransactions(_ applicationName: String?) { + if let applicationName = applicationName { + queue.restoreCompletedTransactions(withApplicationUsername: applicationName) + } else { + queue.restoreCompletedTransactions() + } + } + + #if os(iOS) + public func presentCodeRedemptionSheet() { + if #available(iOS 14, *) { + queue.presentCodeRedemptionSheet() + } else { + NSLog("presentCodeRedemptionSheet is only available on iOS 14 or newer") + } + } + #endif + + #if os(iOS) + @available(iOS 13.4, *) + public func showPriceConsentIfNeeded() { + queue.showPriceConsentIfNeeded() + } + #endif + + // MARK: - observing + + // Sent when the transaction array has changed (additions or state changes). Client should + // check state of transactions and finish as appropriate. + public func paymentQueue( + _ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction] + ) { + if !observingTransactions { + transactionCache.add(transactions, for: .updatedTransactions) + return + } + + // notify dart through callbacks. + transactionsUpdated?(transactions) + } + + // Sent when transactions are removed from the queue (via finishTransaction:). + public func paymentQueue( + _ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction] + ) { + if !observingTransactions { + transactionCache.add(transactions, for: .removedTransactions) + return + } + transactionsRemoved?(transactions) + } + + // Sent when an error is encountered while adding transactions from the user's purchase history + // back to the queue. + public func paymentQueue( + _ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error + ) { + restoreTransactionFailed?(error as NSError) + } + + // Sent when all transactions from the user's purchase history have successfully been added + // back to the queue. + public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { + paymentQueueRestoreCompletedTransactionsFinished?() + } + + // Sent when the download state has changed. + public func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) { + if !observingTransactions { + transactionCache.add(downloads, for: .updatedDownloads) + return + } + updatedDownloads?(downloads) + } + + // Sent when a user initiates an IAP buy from the App Store. + public func paymentQueue( + _ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct + ) -> Bool { + return shouldAddStorePayment?(payment, product) ?? false + } + + public func getUnfinishedTransactions() -> [SKPaymentTransaction] { + return queue.transactions + } + + public var storefront: SKStorefront? { + return queue.storefront + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIATransactionCache.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIATransactionCache.swift new file mode 100644 index 000000000000..d95971cc3ea6 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/FIATransactionCache.swift @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +@objc public enum TransactionCacheKey: Int { + case updatedDownloads + case updatedTransactions + case removedTransactions +} + +public class FIATransactionCache: NSObject { + /// A dictionary storing the objects that are cached. + private var cache: [TransactionCacheKey: [Any]] = [:] + + /// Adds objects to the transaction cache. + /// + /// If the cache already contains an array of objects on the specified key, the supplied + /// array will be appended to the existing array. + @objc(addObjects:forKey:) + public func add(_ objects: [Any], for key: TransactionCacheKey) { + cache[key, default: []].append(contentsOf: objects) + } + + /// Gets the array of objects stored at the given key. + /// + /// If there are no objects associated with the given key an empty array is returned. + @objc(getObjectsForKey:) + public func getObjectsFor(_ key: TransactionCacheKey) -> [Any] { + return cache[key] ?? [] + } + + /// Removes all objects from the transaction cache. + @objc public func clear() { + cache.removeAll() + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTMethodChannelProtocol.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTMethodChannelProtocol.swift new file mode 100644 index 000000000000..cd122d89cfd3 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTMethodChannelProtocol.swift @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +/// A protocol that wraps FlutterMethodChannel. +@objc public protocol FLTMethodChannelProtocol: NSObjectProtocol { + /// Invokes the specified Flutter method with the specified arguments, expecting + /// an asynchronous result. + func invokeMethod(_ method: String, arguments: Any?) + + /// Invokes the specified Flutter method with the specified arguments and specified callback + func invokeMethod(_ method: String, arguments: Any?, result: FlutterResult?) +} + +/// The default method channel that wraps FlutterMethodChannel +public class DefaultMethodChannel: NSObject, FLTMethodChannelProtocol { + /// The wrapped FlutterMethodChannel + private let channel: FlutterMethodChannel + + /// Initialize this wrapper with a FlutterMethodChannel + public init(channel: FlutterMethodChannel) { + self.channel = channel + } + + public func invokeMethod(_ method: String, arguments: Any?) { + channel.invokeMethod(method, arguments: arguments) + } + + public func invokeMethod(_ method: String, arguments: Any?, result: FlutterResult?) { + channel.invokeMethod(method, arguments: arguments, result: result) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueHandlerProtocol.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueHandlerProtocol.swift new file mode 100644 index 000000000000..c50be1232d67 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueHandlerProtocol.swift @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +public typealias TransactionsUpdated = ([SKPaymentTransaction]) -> Void +public typealias TransactionsRemoved = ([SKPaymentTransaction]) -> Void +public typealias RestoreTransactionFailed = (NSError) -> Void +public typealias RestoreCompletedTransactionsFinished = () -> Void +public typealias ShouldAddStorePayment = (SKPayment, SKProduct) -> Bool +public typealias UpdatedDownloads = ([SKDownload]) -> Void + +/// A protocol that conforms to SKPaymentTransactionObserver and handles SKPaymentQueue methods +@objc public protocol FLTPaymentQueueHandlerProtocol: NSObjectProtocol, SKPaymentTransactionObserver +{ + /// An object that provides information needed to complete transactions. + @available(iOS 13.0, macOS 10.15, watchOS 6.2, *) + weak var delegate: SKPaymentQueueDelegate? { get set } + + /// An object containing the location and unique identifier of an Apple App Store storefront. + @available(iOS 13.0, macOS 10.15, watchOS 6.2, *) + var storefront: SKStorefront? { get } + + /// Creates a new FIAPaymentQueueHandler. + /// + /// The "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" + /// callbacks are only called while actively observing transactions. To start + /// observing transactions send the "startObservingPaymentQueue" message. + /// Sending the "stopObservingPaymentQueue" message will stop actively + /// observing transactions. When transactions are not observed they are cached + /// to the "transactionCache" and will be delivered via the + /// "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" + /// callbacks as soon as the "startObservingPaymentQueue" message arrives. + /// + /// Note: cached transactions that are not processed when the application is + /// killed will be delivered again by the App Store as soon as the application + /// starts again. + init( + queue: FLTPaymentQueueProtocol, + transactionsUpdated: TransactionsUpdated?, + transactionRemoved: TransactionsRemoved?, + restoreTransactionFailed: RestoreTransactionFailed?, + restoreCompletedTransactionsFinished: RestoreCompletedTransactionsFinished?, + shouldAddStorePayment: ShouldAddStorePayment?, + updatedDownloads: UpdatedDownloads?, + transactionCache: FLTTransactionCacheProtocol + ) + + /// Can throw exceptions if the transaction type is purchasing, should always used in a try block. + func finish(_ transaction: SKPaymentTransaction) + + /// Attempt to restore transactions. Require app store receipt url. + func restoreTransactions(_ applicationName: String?) + + #if os(iOS) + /// Displays a sheet that enables users to redeem subscription offer codes. + func presentCodeRedemptionSheet() + #endif + + /// Return all transactions that are not marked as complete. + func getUnfinishedTransactions() -> [SKPaymentTransaction] + + /// This method needs to be called before any other methods. + func startObservingPaymentQueue() + + /// Call this method when the Flutter app is no longer listening + func stopObservingPaymentQueue() + + /// Appends a payment to the SKPaymentQueue. + /// + /// - Parameter payment: Payment object to be added to the payment queue. + /// - Returns: whether "addPayment" was successful. + func add(_ payment: SKPayment) -> Bool + + #if os(iOS) + /// Displays the price consent sheet. + /// + /// The price consent sheet is only displayed when the following + /// is true: + /// - You have increased the price of the subscription in App Store Connect. + /// - The subscriber has not yet responded to a price consent query. + /// Otherwise the method has no effect. + @available(iOS 13.4, *) + func showPriceConsentIfNeeded() + #endif +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueProtocol.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueProtocol.swift new file mode 100644 index 000000000000..3315838dd612 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTPaymentQueueProtocol.swift @@ -0,0 +1,120 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +/// A protocol that wraps SKPaymentQueue +@objc public protocol FLTPaymentQueueProtocol: NSObjectProtocol { + /// An object containing the location and unique identifier of an Apple App Store storefront. + @available(iOS 13.0, *) + var storefront: SKStorefront? { get set } + + /// A list of SKPaymentTransactions, which each represents a single transaction + var transactions: [SKPaymentTransaction] { get set } + + /// An object that provides information needed to complete transactions. + @available(iOS 13.0, macOS 10.15, watchOS 6.2, *) + weak var delegate: SKPaymentQueueDelegate? { get set } + + /// Remove a finished (i.e. failed or completed) transaction from the queue. Attempting to finish a + /// purchasing transaction will throw an exception. + func finish(_ transaction: SKPaymentTransaction) + + /// Observers are not retained. The transactions array will only be synchronized with the server + /// while the queue has observers. This may require that the user authenticate. + @objc(addTransactionObserver:) + func add(_ observer: SKPaymentTransactionObserver) + + /// Add a payment to the server queue. The payment is copied to add an SKPaymentTransaction to the + /// transactions array. The same payment can be added multiple times to create multiple + /// transactions. + @objc(addPayment:) + func add(_ payment: SKPayment) + + /// Will add completed transactions for the current user back to the queue to be re-completed. + func restoreCompletedTransactions() + + /// Will add completed transactions for the current user back to the queue to be re-completed. This + /// version requires an identifier to the user's account. + func restoreCompletedTransactions(withApplicationUsername username: String?) + + #if os(iOS) + /// Call this method to have StoreKit present a sheet enabling the user to redeem codes provided by + /// your app. Only for iOS. + @available(iOS 14.0, *) + func presentCodeRedemptionSheet() + + /// If StoreKit has called your SKPaymentQueueDelegate's "paymentQueueShouldShowPriceConsent:" + /// method and you returned NO, you can use this method to show the price consent UI at a later time + /// that is more appropriate for your app. If there is no pending price consent, this method will do + /// nothing. + @available(iOS 13.4, *) + func showPriceConsentIfNeeded() + #endif +} + +/// The default PaymentQueue that wraps SKPaymentQueue +public class DefaultPaymentQueue: NSObject, FLTPaymentQueueProtocol { + /// The wrapped SKPaymentQueue + private let queue: SKPaymentQueue + + private weak var _delegate: SKPaymentQueueDelegate? + + /// Initialize this wrapper with an SKPaymentQueue + public init(queue: SKPaymentQueue) { + self.queue = queue + } + + @objc(addPayment:) + public func add(_ payment: SKPayment) { + queue.add(payment) + } + + public func finish(_ transaction: SKPaymentTransaction) { + queue.finishTransaction(transaction) + } + + @objc(addTransactionObserver:) + public func add(_ observer: SKPaymentTransactionObserver) { + queue.add(observer) + } + + public func restoreCompletedTransactions() { + queue.restoreCompletedTransactions() + } + + public func restoreCompletedTransactions(withApplicationUsername username: String?) { + queue.restoreCompletedTransactions(withApplicationUsername: username) + } + + @available(iOS 13.0, macOS 10.15, watchOS 6.2, *) + public var delegate: SKPaymentQueueDelegate? { + get { queue.delegate } + set { _delegate = newValue } + } + + public var transactions: [SKPaymentTransaction] { + get { queue.transactions } + set {} + } + + @available(iOS 13.0, *) + public var storefront: SKStorefront? { + get { queue.storefront } + set {} + } + + #if os(iOS) + @available(iOS 14.0, *) + public func presentCodeRedemptionSheet() { + queue.presentCodeRedemptionSheet() + } + + @available(iOS 13.4, *) + public func showPriceConsentIfNeeded() { + queue.showPriceConsentIfNeeded() + } + #endif +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTRequestHandlerProtocol.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTRequestHandlerProtocol.swift new file mode 100644 index 000000000000..bab3b2423360 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTRequestHandlerProtocol.swift @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +/// A protocol that wraps SKRequest. +@objc public protocol FLTRequestHandlerProtocol: NSObjectProtocol { + /// Wrapper for SKRequest's start + /// https://developer.apple.com/documentation/storekit/skrequest/1385534-start + func startProductRequest(completionHandler: @escaping (SKProductsResponse?, Error?) -> Void) +} + +/// The default request handler that wraps FIAPRequestHandler +public class DefaultRequestHandler: NSObject, FLTRequestHandlerProtocol { + /// The wrapped FIAPRequestHandler + private let handler: FIAPRequestHandler + + /// Initialize this wrapper with an instance of FIAPRequestHandler + public init(requestHandler: FIAPRequestHandler) { + self.handler = requestHandler + } + + public func startProductRequest( + completionHandler: @escaping (SKProductsResponse?, Error?) -> Void + ) { + handler.startProductRequest(completionHandler: completionHandler) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTTransactionCacheProtocol.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTTransactionCacheProtocol.swift new file mode 100644 index 000000000000..d1b23dfad884 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/Protocols/FLTTransactionCacheProtocol.swift @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +/// A protocol that defines a cache of all transactions, both completed and in progress. +@objc public protocol FLTTransactionCacheProtocol: NSObjectProtocol { + /// Adds objects to the transaction cache. + /// + /// If the cache already contains an array of objects on the specified key, the supplied + /// array will be appended to the existing array. + func add(_ objects: [Any], for key: TransactionCacheKey) + + /// Gets the array of objects stored at the given key. + /// + /// If there are no objects associated with the given key an empty array is returned. + func getObjectsFor(_ key: TransactionCacheKey) -> [Any] + + /// Removes all objects from the transaction cache. + func clear() +} + +/// The default transaction cache that wraps FIATransactionCache +public class DefaultTransactionCache: NSObject, FLTTransactionCacheProtocol { + /// The wrapped FIATransactionCache + private let cache: FIATransactionCache + + /// Initialize this wrapper with an FIATransactionCache + public init(cache: FIATransactionCache) { + self.cache = cache + } + + public func add(_ objects: [Any], for key: TransactionCacheKey) { + cache.add(objects, for: key) + } + + public func getObjectsFor(_ key: TransactionCacheKey) -> [Any] { + return cache.getObjectsFor(key) + } + + public func clear() { + cache.clear() + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAObjectTranslator.m deleted file mode 100644 index cefdf2d2adad..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAObjectTranslator.m +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIAObjectTranslator.h" - -#pragma mark - SKProduct Coders - -@implementation FIAObjectTranslator - -+ (NSDictionary *)getMapFromSKProduct:(SKProduct *)product { - if (!product) { - return nil; - } - return @{ - @"discounts" : [FIAObjectTranslator getMapArrayFromSKProductDiscounts:product.discounts], - @"introductoryPrice" : - [FIAObjectTranslator getMapFromSKProductDiscount:product.introductoryPrice] - ?: [NSNull null], - @"localizedDescription" : product.localizedDescription ?: [NSNull null], - @"localizedTitle" : product.localizedTitle ?: [NSNull null], - @"productIdentifier" : product.productIdentifier ?: [NSNull null], - @"price" : product.price.description ?: [NSNull null], - @"subscriptionGroupIdentifier" : product.subscriptionGroupIdentifier ?: [NSNull null], - @"subscriptionPeriod" : - [FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:product.subscriptionPeriod] - ?: [NSNull null], - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:product.priceLocale] ?: [NSNull null], - }; -} - -+ (NSDictionary *)getMapFromSKProductSubscriptionPeriod:(SKProductSubscriptionPeriod *)period { - if (!period) { - return nil; - } - return @{@"numberOfUnits" : @(period.numberOfUnits), @"unit" : @(period.unit)}; -} - -+ (nonnull NSArray *)getMapArrayFromSKProductDiscounts: - (nonnull NSArray *)productDiscounts { - NSMutableArray *discountsMapArray = [NSMutableArray arrayWithCapacity:productDiscounts.count]; - - for (SKProductDiscount *productDiscount in productDiscounts) { - [discountsMapArray addObject:[FIAObjectTranslator getMapFromSKProductDiscount:productDiscount]]; - } - - return discountsMapArray; -} - -+ (NSDictionary *)getMapFromSKProductDiscount:(SKProductDiscount *)discount { - if (!discount) { - return nil; - } - return @{ - @"identifier" : discount.identifier ?: [NSNull null], - @"numberOfPeriods" : @(discount.numberOfPeriods), - @"paymentMode" : @(discount.paymentMode), - @"price" : discount.price.description ?: [NSNull null], - @"subscriptionPeriod" : - [FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:discount.subscriptionPeriod] - ?: [NSNull null], - @"type" : @(discount.type), - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:discount.priceLocale] ?: [NSNull null], - }; -} - -+ (NSDictionary *)getMapFromSKProductsResponse:(SKProductsResponse *)productResponse { - if (!productResponse) { - return nil; - } - NSMutableArray *productsMapArray = - [NSMutableArray arrayWithCapacity:productResponse.products.count]; - for (SKProduct *product in productResponse.products) { - [productsMapArray addObject:[FIAObjectTranslator getMapFromSKProduct:product]]; - } - return @{ - @"products" : productsMapArray, - @"invalidProductIdentifiers" : productResponse.invalidProductIdentifiers ?: @[] - }; -} - -+ (NSDictionary *)getMapFromSKPayment:(SKPayment *)payment { - if (!payment) { - return nil; - } - return @{ - @"applicationUsername" : payment.applicationUsername ?: [NSNull null], - @"productIdentifier" : payment.productIdentifier ?: [NSNull null], - @"quantity" : @(payment.quantity), - @"requestData" : payment.requestData ? [[NSString alloc] initWithData:payment.requestData - encoding:NSUTF8StringEncoding] - : [NSNull null], - @"simulatesAskToBuyInSandbox" : @(payment.simulatesAskToBuyInSandbox), - }; -} - -// This intentionally only exposes fields that there has been a demonstrated -// need for; see discussion in https://github.com/flutter/plugins/pull/3897. -+ (NSDictionary *)getMapFromNSLocale:(NSLocale *)locale { - if (!locale) { - return nil; - } - return @{ - @"currencySymbol" : locale.currencySymbol ?: [NSNull null], - @"currencyCode" : locale.currencyCode ?: [NSNull null], - @"countryCode" : locale.countryCode ?: [NSNull null], - }; -} - -+ (SKMutablePayment *)getSKMutablePaymentFromMap:(NSDictionary *)map { - if (!map) { - return nil; - } - SKMutablePayment *payment = [[SKMutablePayment alloc] init]; - payment.productIdentifier = map[@"productIdentifier"]; - NSString *utf8String = map[@"requestData"]; - payment.requestData = [utf8String dataUsingEncoding:NSUTF8StringEncoding]; - payment.quantity = [map[@"quantity"] integerValue]; - payment.applicationUsername = map[@"applicationUsername"]; - payment.simulatesAskToBuyInSandbox = [map[@"simulatesAskToBuyInSandbox"] boolValue]; - return payment; -} - -+ (NSDictionary *)getMapFromSKPaymentTransaction:(SKPaymentTransaction *)transaction { - if (!transaction) { - return nil; - } - return @{ - @"error" : [FIAObjectTranslator getMapFromNSError:transaction.error] ?: [NSNull null], - @"payment" : transaction.payment ? [FIAObjectTranslator getMapFromSKPayment:transaction.payment] - : [NSNull null], - @"originalTransaction" : transaction.originalTransaction - ? [FIAObjectTranslator getMapFromSKPaymentTransaction:transaction.originalTransaction] - : [NSNull null], - @"transactionTimeStamp" : transaction.transactionDate - ? @(transaction.transactionDate.timeIntervalSince1970) - : [NSNull null], - @"transactionIdentifier" : transaction.transactionIdentifier ?: [NSNull null], - @"transactionState" : @(transaction.transactionState) - }; -} - -+ (NSDictionary *)getMapFromNSError:(NSError *)error { - if (!error) { - return nil; - } - - return @{ - @"code" : @(error.code), - @"domain" : error.domain ?: @"", - @"userInfo" : [FIAObjectTranslator encodeNSErrorUserInfo:error.userInfo] - }; -} - -+ (id)encodeNSErrorUserInfo:(id)value { - if ([value isKindOfClass:[NSError class]]) { - return [FIAObjectTranslator getMapFromNSError:value]; - } else if ([value isKindOfClass:[NSURL class]]) { - return [value absoluteString]; - } else if ([value isKindOfClass:[NSNumber class]]) { - return value; - } else if ([value isKindOfClass:[NSString class]]) { - return value; - } else if ([value isKindOfClass:[NSArray class]]) { - NSMutableArray *errors = [NSMutableArray arrayWithCapacity:((NSArray *)value).count]; - for (id error in value) { - [errors addObject:[FIAObjectTranslator encodeNSErrorUserInfo:error]]; - } - return errors; - } else if ([value isKindOfClass:[NSDictionary class]]) { - NSMutableDictionary *errors = - [NSMutableDictionary dictionaryWithCapacity:((NSDictionary *)value).count]; - for (id key in value) { - errors[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value[key]]; - } - return errors; - } else { - return [NSString - stringWithFormat: - @"Unable to encode native userInfo object of type %@ to map. Please submit an issue at " - @"https://github.com/flutter/flutter/issues/new with the title " - @"\"[in_app_purchase_storekit] " - @"Unable to encode userInfo of type %@\" and add reproduction steps and the error " - @"details in " - @"the description field.", - [value class], [value class]]; - } -} - -+ (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront { - if (!storefront) { - return nil; - } - - return @{@"countryCode" : storefront.countryCode, @"identifier" : storefront.identifier}; -} - -+ (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront - andSKPaymentTransaction:(SKPaymentTransaction *)transaction { - if (!storefront || !transaction) { - return nil; - } - - return @{ - @"storefront" : [FIAObjectTranslator getMapFromSKStorefront:storefront], - @"transaction" : [FIAObjectTranslator getMapFromSKPaymentTransaction:transaction] - }; -} - -+ (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map - withError:(NSString **)error { - if (!map || map.count <= 0) { - return nil; - } - - NSString *identifier = map[@"identifier"]; - NSString *keyIdentifier = map[@"keyIdentifier"]; - NSString *nonce = map[@"nonce"]; - NSString *signature = map[@"signature"]; - NSNumber *timestamp = map[@"timestamp"]; - - if (!identifier || ![identifier isKindOfClass:NSString.class] || - [identifier isEqualToString:@""]) { - if (error) { - *error = @"When specifying a payment discount the 'identifier' field is mandatory."; - } - return nil; - } - - if (!keyIdentifier || ![keyIdentifier isKindOfClass:NSString.class] || - [keyIdentifier isEqualToString:@""]) { - if (error) { - *error = @"When specifying a payment discount the 'keyIdentifier' field is mandatory."; - } - return nil; - } - - if (!nonce || ![nonce isKindOfClass:NSString.class] || [nonce isEqualToString:@""]) { - if (error) { - *error = @"When specifying a payment discount the 'nonce' field is mandatory."; - } - return nil; - } - - if (!signature || ![signature isKindOfClass:NSString.class] || [signature isEqualToString:@""]) { - if (error) { - *error = @"When specifying a payment discount the 'signature' field is mandatory."; - } - return nil; - } - - if (!timestamp || ![timestamp isKindOfClass:NSNumber.class] || [timestamp longLongValue] <= 0) { - if (error) { - *error = @"When specifying a payment discount the 'timestamp' field is mandatory."; - } - return nil; - } - - SKPaymentDiscount *discount = - [[SKPaymentDiscount alloc] initWithIdentifier:identifier - keyIdentifier:keyIdentifier - nonce:[[NSUUID alloc] initWithUUIDString:nonce] - signature:signature - timestamp:timestamp]; - - return discount; -} - -+ (nullable FIASKPaymentTransactionMessage *)convertTransactionToPigeon: - (nullable SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)) { - if (!transaction) { - return nil; - } - return [FIASKPaymentTransactionMessage - makeWithPayment:[self convertPaymentToPigeon:transaction.payment] - transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] - originalTransaction:transaction.originalTransaction - ? [self convertTransactionToPigeon:transaction.originalTransaction] - : nil - transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate - timeIntervalSince1970]] - transactionIdentifier:transaction.transactionIdentifier - error:[self convertSKErrorToPigeon:transaction.error]]; -} - -+ (nullable FIASKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error { - if (!error) { - return nil; - } - - NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:error.userInfo.count]; - for (NSErrorUserInfoKey key in error.userInfo) { - id value = error.userInfo[key]; - userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; - } - - return [FIASKErrorMessage makeWithCode:error.code domain:error.domain userInfo:userInfo]; -} - -+ (FIASKPaymentTransactionStateMessage)convertTransactionStateToPigeon: - (SKPaymentTransactionState)state { - switch (state) { - case SKPaymentTransactionStatePurchasing: - return FIASKPaymentTransactionStateMessagePurchasing; - case SKPaymentTransactionStatePurchased: - return FIASKPaymentTransactionStateMessagePurchased; - case SKPaymentTransactionStateFailed: - return FIASKPaymentTransactionStateMessageFailed; - case SKPaymentTransactionStateRestored: - return FIASKPaymentTransactionStateMessageRestored; - case SKPaymentTransactionStateDeferred: - return FIASKPaymentTransactionStateMessageDeferred; - } -} - -+ (nullable FIASKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment - API_AVAILABLE(ios(12.2)) { - if (!payment) { - return nil; - } - return [FIASKPaymentMessage - makeWithProductIdentifier:payment.productIdentifier - applicationUsername:payment.applicationUsername - requestData:[[NSString alloc] initWithData:payment.requestData - encoding:NSUTF8StringEncoding] - quantity:payment.quantity - simulatesAskToBuyInSandbox:payment.simulatesAskToBuyInSandbox - paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]]; -} - -+ (nullable FIASKPaymentDiscountMessage *)convertPaymentDiscountToPigeon: - (nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)) { - if (!discount) { - return nil; - } - return [FIASKPaymentDiscountMessage makeWithIdentifier:discount.identifier - keyIdentifier:discount.keyIdentifier - nonce:[discount.nonce UUIDString] - signature:discount.signature - timestamp:[discount.timestamp intValue]]; -} - -+ (nullable FIASKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront - API_AVAILABLE(ios(13.0)) { - if (!storefront) { - return nil; - } - return [FIASKStorefrontMessage makeWithCountryCode:storefront.countryCode - identifier:storefront.identifier]; -} - -+ (nullable FIASKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon: - (nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)) { - if (!period) { - return nil; - } - - FIASKSubscriptionPeriodUnitMessage unit; - switch (period.unit) { - case SKProductPeriodUnitDay: - unit = FIASKSubscriptionPeriodUnitMessageDay; - break; - case SKProductPeriodUnitWeek: - unit = FIASKSubscriptionPeriodUnitMessageWeek; - break; - case SKProductPeriodUnitMonth: - unit = FIASKSubscriptionPeriodUnitMessageMonth; - break; - case SKProductPeriodUnitYear: - unit = FIASKSubscriptionPeriodUnitMessageYear; - break; - } - - return [FIASKProductSubscriptionPeriodMessage makeWithNumberOfUnits:period.numberOfUnits - unit:unit]; -} - -+ (nullable FIASKProductDiscountMessage *)convertProductDiscountToPigeon: - (nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)) { - if (!productDiscount) { - return nil; - } - - FIASKProductDiscountPaymentModeMessage paymentMode; - switch (productDiscount.paymentMode) { - case SKProductDiscountPaymentModeFreeTrial: - paymentMode = FIASKProductDiscountPaymentModeMessageFreeTrial; - break; - case SKProductDiscountPaymentModePayAsYouGo: - paymentMode = FIASKProductDiscountPaymentModeMessagePayAsYouGo; - break; - case SKProductDiscountPaymentModePayUpFront: - paymentMode = FIASKProductDiscountPaymentModeMessagePayUpFront; - break; - } - - FIASKProductDiscountTypeMessage type; - switch (productDiscount.type) { - case SKProductDiscountTypeIntroductory: - type = FIASKProductDiscountTypeMessageIntroductory; - break; - case SKProductDiscountTypeSubscription: - type = FIASKProductDiscountTypeMessageSubscription; - break; - } - - return [FIASKProductDiscountMessage - makeWithPrice:productDiscount.price.description - priceLocale:[self convertNSLocaleToPigeon:productDiscount.priceLocale] - numberOfPeriods:productDiscount.numberOfPeriods - paymentMode:paymentMode - subscriptionPeriod:[self convertSKProductSubscriptionPeriodToPigeon:productDiscount - .subscriptionPeriod] - identifier:productDiscount.identifier - type:type]; -} - -+ (nullable FIASKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale - API_AVAILABLE(ios(12.2)) { - if (!locale) { - return nil; - } - return [FIASKPriceLocaleMessage makeWithCurrencySymbol:locale.currencySymbol - currencyCode:locale.currencyCode - countryCode:locale.countryCode]; -} - -+ (nullable FIASKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product - API_AVAILABLE(ios(12.2)) { - if (!product) { - return nil; - } - - NSArray *skProductDiscounts = product.discounts; - NSMutableArray *pigeonProductDiscounts = - [NSMutableArray arrayWithCapacity:skProductDiscounts.count]; - - for (SKProductDiscount *productDiscount in skProductDiscounts) { - [pigeonProductDiscounts addObject:[self convertProductDiscountToPigeon:productDiscount]]; - }; - - return [FIASKProductMessage - makeWithProductIdentifier:product.productIdentifier - localizedTitle:product.localizedTitle - localizedDescription:product.localizedDescription - priceLocale:[self convertNSLocaleToPigeon:product.priceLocale] - subscriptionGroupIdentifier:product.subscriptionGroupIdentifier - price:product.price.description - subscriptionPeriod: - [self convertSKProductSubscriptionPeriodToPigeon:product.subscriptionPeriod] - introductoryPrice:[self convertProductDiscountToPigeon:product.introductoryPrice] - discounts:pigeonProductDiscounts]; -} - -+ (nullable FIASKProductsResponseMessage *)convertProductsResponseToPigeon: - (nullable SKProductsResponse *)productsResponse API_AVAILABLE(ios(12.2)) { - if (!productsResponse) { - return nil; - } - NSArray *skProducts = productsResponse.products; - NSMutableArray *pigeonProducts = - [NSMutableArray arrayWithCapacity:skProducts.count]; - - for (SKProduct *product in skProducts) { - [pigeonProducts addObject:[self convertProductToPigeon:product]]; - }; - - return [FIASKProductsResponseMessage - makeWithProducts:pigeonProducts - invalidProductIdentifiers:productsResponse.invalidProductIdentifiers ?: @[]]; -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.m deleted file mode 100644 index c9de9dd8052b..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.m +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.h" -#import "./include/in_app_purchase_storekit_objc/FIAObjectTranslator.h" - -@interface FIAPPaymentQueueDelegate () - -// The designated Flutter method channel that handles if a transaction should be continued -@property(nonatomic, strong, readonly) id callbackChannel; - -@end - -@implementation FIAPPaymentQueueDelegate - -- (id)initWithMethodChannel:(id)methodChannel { - self = [super init]; - if (self) { - _callbackChannel = methodChannel; - } - - return self; -} - -- (BOOL)paymentQueue:(SKPaymentQueue *)paymentQueue - shouldContinueTransaction:(SKPaymentTransaction *)transaction - inStorefront:(SKStorefront *)newStorefront { - // Default return value for this method is true (see - // https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate/3521328-paymentqueueshouldshowpriceconse?language=objc) - __block BOOL shouldContinue = YES; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [self.callbackChannel invokeMethod:@"shouldContinueTransaction" - arguments:[FIAObjectTranslator getMapFromSKStorefront:newStorefront - andSKPaymentTransaction:transaction] - result:^(id _Nullable result) { - // When result is a valid instance of NSNumber use it to determine - // if the transaction should continue. Otherwise use the default - // value. - if (result && [result isKindOfClass:[NSNumber class]]) { - shouldContinue = [(NSNumber *)result boolValue]; - } - - dispatch_semaphore_signal(semaphore); - }]; - - // The client should respond within 1 second otherwise continue - // with default value. - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)); - - return shouldContinue; -} - -#if TARGET_OS_IOS -- (BOOL)paymentQueueShouldShowPriceConsent:(SKPaymentQueue *)paymentQueue { - // Default return value for this method is true (see - // https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate/3521328-paymentqueueshouldshowpriceconse?language=objc) - __block BOOL shouldShowPriceConsent = YES; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [self.callbackChannel invokeMethod:@"shouldShowPriceConsent" - arguments:nil - result:^(id _Nullable result) { - // When result is a valid instance of NSNumber use it to determine - // if the transaction should continue. Otherwise use the default - // value. - if (result && [result isKindOfClass:[NSNumber class]]) { - shouldShowPriceConsent = [(NSNumber *)result boolValue]; - } - - dispatch_semaphore_signal(semaphore); - }]; - - // The client should respond within 1 second otherwise continue - // with default value. - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)); - - return shouldShowPriceConsent; -} -#endif - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPReceiptManager.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPReceiptManager.m deleted file mode 100644 index 6df9ea0fb793..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPReceiptManager.m +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIAPReceiptManager.h" - -#if TARGET_OS_OSX -#import -#else -#import -#endif -#import "./include/in_app_purchase_storekit_objc/FIAObjectTranslator.h" - -@interface FIAPReceiptManager () -// Gets the receipt file data from the location of the url. Can be nil if -// there is an error. This interface is defined so it can be stubbed for testing. -- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error; -// Gets the app store receipt url. Can be nil if -// there is an error. This property is defined so it can be stubbed for testing. -@property(nonatomic, readonly) NSURL *receiptURL; -@end - -@implementation FIAPReceiptManager - -- (NSString *)retrieveReceiptWithError:(FlutterError **)flutterError { - NSURL *receiptURL = self.receiptURL; - if (!receiptURL) { - return nil; - } - NSError *receiptError; - NSData *receipt = [self getReceiptData:receiptURL error:&receiptError]; - if (!receipt || receiptError) { - if (flutterError) { - NSDictionary *errorMap = [FIAObjectTranslator getMapFromNSError:receiptError]; - *flutterError = - [FlutterError errorWithCode:[NSString stringWithFormat:@"%@", errorMap[@"code"]] - message:errorMap[@"domain"] - details:errorMap[@"userInfo"]]; - } - return nil; - } - return [receipt base64EncodedStringWithOptions:kNilOptions]; -} - -- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error { - return [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:error]; -} - -- (NSURL *)receiptURL { - return [[NSBundle mainBundle] appStoreReceiptURL]; -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPRequestHandler.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPRequestHandler.m deleted file mode 100644 index b8300d5c8a88..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPRequestHandler.m +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIAPRequestHandler.h" -#import - -#pragma mark - Main Handler - -@interface FIAPRequestHandler () - -@property(nonatomic, copy) ProductRequestCompletion completion; -@property(nonatomic, strong) SKRequest *request; - -@end - -@implementation FIAPRequestHandler - -- (instancetype)initWithRequest:(SKRequest *)request { - self = [super init]; - if (self) { - self.request = request; - request.delegate = self; - } - return self; -} - -- (void)startProductRequestWithCompletionHandler:(ProductRequestCompletion)completion { - self.completion = completion; - [self.request start]; -} - -- (void)productsRequest:(SKProductsRequest *)request - didReceiveResponse:(SKProductsResponse *)response { - if (self.completion) { - self.completion(response, nil); - // set the completion to nil here so self.completion won't be triggered again in - // requestDidFinish for SKProductRequest. - self.completion = nil; - } -} - -- (void)requestDidFinish:(SKRequest *)request { - if (self.completion) { - self.completion(nil, nil); - } -} - -- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { - if (self.completion) { - self.completion(nil, error); - } -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.m deleted file mode 100644 index 94f74f7ca910..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.m +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.h" -#import "./include/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.h" -#import "./include/in_app_purchase_storekit_objc/FIATransactionCache.h" - -@interface FIAPaymentQueueHandler () - -/// The SKPaymentQueue instance connected to the App Store and responsible for processing -/// transactions. -@property(nonatomic, strong) SKPaymentQueue *queue; - -/// Callback method that is called each time the App Store indicates transactions are updated. -@property(nonatomic, nullable, copy) TransactionsUpdated transactionsUpdated; - -/// Callback method that is called each time the App Store indicates transactions are removed. -@property(nonatomic, nullable, copy) TransactionsRemoved transactionsRemoved; - -/// Callback method that is called each time the App Store indicates transactions failed to restore. -@property(nonatomic, nullable, copy) RestoreTransactionFailed restoreTransactionFailed; - -/// Callback method that is called each time the App Store indicates restoring of transactions has -/// finished. -@property(nonatomic, nullable, copy) - RestoreCompletedTransactionsFinished paymentQueueRestoreCompletedTransactionsFinished; - -/// Callback method that is called each time an in-app purchase has been initiated from the App -/// Store. -@property(nonatomic, nullable, copy) ShouldAddStorePayment shouldAddStorePayment; - -/// Callback method that is called each time the App Store indicates downloads are updated. -@property(nonatomic, nullable, copy) UpdatedDownloads updatedDownloads; - -/// The transaction cache responsible for caching transactions. -/// -/// Keeps track of transactions that arrive when the Flutter client is not -/// actively observing for transactions. -@property(nonatomic, strong, nonnull) FIATransactionCache *transactionCache; - -/// Indicates if the Flutter client is observing transactions. -/// -/// When the client is not observing, transactions are cached and send to the -/// client as soon as it starts observing. The Flutter client can start -/// observing by sending a startObservingPaymentQueue message and stop by -/// sending a stopObservingPaymentQueue message. -@property(atomic, assign, readwrite, getter=isObservingTransactions) BOOL observingTransactions; - -@end - -@implementation FIAPaymentQueueHandler - -@synthesize delegate; - -- (instancetype)initWithQueue:(nonnull id)queue - transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated - transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved - restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed - restoreCompletedTransactionsFinished: - (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished - shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads { - return [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:transactionsUpdated - transactionRemoved:transactionsRemoved - restoreTransactionFailed:restoreTransactionFailed - restoreCompletedTransactionsFinished:restoreCompletedTransactionsFinished - shouldAddStorePayment:shouldAddStorePayment - updatedDownloads:updatedDownloads - transactionCache:[[DefaultTransactionCache alloc] init]]; -} - -- (instancetype)initWithQueue:(nonnull id)queue - transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated - transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved - restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed - restoreCompletedTransactionsFinished: - (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished - shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads - transactionCache:(nonnull id)transactionCache { - self = [super init]; - if (self) { - _queue = queue; - _transactionsUpdated = transactionsUpdated; - _transactionsRemoved = transactionsRemoved; - _restoreTransactionFailed = restoreTransactionFailed; - _paymentQueueRestoreCompletedTransactionsFinished = restoreCompletedTransactionsFinished; - _shouldAddStorePayment = shouldAddStorePayment; - _updatedDownloads = updatedDownloads; - _transactionCache = transactionCache; - - [_queue addTransactionObserver:self]; - queue.delegate = self.delegate; - } - return self; -} - -- (void)startObservingPaymentQueue { - self.observingTransactions = YES; - - [self processCachedTransactions]; -} - -- (void)stopObservingPaymentQueue { - // When the client stops observing transaction, the transaction observer is - // not removed from the SKPaymentQueue. The FIAPaymentQueueHandler will cache - // trasnactions in memory when the client is not observing, allowing the app - // to process these transactions if it starts observing again during the same - // lifetime of the app. - // - // If the app is killed, cached transactions will be removed from memory; - // however, the App Store will re-deliver the transactions as soon as the app - // is started again, since the cached transactions have not been acknowledged - // by the client (by sending the `finishTransaction` message). - self.observingTransactions = NO; -} - -- (void)processCachedTransactions { - NSArray *cachedObjects = - [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]; - if (cachedObjects.count != 0) { - self.transactionsUpdated(cachedObjects); - } - - cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]; - if (cachedObjects.count != 0) { - self.updatedDownloads(cachedObjects); - } - - cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]; - if (cachedObjects.count != 0) { - self.transactionsRemoved(cachedObjects); - } - - [self.transactionCache clear]; -} - -- (BOOL)addPayment:(SKPayment *)payment { - for (SKPaymentTransaction *transaction in self.queue.transactions) { - if ([transaction.payment.productIdentifier isEqualToString:payment.productIdentifier]) { - return NO; - } - } - [self.queue addPayment:payment]; - return YES; -} - -- (void)finishTransaction:(SKPaymentTransaction *)transaction { - [self.queue finishTransaction:transaction]; -} - -- (void)restoreTransactions:(nullable NSString *)applicationName { - if (applicationName) { - [self.queue restoreCompletedTransactionsWithApplicationUsername:applicationName]; - } else { - [self.queue restoreCompletedTransactions]; - } -} - -#if TARGET_OS_IOS -- (void)presentCodeRedemptionSheet { - if (@available(iOS 14, *)) { - [self.queue presentCodeRedemptionSheet]; - } else { - NSLog(@"presentCodeRedemptionSheet is only available on iOS 14 or newer"); - } -} -#endif - -#if TARGET_OS_IOS -- (void)showPriceConsentIfNeeded API_AVAILABLE(ios(13.4)) { - [self.queue showPriceConsentIfNeeded]; -} -#endif - -#pragma mark - observing - -// Sent when the transaction array has changed (additions or state changes). Client should check -// state of transactions and finish as appropriate. -- (void)paymentQueue:(SKPaymentQueue *)queue - updatedTransactions:(NSArray *)transactions { - if (!self.observingTransactions) { - [_transactionCache addObjects:transactions forKey:TransactionCacheKeyUpdatedTransactions]; - return; - } - - // notify dart through callbacks. - self.transactionsUpdated(transactions); -} - -// Sent when transactions are removed from the queue (via finishTransaction:). -- (void)paymentQueue:(SKPaymentQueue *)queue - removedTransactions:(NSArray *)transactions { - if (!self.observingTransactions) { - [_transactionCache addObjects:transactions forKey:TransactionCacheKeyRemovedTransactions]; - return; - } - self.transactionsRemoved(transactions); -} - -// Sent when an error is encountered while adding transactions from the user's purchase history back -// to the queue. -- (void)paymentQueue:(SKPaymentQueue *)queue - restoreCompletedTransactionsFailedWithError:(NSError *)error { - self.restoreTransactionFailed(error); -} - -// Sent when all transactions from the user's purchase history have successfully been added back to -// the queue. -- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { - self.paymentQueueRestoreCompletedTransactionsFinished(); -} - -// Sent when the download state has changed. -- (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { - if (!self.observingTransactions) { - [_transactionCache addObjects:downloads forKey:TransactionCacheKeyUpdatedDownloads]; - return; - } - self.updatedDownloads(downloads); -} - -// Sent when a user initiates an IAP buy from the App Store -- (BOOL)paymentQueue:(SKPaymentQueue *)queue - shouldAddStorePayment:(SKPayment *)payment - forProduct:(SKProduct *)product { - return (self.shouldAddStorePayment(payment, product)); -} - -- (NSArray *)getUnfinishedTransactions { - return self.queue.transactions; -} - -- (SKStorefront *)storefront API_AVAILABLE(ios(13.0)) { - return self.queue.storefront; -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIATransactionCache.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIATransactionCache.m deleted file mode 100644 index f0787bb1d957..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/FIATransactionCache.m +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/in_app_purchase_storekit_objc/FIATransactionCache.h" - -@interface FIATransactionCache () - -/// A NSMutableDictionary storing the objects that are cached. -@property(nonatomic, strong, nonnull) NSMutableDictionary *cache; - -@end - -@implementation FIATransactionCache - -- (instancetype)init { - self = [super init]; - if (self) { - self.cache = [[NSMutableDictionary alloc] init]; - } - - return self; -} - -- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key { - NSArray *cachedObjects = self.cache[@(key)]; - - self.cache[@(key)] = - cachedObjects ? [cachedObjects arrayByAddingObjectsFromArray:objects] : objects; -} - -- (NSArray *)getObjectsForKey:(TransactionCacheKey)key { - return self.cache[@(key)]; -} - -- (void)clear { - [self.cache removeAllObjects]; -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTMethodChannelProtocol.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTMethodChannelProtocol.m deleted file mode 100644 index bc8343c74ee1..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTMethodChannelProtocol.m +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "../include/in_app_purchase_storekit_objc/FLTMethodChannelProtocol.h" - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -@interface DefaultMethodChannel () -/// The wrapped FlutterMethodChannel -@property(nonatomic, strong) FlutterMethodChannel *channel; -@end - -@implementation DefaultMethodChannel - -- (instancetype)initWithChannel:(nonnull FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _channel = channel; - } - return self; -} - -- (void)invokeMethod:(nonnull NSString *)method arguments:(id _Nullable)arguments { - [self.channel invokeMethod:method arguments:arguments]; -} - -- (void)invokeMethod:(nonnull NSString *)method - arguments:(id _Nullable)arguments - result:(FlutterResult _Nullable)callback { - [self.channel invokeMethod:method arguments:arguments result:callback]; -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTPaymentQueueProtocol.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTPaymentQueueProtocol.m deleted file mode 100644 index 45181d5c1e4d..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTPaymentQueueProtocol.m +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "../include/in_app_purchase_storekit_objc/FLTPaymentQueueProtocol.h" - -@interface DefaultPaymentQueue () -/// The wrapped SKPaymentQueue -@property(nonatomic, strong) SKPaymentQueue *queue; -@end - -@implementation DefaultPaymentQueue - -@synthesize storefront; -@synthesize delegate; -@synthesize transactions; - -- (instancetype)initWithQueue:(SKPaymentQueue *)queue { - self = [super init]; - if (self) { - _queue = queue; - } - return self; -} - -- (void)addPayment:(SKPayment *_Nonnull)payment { - [self.queue addPayment:payment]; -} - -- (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction { - [self.queue finishTransaction:transaction]; -} - -- (void)addTransactionObserver:(nonnull id)observer { - [self.queue addTransactionObserver:observer]; -} - -- (void)restoreCompletedTransactions { - [self.queue restoreCompletedTransactions]; -} - -- (void)restoreCompletedTransactionsWithApplicationUsername:(nullable NSString *)username { - [self.queue restoreCompletedTransactionsWithApplicationUsername:username]; -} - -- (id)delegate API_AVAILABLE(ios(13.0), macos(10.15), watchos(6.2)) { - return self.queue.delegate; -} - -- (NSArray *)transactions API_AVAILABLE(ios(3.0), macos(10.7), - watchos(6.2)) { - return self.queue.transactions; -} - -- (SKStorefront *)storefront API_AVAILABLE(ios(13.0)) { - return self.queue.storefront; -} - -#if TARGET_OS_IOS -- (void)presentCodeRedemptionSheet API_AVAILABLE(ios(14.0))API_UNAVAILABLE(tvos, macos, watchos) { - [self.queue presentCodeRedemptionSheet]; -} -#endif - -#if TARGET_OS_IOS -- (void)showPriceConsentIfNeeded API_AVAILABLE(ios(13.4))API_UNAVAILABLE(tvos, macos, watchos) { - [self.queue showPriceConsentIfNeeded]; -} -#endif - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTRequestHandlerProtocol.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTRequestHandlerProtocol.m deleted file mode 100644 index 405669561f7f..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTRequestHandlerProtocol.m +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "../include/in_app_purchase_storekit_objc/FLTRequestHandlerProtocol.h" -#import -#import "../include/in_app_purchase_storekit_objc/FIAPRequestHandler.h" - -@interface DefaultRequestHandler () -/// The wrapped FIAPRequestHandler -@property(nonatomic, strong) FIAPRequestHandler *handler; -@end - -@implementation DefaultRequestHandler - -- (void)startProductRequestWithCompletionHandler:(nonnull ProductRequestCompletion)completion { - [self.handler startProductRequestWithCompletionHandler:completion]; -} - -- (nonnull instancetype)initWithRequestHandler:(nonnull FIAPRequestHandler *)handler { - self = [super init]; - if (self) { - _handler = handler; - } - return self; -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTTransactionCacheProtocol.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTTransactionCacheProtocol.m deleted file mode 100644 index 740bed4d84bf..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/Protocols/FLTTransactionCacheProtocol.m +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "../include/in_app_purchase_storekit_objc/FLTTransactionCacheProtocol.h" - -@interface DefaultTransactionCache () -/// The wrapped FIATransactionCache -@property(nonatomic, strong) FIATransactionCache *cache; -@end - -@implementation DefaultTransactionCache - -- (void)addObjects:(nonnull NSArray *)objects forKey:(TransactionCacheKey)key { - [self.cache addObjects:objects forKey:key]; -} - -- (void)clear { - [self.cache clear]; -} - -- (nonnull NSArray *)getObjectsForKey:(TransactionCacheKey)key { - return [self.cache getObjectsForKey:key]; -} - -- (nonnull instancetype)initWithCache:(nonnull FIATransactionCache *)cache { - self = [super init]; - if (self) { - _cache = cache; - } - return self; -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAObjectTranslator.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAObjectTranslator.h deleted file mode 100644 index aed900da37cf..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAObjectTranslator.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "messages.g.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIAObjectTranslator : NSObject - -// Converts an instance of SKProduct into a dictionary. -+ (NSDictionary *)getMapFromSKProduct:(SKProduct *)product; - -// Converts an instance of SKProductSubscriptionPeriod into a dictionary. -+ (NSDictionary *)getMapFromSKProductSubscriptionPeriod:(SKProductSubscriptionPeriod *)period - API_AVAILABLE(ios(11.2)); - -// Converts an instance of SKProductDiscount into a dictionary. -+ (NSDictionary *)getMapFromSKProductDiscount:(SKProductDiscount *)discount - API_AVAILABLE(ios(11.2)); - -// Converts an array of SKProductDiscount instances into an array of dictionaries. -+ (nonnull NSArray *)getMapArrayFromSKProductDiscounts: - (nonnull NSArray *)productDiscounts API_AVAILABLE(ios(12.2)); - -// Converts an instance of SKProductsResponse into a dictionary. -+ (NSDictionary *)getMapFromSKProductsResponse:(SKProductsResponse *)productResponse; - -// Converts an instance of SKPayment into a dictionary. -+ (NSDictionary *)getMapFromSKPayment:(SKPayment *)payment; - -// Converts an instance of NSLocale into a dictionary. -+ (NSDictionary *)getMapFromNSLocale:(NSLocale *)locale; - -// Creates an instance of the SKMutablePayment class based on the supplied dictionary. -+ (SKMutablePayment *)getSKMutablePaymentFromMap:(NSDictionary *)map; - -// Converts an instance of SKPaymentTransaction into a dictionary. -+ (NSDictionary *)getMapFromSKPaymentTransaction:(SKPaymentTransaction *)transaction; - -// Converts an instance of NSError into a dictionary. -+ (NSDictionary *)getMapFromNSError:(NSError *)error; - -// Converts an instance of SKStorefront into a dictionary. -+ (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront - API_AVAILABLE(ios(13), macos(10.15), watchos(6.2)); - -// Converts the supplied instances of SKStorefront and SKPaymentTransaction into a dictionary. -+ (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront - andSKPaymentTransaction:(SKPaymentTransaction *)transaction - API_AVAILABLE(ios(13), macos(10.15), watchos(6.2)); - -// Creates an instance of the SKPaymentDiscount class based on the supplied dictionary. -+ (nullable SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map - withError:(NSString *_Nullable *_Nullable)error - API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKPaymentTransactionMessage *)convertTransactionToPigeon: - (nullable SKPaymentTransaction *)transaction; - -+ (nullable FIASKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront - API_AVAILABLE(ios(13.0)); - -+ (nullable FIASKPaymentDiscountMessage *)convertPaymentDiscountToPigeon: - (nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment - API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error; - -+ (nullable FIASKProductsResponseMessage *)convertProductsResponseToPigeon: - (nullable SKProductsResponse *)payment; - -+ (nullable FIASKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product - API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKProductDiscountMessage *)convertProductDiscountToPigeon: - (nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale - API_AVAILABLE(ios(12.2)); - -+ (nullable FIASKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon: - (nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)); -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.h deleted file mode 100644 index a6823df30f97..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPPaymentQueueDelegate.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#if TARGET_OS_OSX -#import -#else -#import -#endif -#import -#import "FLTMethodChannelProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -API_AVAILABLE(ios(13), macos(10.15)) -API_UNAVAILABLE(tvos, watchos) -@interface FIAPPaymentQueueDelegate : NSObject -- (id)initWithMethodChannel:(id)methodChannel; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPReceiptManager.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPReceiptManager.h deleted file mode 100644 index 074a2127a687..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPReceiptManager.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class FlutterError; - -@interface FIAPReceiptManager : NSObject - -- (nullable NSString *)retrieveReceiptWithError:(FlutterError *_Nullable *_Nullable)error; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPRequestHandler.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPRequestHandler.h deleted file mode 100644 index 336526f6fe56..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPRequestHandler.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "FLTRequestHandlerProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIAPRequestHandler : NSObject - -- (instancetype)initWithRequest:(SKRequest *)request; -- (void)startProductRequestWithCompletionHandler:(ProductRequestCompletion)completion; - -@end - -// The default request handler that wraps FIAPRequestHandler -@interface DefaultRequestHandler : NSObject - -// Initialize this wrapper with an instance of FIAPRequestHandler -- (instancetype)initWithRequestHandler:(FIAPRequestHandler *)handler; -@end -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.h deleted file mode 100644 index e81fc94a4aa2..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIAPaymentQueueHandler.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "FIATransactionCache.h" -#import "FLTPaymentQueueHandlerProtocol.h" -#import "FLTPaymentQueueProtocol.h" -#import "FLTTransactionCacheProtocol.h" - -@class SKPaymentTransaction; - -NS_ASSUME_NONNULL_BEGIN - -@interface FIAPaymentQueueHandler - : NSObject -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIATransactionCache.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIATransactionCache.h deleted file mode 100644 index 4746ae454612..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FIATransactionCache.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, TransactionCacheKey) { - TransactionCacheKeyUpdatedDownloads, - TransactionCacheKeyUpdatedTransactions, - TransactionCacheKeyRemovedTransactions -}; - -@interface FIATransactionCache : NSObject - -/// Adds objects to the transaction cache. -/// -/// If the cache already contains an array of objects on the specified key, the supplied -/// array will be appended to the existing array. -- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key; - -/// Gets the array of objects stored at the given key. -/// -/// If there are no objects associated with the given key nil is returned. -- (NSArray *)getObjectsForKey:(TransactionCacheKey)key; - -/// Removes all objects from the transaction cache. -- (void)clear; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTMethodChannelProtocol.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTMethodChannelProtocol.h deleted file mode 100644 index aa20b8d0a6e3..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTMethodChannelProtocol.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -NS_ASSUME_NONNULL_BEGIN -/// A protocol that wraps FlutterMethodChannel. -@protocol FLTMethodChannelProtocol - -/// Invokes the specified Flutter method with the specified arguments, expecting -/// an asynchronous result. -- (void)invokeMethod:(NSString *)method arguments:(id _Nullable)arguments; - -/// Invokes the specified Flutter method with the specified arguments and specified callback -- (void)invokeMethod:(NSString *)method - arguments:(id _Nullable)arguments - result:(FlutterResult _Nullable)callback; - -@end - -/// The default method channel that wraps FlutterMethodChannel -@interface DefaultMethodChannel : NSObject - -/// Initialize this wrapper with a FlutterMethodChannel -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueHandlerProtocol.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueHandlerProtocol.h deleted file mode 100644 index 2743778cbe26..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueHandlerProtocol.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import "FIATransactionCache.h" -#import "FLTPaymentQueueProtocol.h" -#import "FLTTransactionCacheProtocol.h" - -NS_ASSUME_NONNULL_BEGIN -typedef void (^TransactionsUpdated)(NSArray *transactions); -typedef void (^TransactionsRemoved)(NSArray *transactions); -typedef void (^RestoreTransactionFailed)(NSError *error); -typedef void (^RestoreCompletedTransactionsFinished)(void); -typedef BOOL (^ShouldAddStorePayment)(SKPayment *payment, SKProduct *product); -typedef void (^UpdatedDownloads)(NSArray *downloads); - -/// A protocol that conforms to SKPaymentTransactionObserver and handles SKPaymentQueue methods -@protocol FLTPaymentQueueHandlerProtocol -/// An object that provides information needed to complete transactions. -@property(nonatomic, weak, nullable) id delegate API_AVAILABLE( - ios(13.0), macos(10.15), watchos(6.2)); -/// An object containing the location and unique identifier of an Apple App Store storefront. -@property(nonatomic, readonly, nullable) - SKStorefront *storefront API_AVAILABLE(ios(13.0), macos(10.15), watchos(6.2)); - -/// Creates a new FIAPaymentQueueHandler. -/// -/// The "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" -/// callbacks are only called while actively observing transactions. To start -/// observing transactions send the "startObservingPaymentQueue" message. -/// Sending the "stopObservingPaymentQueue" message will stop actively -/// observing transactions. When transactions are not observed they are cached -/// to the "transactionCache" and will be delivered via the -/// "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" -/// callbacks as soon as the "startObservingPaymentQueue" message arrives. -/// -/// Note: cached transactions that are not processed when the application is -/// killed will be delivered again by the App Store as soon as the application -/// starts again. -/// -/// @param queue The SKPaymentQueue instance connected to the App Store and -/// responsible for processing transactions. -/// @param transactionsUpdated Callback method that is called each time the App -/// Store indicates transactions are updated. -/// @param transactionsRemoved Callback method that is called each time the App -/// Store indicates transactions are removed. -/// @param restoreTransactionFailed Callback method that is called each time -/// the App Store indicates transactions failed -/// to restore. -/// @param restoreCompletedTransactionsFinished Callback method that is called -/// each time the App Store -/// indicates restoring of -/// transactions has finished. -/// @param shouldAddStorePayment Callback method that is called each time an -/// in-app purchase has been initiated from the -/// App Store. -/// @param updatedDownloads Callback method that is called each time the App -/// Store indicates downloads are updated. -/// @param transactionCache An empty [FIATransactionCache] instance that is -/// responsible for keeping track of transactions that -/// arrive when not actively observing transactions. -- (instancetype)initWithQueue:(id)queue - transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated - transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved - restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed - restoreCompletedTransactionsFinished: - (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished - shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads - transactionCache:(nonnull id)transactionCache; - -/// Can throw exceptions if the transaction type is purchasing, should always used in a @try block. -- (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction; - -/// Attempt to restore transactions. Require app store receipt url. -- (void)restoreTransactions:(nullable NSString *)applicationName; - -/// Displays a sheet that enables users to redeem subscription offer codes. -- (void)presentCodeRedemptionSheet API_UNAVAILABLE(tvos, macos, watchos); - -/// Return all transactions that are not marked as complete. -- (NSArray *)getUnfinishedTransactions; - -/// This method needs to be called before any other methods. -- (void)startObservingPaymentQueue; - -/// Call this method when the Flutter app is no longer listening -- (void)stopObservingPaymentQueue; - -/// Appends a payment to the SKPaymentQueue. -/// -/// @param payment Payment object to be added to the payment queue. -/// @return whether "addPayment" was successful. -- (BOOL)addPayment:(SKPayment *)payment; - -/// Displays the price consent sheet. -/// -/// The price consent sheet is only displayed when the following -/// is true: -/// - You have increased the price of the subscription in App Store Connect. -/// - The subscriber has not yet responded to a price consent query. -/// Otherwise the method has no effect. -- (void)showPriceConsentIfNeeded API_AVAILABLE(ios(13.4))API_UNAVAILABLE(tvos, macos, watchos); - -@end -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueProtocol.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueProtocol.h deleted file mode 100644 index f20383fc00fb..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTPaymentQueueProtocol.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol that wraps SKPaymentQueue -@protocol FLTPaymentQueueProtocol - -/// An object containing the location and unique identifier of an Apple App Store storefront. -@property(nonatomic, strong) SKStorefront *storefront API_AVAILABLE(ios(13.0)); - -/// A list of SKPaymentTransactions, which each represents a single transaction -@property(nonatomic, strong) NSArray *transactions API_AVAILABLE( - ios(3.0), macos(10.7), watchos(6.2)); - -/// An object that provides information needed to complete transactions. -@property(nonatomic, weak, nullable) id delegate API_AVAILABLE( - ios(13.0), macos(10.15), watchos(6.2)); - -/// Remove a finished (i.e. failed or completed) transaction from the queue. Attempting to finish a -/// purchasing transaction will throw an exception. -- (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction; - -/// Observers are not retained. The transactions array will only be synchronized with the server -/// while the queue has observers. This may require that the user authenticate. -- (void)addTransactionObserver:(id)observer; - -/// Add a payment to the server queue. The payment is copied to add an SKPaymentTransaction to the -/// transactions array. The same payment can be added multiple times to create multiple -/// transactions. -- (void)addPayment:(SKPayment *_Nonnull)payment; - -/// Will add completed transactions for the current user back to the queue to be re-completed. -- (void)restoreCompletedTransactions API_AVAILABLE(ios(3.0), macos(10.7), watchos(6.2), - visionos(1.0)); - -/// Will add completed transactions for the current user back to the queue to be re-completed. This -/// version requires an identifier to the user's account. -- (void)restoreCompletedTransactionsWithApplicationUsername:(nullable NSString *)username - API_AVAILABLE(ios(7.0), macos(10.9), watchos(6.2)); - -/// Call this method to have StoreKit present a sheet enabling the user to redeem codes provided by -/// your app. Only for iOS. -- (void)presentCodeRedemptionSheet API_AVAILABLE(ios(14.0))API_UNAVAILABLE(tvos, macos, watchos); - -/// If StoreKit has called your SKPaymentQueueDelegate's "paymentQueueShouldShowPriceConsent:" -/// method and you returned NO, you can use this method to show the price consent UI at a later time -/// that is more appropriate for your app. If there is no pending price consent, this method will do -/// nothing. -- (void)showPriceConsentIfNeeded API_AVAILABLE(ios(13.4))API_UNAVAILABLE(tvos, macos, watchos); - -@end - -/// The default PaymentQueue that wraps SKPaymentQueue -@interface DefaultPaymentQueue : NSObject - -/// Initialize this wrapper with an SKPaymentQueue -- (instancetype)initWithQueue:(SKPaymentQueue *)queue NS_DESIGNATED_INITIALIZER; - -/// The default initializer is unavailable, as it this must be initlai -- (instancetype)init NS_UNAVAILABLE; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTRequestHandlerProtocol.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTRequestHandlerProtocol.h deleted file mode 100644 index 60976a3c4521..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTRequestHandlerProtocol.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN -typedef void (^ProductRequestCompletion)(SKProductsResponse *_Nullable response, - NSError *_Nullable errror); -/// A protocol that wraps SKRequest. -@protocol FLTRequestHandlerProtocol - -/// Wrapper for SKRequest's start -/// https://developer.apple.com/documentation/storekit/skrequest/1385534-start -- (void)startProductRequestWithCompletionHandler:(ProductRequestCompletion)completion; -@end -NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTTransactionCacheProtocol.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTTransactionCacheProtocol.h deleted file mode 100644 index 02e426d08709..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit_objc/include/in_app_purchase_storekit_objc/FLTTransactionCacheProtocol.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#import "FIATransactionCache.h" - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol that defines a cache of all transactions, both completed and in progress. -@protocol FLTTransactionCacheProtocol - -/// Adds objects to the transaction cache. -/// -/// If the cache already contains an array of objects on the specified key, the supplied -/// array will be appended to the existing array. -- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key; - -/// Gets the array of objects stored at the given key. -/// -/// If there are no objects associated with the given key nil is returned. -- (NSArray *)getObjectsForKey:(TransactionCacheKey)key; - -/// Removes all objects from the transaction cache. -- (void)clear; -@end - -/// The default method channel that wraps FIATransactionCache -@interface DefaultTransactionCache : NSObject - -/// Initialize this wrapper with an FIATransactionCache -- (instancetype)initWithCache:(FIATransactionCache *)cache; -@end - -NS_ASSUME_NONNULL_END 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 4779b92e6028..445655412274 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 @@ -16,14 +16,13 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; - F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */; }; + F22BF91C2BC9B40B00713878 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22BF91B2BC9B40B00713878 /* Stubs.swift */; }; F22FD7A22CB080AE0006F28F /* StoreKit2TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22FD7A12CB080AE0006F28F /* StoreKit2TranslatorTests.swift */; }; F24C45E22C409D42000C6C72 /* InAppPurchasePluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24C45E12C409D41000C6C72 /* InAppPurchasePluginTests.swift */; }; F276940B2C47268700277144 /* ProductRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F276940A2C47268700277144 /* ProductRequestHandlerTests.swift */; }; F27694112C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */; }; F27694172C49DBCA00277144 /* FIATransactionCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */; }; F2858EE82C76A4230063A092 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = F6E5D5F926131C4800C68BED /* Configuration.storekit */; }; - F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD392C1256DD0067C78A /* Stubs.m */; }; F2D127492CB4A76D005FA2E5 /* InAppPurchaseStoreKit2PluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D127482CB4A76D005FA2E5 /* InAppPurchaseStoreKit2PluginTests.swift */; }; F2D5271A2C50627500C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527192C50627500C137C7 /* PaymentQueueTests.swift */; }; F2D5272A2C583C4A00C137C7 /* TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527292C583C4A00C137C7 /* TranslatorTests.swift */; }; @@ -56,7 +55,8 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 6458340B2CE3497379F6B389 /* 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 = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* in_app_purchase_storekit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = in_app_purchase_storekit; path = ../../darwin/in_app_purchase_storekit; sourceTree = ""; }; 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -72,20 +72,16 @@ A5279297219369C600FF69E6 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; A59001A421E69658004A3E5E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; + F22BF91B2BC9B40B00713878 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; F22FD7A12CB080AE0006F28F /* StoreKit2TranslatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StoreKit2TranslatorTests.swift; path = ../shared/RunnerTests/StoreKit2TranslatorTests.swift; sourceTree = SOURCE_ROOT; }; F24C45E12C409D41000C6C72 /* InAppPurchasePluginTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InAppPurchasePluginTests.swift; path = ../../shared/RunnerTests/InAppPurchasePluginTests.swift; sourceTree = ""; }; F276940A2C47268700277144 /* ProductRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProductRequestHandlerTests.swift; path = ../../shared/RunnerTests/ProductRequestHandlerTests.swift; sourceTree = ""; }; F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIAPPaymentQueueDeleteTests.swift; path = ../../shared/RunnerTests/FIAPPaymentQueueDeleteTests.swift; sourceTree = ""; }; F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIATransactionCacheTests.swift; path = ../../shared/RunnerTests/FIATransactionCacheTests.swift; sourceTree = ""; }; - F295AD362C1251300067C78A /* Stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; - F295AD392C1256DD0067C78A /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; F2D127482CB4A76D005FA2E5 /* InAppPurchaseStoreKit2PluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = InAppPurchaseStoreKit2PluginTests.swift; path = ../shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift; sourceTree = SOURCE_ROOT; }; F2D527192C50627500C137C7 /* PaymentQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PaymentQueueTests.swift; path = ../../shared/RunnerTests/PaymentQueueTests.swift; sourceTree = ""; }; F2D527292C583C4A00C137C7 /* TranslatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TranslatorTests.swift; path = ../../shared/RunnerTests/TranslatorTests.swift; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; - 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; - 78DABEA22ED26510000E7860 /* in_app_purchase_storekit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = in_app_purchase_storekit; path = ../../darwin/in_app_purchase_storekit; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -194,9 +190,7 @@ F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */, F24C45E12C409D41000C6C72 /* InAppPurchasePluginTests.swift */, F276940A2C47268700277144 /* ProductRequestHandlerTests.swift */, - F295AD392C1256DD0067C78A /* Stubs.m */, - F295AD362C1251300067C78A /* Stubs.h */, - F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */, + F22BF91B2BC9B40B00713878 /* Stubs.swift */, F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; @@ -290,7 +284,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; @@ -375,10 +369,9 @@ files = ( F2D5271A2C50627500C137C7 /* PaymentQueueTests.swift in Sources */, F24C45E22C409D42000C6C72 /* InAppPurchasePluginTests.swift in Sources */, - F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */, + F22BF91C2BC9B40B00713878 /* Stubs.swift in Sources */, F22FD7A22CB080AE0006F28F /* StoreKit2TranslatorTests.swift in Sources */, F276940B2C47268700277144 /* ProductRequestHandlerTests.swift in Sources */, - F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */, F2D5272A2C583C4A00C137C7 /* TranslatorTests.swift in Sources */, F27694172C49DBCA00277144 /* FIATransactionCacheTests.swift in Sources */, F2D127492CB4A76D005FA2E5 /* InAppPurchaseStoreKit2PluginTests.swift in Sources */, @@ -675,7 +668,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; 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 f436a2aa78a7..13d2a74c3232 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 @@ -2,4 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "Stubs.h" +#import +@import in_app_purchase_storekit_objc; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.h deleted file mode 120000 index 420bd56538d1..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.h +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/Stubs.h \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m deleted file mode 120000 index eee9d6b331a9..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/Stubs.m \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.swift new file mode 120000 index 000000000000..be62f5885aa8 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/Stubs.swift \ No newline at end of file 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 deleted file mode 100644 index 875f43e844a9..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -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) - } -} 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 ed0131983d17..e4280584bd86 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 @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -35,7 +35,6 @@ F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */; }; F2D527262C583C1C00C137C7 /* TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527252C583C1C00C137C7 /* TranslatorTests.swift */; }; - F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1B2905FC3200E3999D /* Stubs.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -82,8 +81,6 @@ 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 = ""; }; - 39C4797E13DFF5FCF1A87568 /* 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 = ""; }; - 46EFB01DD1BBB34F886C33A0 /* 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 = ""; }; 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; @@ -100,8 +97,8 @@ F2D527252C583C1C00C137C7 /* TranslatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TranslatorTests.swift; path = ../../shared/RunnerTests/TranslatorTests.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F79BDC152905FC0500E3999D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../../shared/RunnerTests/Info.plist; sourceTree = ""; }; - F79BDC1B2905FC3200E3999D /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; - F79BDC1F2906023C00E3999D /* Stubs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* in_app_purchase_storekit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = in_app_purchase_storekit; path = ../../../darwin/in_app_purchase_storekit; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -184,6 +181,8 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* in_app_purchase_storekit */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, @@ -216,9 +215,7 @@ F27694122C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift */, F24C45E32C409D87000C6C72 /* InAppPurchasePluginTests.swift */, F27694082C4724B200277144 /* ProductRequestHandlerTests.swift */, - F79BDC1F2906023C00E3999D /* Stubs.h */, F79BDC152905FC0500E3999D /* Info.plist */, - F79BDC1B2905FC3200E3999D /* Stubs.m */, F2C3A7402BD9D33D000D35F2 /* Stubs.swift */, F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */, ); @@ -401,7 +398,6 @@ files = ( F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */, F24C45E42C409D87000C6C72 /* InAppPurchasePluginTests.swift in Sources */, - F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */, F27694092C4724B200277144 /* ProductRequestHandlerTests.swift in Sources */, F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */, F2D527262C583C1C00C137C7 /* TranslatorTests.swift in Sources */, 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 f436a2aa78a7..4442204774ff 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 @@ -2,4 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "Stubs.h" +#import +@import in_app_purchase_storekit_objc; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.h b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.h deleted file mode 120000 index 420bd56538d1..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.h +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/Stubs.h \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.m deleted file mode 120000 index eee9d6b331a9..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/Stubs.m \ No newline at end of file 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 deleted file mode 100644 index 875f43e844a9..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -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) - } -} 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 120000 index 000000000000..be62f5885aa8 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/Stubs.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/Stubs.swift \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift index eb49205eb0bc..2d2d4b0bf7d1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift @@ -615,7 +615,7 @@ final class InAppPurchasePluginTests: XCTestCase { XCTFail("Expected a transaction but got nil") return } - XCTAssertEqual(result.payment, originalPigeon?.payment) + XCTAssertEqual(result.payment.productIdentifier, originalPigeon?.payment.productIdentifier) XCTAssertEqual(result.transactionIdentifier, originalPigeon?.transactionIdentifier) XCTAssertEqual(result.transactionState, originalPigeon?.transactionState) XCTAssertEqual(result.transactionTimeStamp, originalPigeon?.transactionTimeStamp) 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 deleted file mode 100644 index 331e84808ee9..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "FIATransactionCache.h" -#import "FLTMethodChannelProtocol.h" -#import "FLTPaymentQueueHandlerProtocol.h" -#import "FLTPaymentQueueProtocol.h" -#import "FLTRequestHandlerProtocol.h" -#import "FLTTransactionCacheProtocol.h" - -#if __has_include() -@import in_app_purchase_storekit; -#else -@import in_app_purchase_storekit_objc; -#endif - -NS_ASSUME_NONNULL_BEGIN -API_AVAILABLE(ios(11.2), macos(10.13.2)) -@interface SKProductSubscriptionPeriodStub : SKProductSubscriptionPeriod -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -API_AVAILABLE(ios(11.2), macos(10.13.2)) -@interface SKProductDiscountStub : SKProductDiscount -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -@interface SKProductStub : SKProduct -- (instancetype)initWithMap:(NSDictionary *)map; -- (instancetype)initWithProductID:(NSString *)productIdentifier; -@end - -@interface SKProductRequestStub : SKProductsRequest -@property(nonatomic, assign) BOOL returnError; -- (instancetype)initWithProductIdentifiers:(NSSet *)productIdentifiers; -- (instancetype)initWithFailureError:(NSError *)error; -@end - -@interface SKProductsResponseStub : SKProductsResponse -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -@interface SKPaymentQueueStub : SKPaymentQueue -@property(nonatomic, assign) SKPaymentTransactionState testState; -@property(nonatomic, strong, nullable) id observer; -@end - -@interface SKPaymentTransactionStub : SKPaymentTransaction -- (instancetype)initWithMap:(NSDictionary *)map; -- (instancetype)initWithState:(SKPaymentTransactionState)state; -- (instancetype)initWithState:(SKPaymentTransactionState)state payment:(SKPayment *)payment; -@end - -@interface SKMutablePaymentStub : SKMutablePayment -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -@interface NSErrorStub : NSError -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -@interface FIAPReceiptManagerStub : FIAPReceiptManager -// Indicates whether getReceiptData of this stub is going to return an error. -// Setting this to true will let getReceiptData give a basic NSError and return nil. -@property(nonatomic, assign) BOOL returnError; -// Indicates whether the receipt url will be nil. -@property(nonatomic, assign) BOOL returnNilURL; -@end - -@interface SKReceiptRefreshRequestStub : SKReceiptRefreshRequest -- (instancetype)initWithFailureError:(NSError *)error; -@end - -API_AVAILABLE(ios(13.0), macos(10.15)) -@interface SKStorefrontStub : SKStorefront -- (instancetype)initWithMap:(NSDictionary *)map; -@end - -// An interface representing a stubbed DefaultPaymentQueue -@interface PaymentQueueStub : NSObject - -// FLTPaymentQueueProtocol properties -@property(nonatomic, assign) SKPaymentTransactionState paymentState; -@property(nonatomic, strong, nullable) id observer; -@property(nonatomic, strong, readwrite) SKStorefront *storefront API_AVAILABLE(ios(13.0)); -@property(nonatomic, strong, readwrite) NSArray *transactions API_AVAILABLE( - ios(3.0), macos(10.7), watchos(6.2)); - -// Test Properties -@property(nonatomic, assign) - SKPaymentTransactionState testState; // Set this property to set a test Transaction state, then - // call addPayment to add it to the queue. -@property(nonatomic, strong, nonnull) - SKPaymentQueue *realQueue; // This is a reference to the real SKPaymentQueue - -// Stubs -@property(nonatomic, copy, nullable) void (^showPriceConsentIfNeededStub)(void); -@property(nonatomic, copy, nullable) void (^restoreTransactionsStub)(NSString *); -@property(nonatomic, copy, nullable) void (^startObservingPaymentQueueStub)(void); -@property(nonatomic, copy, nullable) void (^stopObservingPaymentQueueStub)(void); -@property(nonatomic, copy, nullable) void (^presentCodeRedemptionSheetStub)(void); -@property(nonatomic, copy, nullable) - NSArray * (^getUnfinishedTransactionsStub)(void); - -@end - -// An interface representing a stubbed DefaultTransactionCache -@interface TransactionCacheStub : NSObject - -// Stubs -@property(nonatomic, copy, nullable) NSArray * (^getObjectsForKeyStub)(TransactionCacheKey key); -@property(nonatomic, copy, nullable) void (^clearStub)(void); -@property(nonatomic, copy, nullable) void (^addObjectsStub)(NSArray *, TransactionCacheKey); - -@end - -// An interface representing a stubbed DefaultMethodChannel -@interface MethodChannelStub : NSObject - -// Stubs -@property(nonatomic, copy, nullable) void (^invokeMethodChannelStub) - (NSString *method, id _Nullable arguments); -@property(nonatomic, copy, nullable) void (^invokeMethodChannelWithResultsStub) - (NSString *method, id _Nullable arguments, FlutterResult _Nullable); - -@end - -// An interface representing a stubbed DefaultPaymentQueueHandler -@interface PaymentQueueHandlerStub - : NSObject - -// Stubs -@property(nonatomic, copy, nullable) BOOL (^addPaymentStub)(SKPayment *payment); -@property(nonatomic, copy, nullable) void (^showPriceConsentIfNeededStub)(void); -@property(nonatomic, copy, nullable) void (^stopObservingPaymentQueueStub)(void); -@property(nonatomic, copy, nullable) void (^startObservingPaymentQueueStub)(void); -@property(nonatomic, copy, nullable) void (^presentCodeRedemptionSheetStub)(void); -@property(nonatomic, copy, nullable) void (^restoreTransactions)(NSString *); -@property(nonatomic, copy, nullable) - NSArray * (^getUnfinishedTransactionsStub)(void); -@property(nonatomic, copy, nullable) void (^finishTransactionStub)(SKPaymentTransaction *); -@property(nonatomic, copy, nullable) void (^paymentQueueUpdatedTransactionsStub) - (SKPaymentQueue *, NSArray *); - -@end - -// An interface representing a stubbed DefaultRequestHandler -@interface RequestHandlerStub : NSObject - -// Stubs -@property(nonatomic, copy, nullable) void (^startProductRequestWithCompletionHandlerStub) - (ProductRequestCompletion); - -@end - -#if TARGET_OS_IOS -@interface FlutterPluginRegistrarStub : NSObject - -// Stubs -@property(nonatomic, weak, nullable) UIViewController *viewController; -@property(nonatomic, copy, nullable) void (^addApplicationDelegateStub)(NSObject *); -@property(nonatomic, copy, nullable) void (^addMethodCallDelegateStub) - (NSObject *, FlutterMethodChannel *); -@property(nonatomic, copy, nullable) NSString * (^lookupKeyForAssetStub)(NSString *); -@property(nonatomic, copy, nullable) NSString * (^lookupKeyForAssetFromPackageStub) - (NSString *, NSString *); -@property(nonatomic, copy, nullable) NSObject * (^messengerStub)(void); -@property(nonatomic, copy, nullable) void (^publishStub)(NSObject *); -@property(nonatomic, copy, nullable) void (^registerViewFactoryStub) - (NSObject *, NSString *); -@property(nonatomic, copy, nullable) NSObject * (^texturesStub)(void); -@property(nonatomic, copy, nullable) - void (^registerViewFactoryWithGestureRecognizersBlockingPolicyStub) - (NSObject *, NSString *, - FlutterPlatformViewGestureRecognizersBlockingPolicy); -@end -#endif - -@interface FlutterBinaryMessengerStub : NSObject - -// Stubs -@property(nonatomic, copy, nullable) void (^cleanUpConnectionStub)(FlutterBinaryMessengerConnection) - ; -@property(nonatomic, copy, nullable) void (^sendOnChannelMessageStub)(NSString *, NSData *); -@property(nonatomic, copy, nullable) void (^sendOnChannelMessageBinaryReplyStub) - (NSString *, NSData *, FlutterBinaryReply); -@property(nonatomic, copy, nullable) - FlutterBinaryMessengerConnection (^setMessageHandlerOnChannelBinaryMessageHandlerStub) - (NSString *, FlutterBinaryMessageHandler); -@end - -NS_ASSUME_NONNULL_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 deleted file mode 100644 index b3e0891d3f62..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "Stubs.h" -#import -#import - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -@implementation SKProductSubscriptionPeriodStub - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - [self setValue:map[@"numberOfUnits"] ?: @(0) forKey:@"numberOfUnits"]; - [self setValue:map[@"unit"] ?: @(0) forKey:@"unit"]; - } - return self; -} - -@end - -@implementation SKProductDiscountStub - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - [self setValue:[[NSDecimalNumber alloc] initWithString:map[@"price"]] ?: [NSNull null] - forKey:@"price"]; - NSLocale *locale = NSLocale.systemLocale; - [self setValue:locale ?: [NSNull null] forKey:@"priceLocale"]; - [self setValue:map[@"numberOfPeriods"] ?: @(0) forKey:@"numberOfPeriods"]; - SKProductSubscriptionPeriodStub *subscriptionPeriodSub = - [[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]]; - [self setValue:subscriptionPeriodSub forKey:@"subscriptionPeriod"]; - [self setValue:map[@"paymentMode"] ?: @(0) forKey:@"paymentMode"]; - [self setValue:map[@"identifier"] ?: [NSNull null] forKey:@"identifier"]; - [self setValue:map[@"type"] ?: @(0) forKey:@"type"]; - } - return self; -} - -@end - -@implementation SKProductStub - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - [self setValue:map[@"productIdentifier"] ?: [NSNull null] forKey:@"productIdentifier"]; - [self setValue:map[@"localizedDescription"] ?: [NSNull null] forKey:@"localizedDescription"]; - [self setValue:map[@"localizedTitle"] ?: [NSNull null] forKey:@"localizedTitle"]; - [self setValue:map[@"downloadable"] ?: @NO forKey:@"downloadable"]; - [self setValue:[[NSDecimalNumber alloc] initWithString:map[@"price"]] ?: [NSNull null] - forKey:@"price"]; - NSLocale *locale = NSLocale.systemLocale; - [self setValue:locale ?: [NSNull null] forKey:@"priceLocale"]; - [self setValue:map[@"downloadContentLengths"] ?: @(0) forKey:@"downloadContentLengths"]; - SKProductSubscriptionPeriodStub *period = - [[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]]; - [self setValue:period ?: [NSNull null] forKey:@"subscriptionPeriod"]; - SKProductDiscountStub *discount = - [[SKProductDiscountStub alloc] initWithMap:map[@"introductoryPrice"]]; - [self setValue:discount ?: [NSNull null] forKey:@"introductoryPrice"]; - [self setValue:map[@"subscriptionGroupIdentifier"] ?: [NSNull null] - forKey:@"subscriptionGroupIdentifier"]; - NSMutableArray *discounts = [[NSMutableArray alloc] init]; - for (NSDictionary *discountMap in map[@"discounts"]) { - [discounts addObject:[[SKProductDiscountStub alloc] initWithMap:discountMap]]; - } - [self setValue:discounts forKey:@"discounts"]; - } - return self; -} - -- (instancetype)initWithProductID:(NSString *)productIdentifier { - self = [super init]; - if (self) { - [self setValue:productIdentifier forKey:@"productIdentifier"]; - } - return self; -} - -@end - -@interface SKProductRequestStub () - -@property(nonatomic, strong) NSSet *identifers; -@property(nonatomic, strong) NSError *error; - -@end - -@implementation SKProductRequestStub - -- (instancetype)initWithProductIdentifiers:(NSSet *)productIdentifiers { - self = [super initWithProductIdentifiers:productIdentifiers]; - self.identifers = productIdentifiers; - return self; -} - -- (instancetype)initWithFailureError:(NSError *)error { - self = [super init]; - self.error = error; - return self; -} - -- (void)start { - NSMutableArray *productArray = [NSMutableArray new]; - for (NSString *identifier in self.identifers) { - [productArray addObject:@{@"productIdentifier" : identifier}]; - } - SKProductsResponseStub *response; - if (self.returnError) { - response = nil; - } else { - response = [[SKProductsResponseStub alloc] initWithMap:@{@"products" : productArray}]; - } - - if (self.error) { - [self.delegate request:self didFailWithError:self.error]; - } else { - [self.delegate productsRequest:self didReceiveResponse:response]; - } -} - -@end - -@implementation SKProductsResponseStub - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - NSMutableArray *products = [NSMutableArray new]; - for (NSDictionary *productMap in map[@"products"]) { - SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - [products addObject:product]; - } - [self setValue:products forKey:@"products"]; - } - return self; -} - -@end - -@interface SKPaymentQueueStub () - -@end - -@implementation SKPaymentQueueStub - -- (void)addTransactionObserver:(id)observer { - self.observer = observer; -} - -- (void)removeTransactionObserver:(id)observer { - self.observer = nil; -} - -- (void)addPayment:(SKPayment *)payment { - SKPaymentTransactionStub *transaction = - [[SKPaymentTransactionStub alloc] initWithState:self.testState payment:payment]; - [self.observer paymentQueue:self updatedTransactions:@[ transaction ]]; -} - -- (void)restoreCompletedTransactions { - if ([self.observer - respondsToSelector:@selector(paymentQueueRestoreCompletedTransactionsFinished:)]) { - [self.observer paymentQueueRestoreCompletedTransactionsFinished:self]; - } -} - -- (void)finishTransaction:(SKPaymentTransaction *)transaction { - if ([self.observer respondsToSelector:@selector(paymentQueue:removedTransactions:)]) { - [self.observer paymentQueue:self removedTransactions:@[ transaction ]]; - } -} - -@end - -@implementation SKPaymentTransactionStub { - SKPayment *_payment; -} - -- (instancetype)initWithID:(NSString *)identifier { - self = [super init]; - if (self) { - [self setValue:identifier forKey:@"transactionIdentifier"]; - } - return self; -} - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - [self setValue:map[@"transactionIdentifier"] forKey:@"transactionIdentifier"]; - [self setValue:map[@"transactionState"] forKey:@"transactionState"]; - if (![map[@"originalTransaction"] isKindOfClass:[NSNull class]] && - map[@"originalTransaction"]) { - [self setValue:[[SKPaymentTransactionStub alloc] initWithMap:map[@"originalTransaction"]] - forKey:@"originalTransaction"]; - } - [self setValue:map[@"error"] ? [[NSErrorStub alloc] initWithMap:map[@"error"]] : [NSNull null] - forKey:@"error"]; - [self setValue:[NSDate dateWithTimeIntervalSince1970:[map[@"transactionTimeStamp"] doubleValue]] - forKey:@"transactionDate"]; - } - return self; -} - -- (instancetype)initWithState:(SKPaymentTransactionState)state { - self = [super init]; - if (self) { - // Only purchased and restored transactions have transactionIdentifier: - // https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc - if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) { - [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; - } - [self setValue:@(state) forKey:@"transactionState"]; - } - return self; -} - -- (instancetype)initWithState:(SKPaymentTransactionState)state payment:(SKPayment *)payment { - self = [super init]; - if (self) { - // Only purchased and restored transactions have transactionIdentifier: - // https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc - if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) { - [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; - } - [self setValue:@(state) forKey:@"transactionState"]; - _payment = payment; - } - return self; -} - -- (SKPayment *)payment { - return _payment; -} - -@end - -@implementation NSErrorStub - -- (instancetype)initWithMap:(NSDictionary *)map { - return [self initWithDomain:[map objectForKey:@"domain"] - code:[[map objectForKey:@"code"] integerValue] - userInfo:[map objectForKey:@"userInfo"]]; -} - -@end - -@implementation FIAPReceiptManagerStub : FIAPReceiptManager - -- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error { - if (self.returnError) { - *error = [NSError errorWithDomain:@"test" - code:1 - userInfo:@{ - @"name" : @"test", - @"houseNr" : @5, - @"error" : [[NSError alloc] initWithDomain:@"internalTestDomain" - code:99 - userInfo:nil] - }]; - return nil; - } - NSString *originalString = [NSString stringWithFormat:@"test"]; - return [[NSData alloc] initWithBase64EncodedString:originalString options:kNilOptions]; -} - -- (NSURL *)receiptURL { - if (self.returnNilURL) { - return nil; - } else { - return [[NSBundle mainBundle] appStoreReceiptURL]; - } -} - -@end - -@implementation SKReceiptRefreshRequestStub { - NSError *_error; -} - -- (instancetype)initWithReceiptProperties:(NSDictionary *)properties { - self = [super initWithReceiptProperties:properties]; - return self; -} - -- (instancetype)initWithFailureError:(NSError *)error { - self = [super init]; - _error = error; - return self; -} - -- (void)start { - if (_error) { - [self.delegate request:self didFailWithError:_error]; - } else { - [self.delegate requestDidFinish:self]; - } -} - -@end - -@implementation SKStorefrontStub - -- (instancetype)initWithMap:(NSDictionary *)map { - self = [super init]; - if (self) { - // Set stub values - [self setValue:map[@"countryCode"] forKey:@"countryCode"]; - [self setValue:map[@"identifier"] forKey:@"identifier"]; - } - return self; -} -@end - -@implementation PaymentQueueStub - -@synthesize transactions; -@synthesize delegate; - -- (void)finishTransaction:(SKPaymentTransaction *)transaction { - [self.observer paymentQueue:self.realQueue removedTransactions:@[ transaction ]]; -} - -- (void)addPayment:(SKPayment *_Nonnull)payment { - SKPaymentTransactionStub *transaction = - [[SKPaymentTransactionStub alloc] initWithState:self.testState payment:payment]; - [self.observer paymentQueue:self.realQueue updatedTransactions:@[ transaction ]]; -} - -- (void)addTransactionObserver:(nonnull id)observer { - self.observer = observer; -} - -- (void)restoreCompletedTransactions { - [self.observer paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)self]; -} - -- (void)restoreCompletedTransactionsWithApplicationUsername:(nullable NSString *)username { - [self.observer paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)self]; -} - -- (NSArray *_Nonnull)getUnfinishedTransactions { - if (self.getUnfinishedTransactionsStub) { - return self.getUnfinishedTransactionsStub(); - } else { - return @[]; - } -} - -#if TARGET_OS_IOS -- (void)presentCodeRedemptionSheet { - if (self.presentCodeRedemptionSheetStub) { - self.presentCodeRedemptionSheetStub(); - } -} -#endif - -#if TARGET_OS_IOS -- (void)showPriceConsentIfNeeded { - if (self.showPriceConsentIfNeededStub) { - self.showPriceConsentIfNeededStub(); - } -} -#endif - -- (void)restoreTransactions:(nullable NSString *)applicationName { - if (self.restoreTransactionsStub) { - self.restoreTransactionsStub(applicationName); - } -} - -- (void)startObservingPaymentQueue { - if (self.startObservingPaymentQueueStub) { - self.startObservingPaymentQueueStub(); - } -} - -- (void)stopObservingPaymentQueue { - if (self.stopObservingPaymentQueueStub) { - self.stopObservingPaymentQueueStub(); - } -} - -- (void)removeTransactionObserver:(id)observer { - self.observer = nil; -} -@end - -@implementation MethodChannelStub -- (void)invokeMethod:(nonnull NSString *)method arguments:(id _Nullable)arguments { - if (self.invokeMethodChannelStub) { - self.invokeMethodChannelStub(method, arguments); - } -} - -- (void)invokeMethod:(nonnull NSString *)method - arguments:(id _Nullable)arguments - result:(FlutterResult _Nullable)callback { - if (self.invokeMethodChannelWithResultsStub) { - self.invokeMethodChannelWithResultsStub(method, arguments, callback); - } -} - -@end - -@implementation TransactionCacheStub -- (void)addObjects:(nonnull NSArray *)objects forKey:(TransactionCacheKey)key { - if (self.addObjectsStub) { - self.addObjectsStub(objects, key); - } -} - -- (void)clear { - if (self.clearStub) { - self.clearStub(); - } -} - -- (nonnull NSArray *)getObjectsForKey:(TransactionCacheKey)key { - if (self.getObjectsForKeyStub) { - return self.getObjectsForKeyStub(key); - } - return @[]; -} -@end - -@implementation PaymentQueueHandlerStub - -@synthesize storefront; -@synthesize delegate; - -- (void)paymentQueue:(nonnull SKPaymentQueue *)queue - updatedTransactions:(nonnull NSArray *)transactions { - if (self.paymentQueueUpdatedTransactionsStub) { - self.paymentQueueUpdatedTransactionsStub(queue, transactions); - } -} - -#if TARGET_OS_IOS -- (void)showPriceConsentIfNeeded { - if (self.showPriceConsentIfNeededStub) { - self.showPriceConsentIfNeededStub(); - } -} -#endif - -- (BOOL)addPayment:(nonnull SKPayment *)payment { - if (self.addPaymentStub) { - return self.addPaymentStub(payment); - } else { - return NO; - } -} - -- (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction { - if (self.finishTransactionStub) { - self.finishTransactionStub(transaction); - } -} - -- (nonnull NSArray *)getUnfinishedTransactions { - if (self.getUnfinishedTransactionsStub) { - return self.getUnfinishedTransactionsStub(); - } else { - return @[]; - } -} - -- (nonnull instancetype)initWithQueue:(nonnull id)queue - transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated - transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved - restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed - restoreCompletedTransactionsFinished: - (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished - shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads - transactionCache:(nonnull id)transactionCache { - return [[PaymentQueueHandlerStub alloc] init]; -} - -#if TARGET_OS_IOS -- (void)presentCodeRedemptionSheet { - if (self.presentCodeRedemptionSheetStub) { - self.presentCodeRedemptionSheetStub(); - } -} -#endif - -- (void)restoreTransactions:(nullable NSString *)applicationName { - if (self.restoreTransactions) { - self.restoreTransactions(applicationName); - } -} - -- (void)startObservingPaymentQueue { - if (self.startObservingPaymentQueueStub) { - self.startObservingPaymentQueueStub(); - } -} - -- (void)stopObservingPaymentQueue { - if (self.stopObservingPaymentQueueStub) { - self.stopObservingPaymentQueueStub(); - } -} - -- (nonnull instancetype)initWithQueue:(nonnull id)queue - transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated - transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved - restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed - restoreCompletedTransactionsFinished: - (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished - shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads { - return [[PaymentQueueHandlerStub alloc] init]; -} - -@end - -@implementation RequestHandlerStub - -- (void)startProductRequestWithCompletionHandler:(nonnull ProductRequestCompletion)completion { - if (self.startProductRequestWithCompletionHandlerStub) { - self.startProductRequestWithCompletionHandlerStub(completion); - } -} -@end - -/// This mock is only used in iOS tests -#if TARGET_OS_IOS - -// This FlutterPluginRegistrar is a protocol, so to make a stub it has to be implemented. -@implementation FlutterPluginRegistrarStub - -- (void)addApplicationDelegate:(nonnull NSObject *)delegate { - if (self.addApplicationDelegateStub) { - self.addApplicationDelegateStub(delegate); - } -} - -- (void)addMethodCallDelegate:(nonnull NSObject *)delegate - channel:(nonnull FlutterMethodChannel *)channel { - if (self.addMethodCallDelegateStub) { - self.addMethodCallDelegateStub(delegate, channel); - } -} - -- (nonnull NSString *)lookupKeyForAsset:(nonnull NSString *)asset { - if (self.lookupKeyForAssetStub) { - return self.lookupKeyForAssetStub(asset); - } - return nil; -} - -- (nonnull NSString *)lookupKeyForAsset:(nonnull NSString *)asset - fromPackage:(nonnull NSString *)package { - if (self.lookupKeyForAssetFromPackageStub) { - return self.lookupKeyForAssetFromPackageStub(asset, package); - } - return nil; -} - -- (nonnull NSObject *)messenger { - if (self.messengerStub) { - return self.messengerStub(); - } - return [[FlutterBinaryMessengerStub alloc] init]; // Or default behavior -} - -- (void)publish:(nonnull NSObject *)value { - if (self.publishStub) { - self.publishStub(value); - } -} - -- (void)registerViewFactory:(nonnull NSObject *)factory - withId:(nonnull NSString *)factoryId { - if (self.registerViewFactoryStub) { - self.registerViewFactoryStub(factory, factoryId); - } -} - -- (nonnull NSObject *)textures { - if (self.texturesStub) { - return self.texturesStub(); - } - return nil; -} - -- (void)registerViewFactory:(nonnull NSObject *)factory - withId:(nonnull NSString *)factoryId - gestureRecognizersBlockingPolicy: - (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy { - if (self.registerViewFactoryWithGestureRecognizersBlockingPolicyStub) { - self.registerViewFactoryWithGestureRecognizersBlockingPolicyStub( - factory, factoryId, gestureRecognizersBlockingPolicy); - } -} - -- (void)addSceneDelegate:(nonnull NSObject *)delegate { -} - -- (nullable NSObject *)valuePublishedByPlugin:(nonnull NSString *)pluginKey { - return nil; -} - -@end - -// This FlutterBinaryMessenger is a protocol, so to make a stub it has to be implemented. -@implementation FlutterBinaryMessengerStub -- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { - if (self.cleanUpConnectionStub) { - self.cleanUpConnectionStub(connection); - } -} - -- (void)sendOnChannel:(nonnull NSString *)channel message:(NSData *_Nullable)message { - if (self.sendOnChannelMessageStub) { - self.sendOnChannelMessageStub(channel, message); - } -} - -- (void)sendOnChannel:(nonnull NSString *)channel - message:(NSData *_Nullable)message - binaryReply:(FlutterBinaryReply _Nullable)callback { - if (self.sendOnChannelMessageBinaryReplyStub) { - self.sendOnChannelMessageBinaryReplyStub(channel, message, callback); - } -} - -- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel - binaryMessageHandler: - (FlutterBinaryMessageHandler _Nullable)handler { - if (self.setMessageHandlerOnChannelBinaryMessageHandlerStub) { - return self.setMessageHandlerOnChannelBinaryMessageHandlerStub(channel, handler); - } - return 0; -} -@end - -#endif diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.swift new file mode 100644 index 000000000000..ac48444cbaea --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.swift @@ -0,0 +1,600 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit +import StoreKitTest + +@testable import in_app_purchase_storekit + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +@available(iOS 11.2, macOS 10.13.2, *) +class SKProductSubscriptionPeriodStub: SKProductSubscriptionPeriod { + init(map: [String: Any]) { + super.init() + setValue(map["numberOfUnits"] ?? 0, forKey: "numberOfUnits") + setValue(map["unit"] ?? 0, forKey: "unit") + } +} + +@available(iOS 11.2, macOS 10.13.2, *) +class SKProductDiscountStub: SKProductDiscount { + init(map: [String: Any]?) { + super.init() + setValue( + (map?["price"] as? String).flatMap { NSDecimalNumber(string: $0) } as Any, forKey: "price") + setValue(NSLocale.system, forKey: "priceLocale") + setValue(map?["numberOfPeriods"] ?? 0, forKey: "numberOfPeriods") + let subscriptionPeriodSub = SKProductSubscriptionPeriodStub( + map: map?["subscriptionPeriod"] as? [String: Any] ?? [:]) + setValue(subscriptionPeriodSub, forKey: "subscriptionPeriod") + setValue(map?["paymentMode"] ?? 0, forKey: "paymentMode") + setValue(map?["identifier"] as? String, forKey: "identifier") + setValue(map?["type"] ?? 0, forKey: "type") + } +} + +class SKProductStub: SKProduct { + init(map: [String: Any]) { + super.init() + setValue(map["productIdentifier"] as? String, forKey: "productIdentifier") + setValue(map["localizedDescription"] as? String, forKey: "localizedDescription") + setValue(map["localizedTitle"] as? String, forKey: "localizedTitle") + setValue(map["downloadable"] ?? false, forKey: "downloadable") + setValue( + (map["price"] as? String).flatMap { NSDecimalNumber(string: $0) } as Any, forKey: "price") + setValue(NSLocale.system, forKey: "priceLocale") + setValue(map["downloadContentLengths"] ?? 0, forKey: "downloadContentLengths") + if #available(iOS 11.2, macOS 10.13.2, *) { + let period = SKProductSubscriptionPeriodStub( + map: map["subscriptionPeriod"] as? [String: Any] ?? [:]) + setValue(period, forKey: "subscriptionPeriod") + let discount = SKProductDiscountStub(map: map["introductoryPrice"] as? [String: Any]) + setValue(discount, forKey: "introductoryPrice") + } + setValue(map["subscriptionGroupIdentifier"] as? String, forKey: "subscriptionGroupIdentifier") + if #available(iOS 11.2, macOS 10.13.2, *) { + let discounts = (map["discounts"] as? [[String: Any]] ?? []).map { + SKProductDiscountStub(map: $0) + } + setValue(discounts, forKey: "discounts") + } + } + + init(productID: String) { + super.init() + setValue(productID, forKey: "productIdentifier") + } +} + +class SKProductRequestStub: SKProductsRequest { + private var identifiers: Set = [] + private var requestError: Error? + var returnError = false + + required override init() { + super.init() + } + + override init(productIdentifiers: Set) { + super.init(productIdentifiers: productIdentifiers) + identifiers = productIdentifiers + } + + init(failureError error: Error) { + super.init() + requestError = error + } + + override func start() { + let productArray = identifiers.map { ["productIdentifier": $0] } + var response: SKProductsResponseStub? + if returnError { + response = nil + } else { + response = SKProductsResponseStub(map: ["products": productArray]) + } + + if let requestError = requestError { + delegate?.request?(self, didFailWithError: requestError) + } else if let response = response { + delegate?.productsRequest(self, didReceive: response) + } + } +} + +class SKProductsResponseStub: SKProductsResponse { + init(map: [String: Any]) { + super.init() + let products = (map["products"] as? [[String: Any]] ?? []).map { SKProductStub(map: $0) } + setValue(products, forKey: "products") + } +} + +class SKPaymentQueueStub: SKPaymentQueue { + var testState: SKPaymentTransactionState = .purchasing + var observer: SKPaymentTransactionObserver? + + override func add(_ observer: SKPaymentTransactionObserver) { + self.observer = observer + } + + override func remove(_ observer: SKPaymentTransactionObserver) { + self.observer = nil + } + + override func add(_ payment: SKPayment) { + let transaction = SKPaymentTransactionStub(state: testState, payment: payment) + observer?.paymentQueue(self, updatedTransactions: [transaction]) + } + + override func restoreCompletedTransactions() { + observer?.paymentQueueRestoreCompletedTransactionsFinished?(self) + } + + override func finishTransaction(_ transaction: SKPaymentTransaction) { + observer?.paymentQueue?(self, removedTransactions: [transaction]) + } +} + +/// Finds the ivar backing `SKPaymentTransaction.payment`. +/// +/// `payment` is not key-value-coding compliant for `setValue(_:forKey:)` (there is no +/// `setPayment:` and the underlying ivar isn't named `payment`/`_payment`), so a stub that +/// needs to provide a payment for a transaction it didn't get from the real payment queue has +/// to poke the ivar directly via the Objective-C runtime. +private let skPaymentTransactionPaymentIvar: Ivar? = { + var count: UInt32 = 0 + guard let ivars = class_copyIvarList(SKPaymentTransaction.self, &count) else { return nil } + defer { free(ivars) } + for i in 0.. Data? { + if returnError { + error?.pointee = NSError( + domain: "test", code: 1, + userInfo: [ + "name": "test", + "houseNr": 5, + "error": NSError(domain: "internalTestDomain", code: 99, userInfo: nil), + ]) + return nil + } + let originalString = "test" + return Data(base64Encoded: originalString) + } + + override var receiptURL: URL? { + if returnNilURL { + return nil + } else { + return Bundle.main.appStoreReceiptURL + } + } +} + +class SKReceiptRefreshRequestStub: SKReceiptRefreshRequest { + private var _error: Error? + + required override init() { + super.init() + } + + override init(receiptProperties properties: [String: Any]?) { + super.init(receiptProperties: properties) + } + + init(failureError error: Error) { + super.init() + _error = error + } + + override func start() { + if let error = _error { + delegate?.request?(self, didFailWithError: error) + } else { + delegate?.requestDidFinish?(self) + } + } +} + +@available(iOS 13.0, macOS 10.15, *) +class SKStorefrontStub: SKStorefront { + init(map: [String: Any]) { + super.init() + setValue(map["countryCode"] as? String, forKey: "countryCode") + setValue(map["identifier"] as? String, forKey: "identifier") + } +} + +// An interface representing a stubbed DefaultPaymentQueue +class PaymentQueueStub: NSObject, FLTPaymentQueueProtocol { + // FLTPaymentQueueProtocol properties + var paymentState: SKPaymentTransactionState = .purchasing + var observer: SKPaymentTransactionObserver? + var storefront: SKStorefront? + var transactions: [SKPaymentTransaction] = [] + weak var delegate: SKPaymentQueueDelegate? + + // Test Properties + var testState: SKPaymentTransactionState = .purchasing + var realQueue: SKPaymentQueue = .default() + + // Stubs + var showPriceConsentIfNeededStub: (() -> Void)? + var restoreTransactionsStub: ((String?) -> Void)? + var startObservingPaymentQueueStub: (() -> Void)? + var stopObservingPaymentQueueStub: (() -> Void)? + var presentCodeRedemptionSheetStub: (() -> Void)? + var getUnfinishedTransactionsStub: (() -> [SKPaymentTransaction])? + + func finish(_ transaction: SKPaymentTransaction) { + observer?.paymentQueue?(realQueue, removedTransactions: [transaction]) + } + + @objc(addPayment:) + func add(_ payment: SKPayment) { + let transaction = SKPaymentTransactionStub(state: testState, payment: payment) + observer?.paymentQueue(realQueue, updatedTransactions: [transaction]) + } + + @objc(addTransactionObserver:) + func add(_ observer: SKPaymentTransactionObserver) { + self.observer = observer + } + + func removeTransactionObserver(_ observer: SKPaymentTransactionObserver) { + self.observer = nil + } + + func restoreCompletedTransactions() { + observer?.paymentQueueRestoreCompletedTransactionsFinished?(realQueue) + } + + func restoreCompletedTransactions(withApplicationUsername username: String?) { + observer?.paymentQueueRestoreCompletedTransactionsFinished?(realQueue) + } + + func getUnfinishedTransactions() -> [SKPaymentTransaction] { + return getUnfinishedTransactionsStub?() ?? [] + } + + #if os(iOS) + func presentCodeRedemptionSheet() { + presentCodeRedemptionSheetStub?() + } + + func showPriceConsentIfNeeded() { + showPriceConsentIfNeededStub?() + } + #endif + + func restoreTransactions(_ applicationName: String?) { + restoreTransactionsStub?(applicationName) + } + + func startObservingPaymentQueue() { + startObservingPaymentQueueStub?() + } + + func stopObservingPaymentQueue() { + stopObservingPaymentQueueStub?() + } +} + +// An interface representing a stubbed DefaultTransactionCache +class TransactionCacheStub: NSObject, FLTTransactionCacheProtocol { + var getObjectsForKeyStub: ((TransactionCacheKey) -> [Any])? + var clearStub: (() -> Void)? + var addObjectsStub: (([Any], TransactionCacheKey) -> Void)? + + func add(_ objects: [Any], for key: TransactionCacheKey) { + addObjectsStub?(objects, key) + } + + func clear() { + clearStub?() + } + + func getObjectsFor(_ key: TransactionCacheKey) -> [Any] { + return getObjectsForKeyStub?(key) ?? [] + } +} + +// An interface representing a stubbed DefaultMethodChannel +class MethodChannelStub: NSObject, FLTMethodChannelProtocol { + var invokeMethodChannelStub: ((String, Any?) -> Void)? + var invokeMethodChannelWithResultsStub: ((String, Any?, FlutterResult?) -> Void)? + + func invokeMethod(_ method: String, arguments: Any?) { + invokeMethodChannelStub?(method, arguments) + } + + func invokeMethod(_ method: String, arguments: Any?, result: FlutterResult?) { + invokeMethodChannelWithResultsStub?(method, arguments, result) + } +} + +// An interface representing a stubbed DefaultPaymentQueueHandler +class PaymentQueueHandlerStub: NSObject, SKPaymentTransactionObserver, + FLTPaymentQueueHandlerProtocol +{ + weak var delegate: SKPaymentQueueDelegate? + var storefront: SKStorefront? + + var addPaymentStub: ((SKPayment) -> Bool)? + var showPriceConsentIfNeededStub: (() -> Void)? + var stopObservingPaymentQueueStub: (() -> Void)? + var startObservingPaymentQueueStub: (() -> Void)? + var presentCodeRedemptionSheetStub: (() -> Void)? + var restoreTransactions: ((String?) -> Void)? + var getUnfinishedTransactionsStub: (() -> [SKPaymentTransaction])? + var finishTransactionStub: ((SKPaymentTransaction) -> Void)? + var paymentQueueUpdatedTransactionsStub: ((SKPaymentQueue, [SKPaymentTransaction]) -> Void)? + + override init() { + super.init() + } + + required init( + queue: FLTPaymentQueueProtocol, + transactionsUpdated: TransactionsUpdated?, + transactionRemoved: TransactionsRemoved?, + restoreTransactionFailed: RestoreTransactionFailed?, + restoreCompletedTransactionsFinished: RestoreCompletedTransactionsFinished?, + shouldAddStorePayment: ShouldAddStorePayment?, + updatedDownloads: UpdatedDownloads?, + transactionCache: FLTTransactionCacheProtocol + ) { + super.init() + } + + func paymentQueue( + _ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction] + ) { + paymentQueueUpdatedTransactionsStub?(queue, transactions) + } + + #if os(iOS) + func showPriceConsentIfNeeded() { + showPriceConsentIfNeededStub?() + } + #endif + + func add(_ payment: SKPayment) -> Bool { + return addPaymentStub?(payment) ?? false + } + + func finish(_ transaction: SKPaymentTransaction) { + finishTransactionStub?(transaction) + } + + func getUnfinishedTransactions() -> [SKPaymentTransaction] { + return getUnfinishedTransactionsStub?() ?? [] + } + + #if os(iOS) + func presentCodeRedemptionSheet() { + presentCodeRedemptionSheetStub?() + } + #endif + + func restoreTransactions(_ applicationName: String?) { + restoreTransactions?(applicationName) + } + + func startObservingPaymentQueue() { + startObservingPaymentQueueStub?() + } + + func stopObservingPaymentQueue() { + stopObservingPaymentQueueStub?() + } +} + +// An interface representing a stubbed DefaultRequestHandler +class RequestHandlerStub: NSObject, FLTRequestHandlerProtocol { + var startProductRequestWithCompletionHandlerStub: + (((SKProductsResponse?, Error?) -> Void) -> Void)? + + func startProductRequest(completionHandler: @escaping (SKProductsResponse?, Error?) -> Void) { + startProductRequestWithCompletionHandlerStub?(completionHandler) + } +} + +/// This mock is only used in iOS tests +#if os(iOS) + + // This FlutterPluginRegistrar is a protocol, so to make a stub it has to be implemented. + class FlutterPluginRegistrarStub: NSObject, FlutterPluginRegistrar { + weak var viewController: UIViewController? + var addApplicationDelegateStub: ((FlutterPlugin) -> Void)? + var addMethodCallDelegateStub: ((FlutterPlugin, FlutterMethodChannel) -> Void)? + var lookupKeyForAssetStub: ((String) -> String)? + var lookupKeyForAssetFromPackageStub: ((String, String) -> String)? + var messengerStub: (() -> FlutterBinaryMessenger)? + var publishStub: ((Any) -> Void)? + var registerViewFactoryStub: ((FlutterPlatformViewFactory, String) -> Void)? + var texturesStub: (() -> FlutterTextureRegistry)? + var registerViewFactoryWithGestureRecognizersBlockingPolicyStub: + ( + (FlutterPlatformViewFactory, String, FlutterPlatformViewGestureRecognizersBlockingPolicy) -> + Void + )? + + func addApplicationDelegate(_ delegate: FlutterPlugin) { + addApplicationDelegateStub?(delegate) + } + + func addMethodCallDelegate(_ delegate: FlutterPlugin, channel: FlutterMethodChannel) { + addMethodCallDelegateStub?(delegate, channel) + } + + func lookupKey(forAsset asset: String) -> String { + return lookupKeyForAssetStub?(asset) ?? "" + } + + func lookupKey(forAsset asset: String, fromPackage package: String) -> String { + return lookupKeyForAssetFromPackageStub?(asset, package) ?? "" + } + + func messenger() -> FlutterBinaryMessenger { + return messengerStub?() ?? FlutterBinaryMessengerStub() + } + + func publish(_ value: Any) { + publishStub?(value) + } + + func register(_ factory: FlutterPlatformViewFactory, withId factoryId: String) { + registerViewFactoryStub?(factory, factoryId) + } + + func textures() -> FlutterTextureRegistry { + return texturesStub!() + } + + func register( + _ factory: FlutterPlatformViewFactory, withId factoryId: String, + gestureRecognizersBlockingPolicy: + FlutterPlatformViewGestureRecognizersBlockingPolicy + ) { + registerViewFactoryWithGestureRecognizersBlockingPolicyStub?( + factory, factoryId, gestureRecognizersBlockingPolicy) + } + + func addSceneDelegate(_ delegate: FlutterSceneLifeCycleDelegate) {} + } + +#endif + +// This FlutterBinaryMessenger is a protocol, so to make a stub it has to be implemented. +class FlutterBinaryMessengerStub: NSObject, FlutterBinaryMessenger { + var cleanUpConnectionStub: ((FlutterBinaryMessengerConnection) -> Void)? + var sendOnChannelMessageStub: ((String, Data?) -> Void)? + var sendOnChannelMessageBinaryReplyStub: ((String, Data?, FlutterBinaryReply?) -> Void)? + var setMessageHandlerOnChannelBinaryMessageHandlerStub: + ((String, FlutterBinaryMessageHandler?) -> FlutterBinaryMessengerConnection)? + + func cleanUpConnection(_ connection: FlutterBinaryMessengerConnection) { + cleanUpConnectionStub?(connection) + } + + func send(onChannel channel: String, message: Data?) { + sendOnChannelMessageStub?(channel, message) + } + + func send(onChannel channel: String, message: Data?, binaryReply callback: FlutterBinaryReply?) { + sendOnChannelMessageBinaryReplyStub?(channel, message, callback) + } + + func setMessageHandlerOnChannel( + _ channel: String, binaryMessageHandler handler: FlutterBinaryMessageHandler? + ) -> FlutterBinaryMessengerConnection { + return setMessageHandlerOnChannelBinaryMessageHandlerStub?(channel, handler) ?? 0 + } +} + +class InAppPurchasePluginStub: InAppPurchasePlugin { + override func getProductRequest(withIdentifiers productIdentifiers: Set) + -> SKProductsRequest + { + return SKProductRequestStub(productIdentifiers: productIdentifiers) + } + + override func getProduct(productID: String) -> SKProduct? { + if productID == "" { + return nil + } + return SKProductStub(productID: productID) + } + + override func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } +} 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 cfae2bb9f802..e0b8a7c9b808 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.4.11+2 +version: 0.4.11+3 environment: sdk: ^3.10.0