From 4fbc4e8ec0d369408155e6aa7a0c7401e22272e5 Mon Sep 17 00:00:00 2001 From: Ramesh Gadagoju Date: Wed, 23 Aug 2023 14:48:06 +0530 Subject: [PATCH 1/3] customerId is returned in Purchase API handler earlier it was not handled and also in view files cleared SwiftLint warning issues --- .../Authentication/CBAuthentication.swift | 6 +- .../CBAuthenticationResource.swift | 3 +- Chargebee/Classes/Cache/CBCache.swift | 46 +++++----- Chargebee/Classes/Network/CBAPIRequest.swift | 6 +- .../Classes/Purchase/CBPurchaseManager.swift | 85 +++++++++---------- .../Restore/BackgroundOperationQueue.swift | 15 ++-- .../Restore/CBPurchaseManager+Extension.swift | 68 +++++++-------- .../UIApplication+Settings.swift | 10 ++- .../CBSDKProductsTableViewController.swift | 7 +- 9 files changed, 121 insertions(+), 125 deletions(-) diff --git a/Chargebee/Classes/Authentication/CBAuthentication.swift b/Chargebee/Classes/Authentication/CBAuthentication.swift index d885c94..1601271 100644 --- a/Chargebee/Classes/Authentication/CBAuthentication.swift +++ b/Chargebee/Classes/Authentication/CBAuthentication.swift @@ -45,7 +45,7 @@ extension CBAuthenticationManager { guard key.isNotEmpty else { return onError(CBError.defaultSytemError(statusCode: 400, message: "SDK Key is empty")) } - + if CBCache.shared.isCacheDataAvailable() { CBCache.shared.readConfigDetails(logger: logger) { status in handler(status) @@ -53,12 +53,12 @@ extension CBAuthenticationManager { let request = CBAPIRequest(resource: CBAuthenticationResource(key: key, bundleId: bundleId, appName: appName)) self.authenticateRestClient(network: request, logger: logger, handler: handler) } - }else{ + } else { let request = CBAPIRequest(resource: CBAuthenticationResource(key: key, bundleId: bundleId, appName: appName)) authenticateRestClient(network: request, logger: logger, handler: handler) } } - + func authenticateRestClient(network: T, logger: CBLogger, handler: @escaping CBAuthenticationHandler) { let (onSuccess, onError) = CBResult.buildResultHandlers(handler, logger) network.load(withCompletion: { status in diff --git a/Chargebee/Classes/Authentication/CBAuthenticationResource.swift b/Chargebee/Classes/Authentication/CBAuthenticationResource.swift index 4cffa9f..88b27dd 100644 --- a/Chargebee/Classes/Authentication/CBAuthenticationResource.swift +++ b/Chargebee/Classes/Authentication/CBAuthenticationResource.swift @@ -37,8 +37,7 @@ final class CBAuthenticationResource: CBAPIResource { return urlRequest } - init(key: String, bundleId: String, - appName: String) { + init(key: String, bundleId: String, appName: String) { self.baseUrl = CBEnvironment.baseUrl self.requestBody = CBAuthenticationBody.init(key: key, bundleId: bundleId, appName: appName) } diff --git a/Chargebee/Classes/Cache/CBCache.swift b/Chargebee/Classes/Cache/CBCache.swift index 9549bc1..0b3ffb0 100644 --- a/Chargebee/Classes/Cache/CBCache.swift +++ b/Chargebee/Classes/Cache/CBCache.swift @@ -18,29 +18,29 @@ private struct ConfigCacheModel: Codable { } private protocol CacheProtocol { - func writeConfigDetails(object:CBAuthentication) + func writeConfigDetails(object: CBAuthentication) func readConfigDetails(logger: CBLogger, withCompletion completion: @escaping (CBResult) -> Void, onError: ErrorHandler) func saveAuthenticationDetails(data: CBAuthenticationStatus) - func isCacheDataAvailable()-> Bool + func isCacheDataAvailable() -> Bool } internal struct CBCache: CacheProtocol { private let uniqueIdKey = "chargebee_config" static let shared = CBCache() - + func readConfigDetails(logger: CBLogger, withCompletion completion: @escaping (CBResult) -> Void, onError: ErrorHandler) { let (onSuccess, _) = CBResult.buildResultHandlers(completion, logger) if let cacheModel = getCacheObject() { let auth = CBAuthenticationStatus.init(details: cacheModel.config) onSuccess(auth) - }else{ - onError(CBError.defaultSytemError(statusCode: 400, message:"Failed to read config details from Cache")) + } else { + onError(CBError.defaultSytemError(statusCode: 400, message: "Failed to read config details from Cache")) } } - - internal func writeConfigDetails(object configObject:CBAuthentication) { + + internal func writeConfigDetails(object configObject: CBAuthentication) { if let cacheModel = getCacheObject() { - if cacheModel.config.appId != nil{ + if cacheModel.config.appId != nil { UserDefaults.standard.removeObject(forKey: getFormattedkey()) } } @@ -49,26 +49,26 @@ internal struct CBCache: CacheProtocol { UserDefaults.standard.set(encoded, forKey: getFormattedkey()) } } - + private func createTime() -> Date { let currentDate = Date() let newDate = NSDate(timeInterval: 86400, since: currentDate) return newDate as Date } - + internal func saveAuthenticationDetails(data: CBAuthenticationStatus) { - if let appId = data.details.appId,let status = data.details.status , let version = data.details.version{ + if let appId = data.details.appId, let status = data.details.status, let version = data.details.version { let configDetails = CBAuthentication.init(appId: appId, status: status, version: version) self.writeConfigDetails(object: configDetails) } } - - internal func isCacheDataAvailable()-> Bool { + + internal func isCacheDataAvailable() -> Bool { if let cacheModel = getCacheObject() { - if let appID = cacheModel.config.appId ,let status = cacheModel.config.status { + if let appID = cacheModel.config.appId, let status = cacheModel.config.status { if !appID.isEmpty && !status.isEmpty { - let waitingDate:NSDate = cacheModel.validTill as NSDate - if (Date().compare(waitingDate as Date) == ComparisonResult.orderedDescending) { + let waitingDate: NSDate = cacheModel.validTill as NSDate + if Date().compare(waitingDate as Date) == ComparisonResult.orderedDescending { UserDefaults.standard.removeObject(forKey: getFormattedkey()) return false } @@ -78,26 +78,24 @@ internal struct CBCache: CacheProtocol { } return false } - - fileprivate func getCacheObject()-> ConfigCacheModel? { + + fileprivate func getCacheObject() -> ConfigCacheModel? { if let data = UserDefaults.standard.object(forKey: getFormattedkey()) as? Data, let cacheModel = try? JSONDecoder().decode(ConfigCacheModel.self, from: data) { return cacheModel } return nil } - - private func getBundleID()-> String{ + + private func getBundleID() -> String { var bundleID = "" if let id = Bundle.main.bundleIdentifier { bundleID = id } return bundleID } - - private func getFormattedkey()-> String { + + private func getFormattedkey() -> String { return getBundleID().appending("_").appending(uniqueIdKey) } } - - diff --git a/Chargebee/Classes/Network/CBAPIRequest.swift b/Chargebee/Classes/Network/CBAPIRequest.swift index 9754d88..46bf104 100644 --- a/Chargebee/Classes/Network/CBAPIRequest.swift +++ b/Chargebee/Classes/Network/CBAPIRequest.swift @@ -127,13 +127,13 @@ extension URLSession: NetworkSession { } struct NetworkClient { - + func retrieve(network: T, logger: CBLogger, handler: @escaping (CBResult) -> Void) { let (onSuccess, onError) = CBResult.buildResultHandlers(handler, logger) - network.load(withCompletion: { result in + network.load(withCompletion: { result in if let data = result as? U { onSuccess(data) - }else{ + } else { onError(CBError.defaultSytemError(statusCode: 480, message: "json serialization failure")) } }, onError: onError) diff --git a/Chargebee/Classes/Purchase/CBPurchaseManager.swift b/Chargebee/Classes/Purchase/CBPurchaseManager.swift index 2f5be75..074ad23 100644 --- a/Chargebee/Classes/Purchase/CBPurchaseManager.swift +++ b/Chargebee/Classes/Purchase/CBPurchaseManager.swift @@ -12,7 +12,7 @@ public class CBPurchase: NSObject { public static let shared = CBPurchase() private var productIDs: [String] = [] public var receiveProductsHandler: ((_ result: Result<[CBProduct], CBPurchaseError>) -> Void)? - public var buyProductHandler: ((Result<(status:Bool, subscriptionId:String?, planId:String?), Error>) -> Void)? + public var buyProductHandler: ((Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)? private var buyNonSubscriptionProductHandler: ((Result) -> Void)? private var authenticationManager = CBAuthenticationManager() @@ -21,7 +21,6 @@ public class CBPurchase: NSObject { var restoredPurchasesCount = 0 private var activeProduct: CBProduct? var customer: CBCustomer? - var restoreResponseHandler: ((Result<[InAppSubscription], RestoreError>) -> Void)? var refreshHandler: RestoreResultCompletion? var includeInActiveProducts = false @@ -33,8 +32,7 @@ public class CBPurchase: NSObject { super.init() startPaymentQueueObserver() } - - deinit{ + deinit { stopPaymentQueueObserver() } } @@ -111,46 +109,45 @@ public extension CBPurchase { retrieveProducts() } } - - func purchaseNonSubscriptionProduct(product: CBProduct, customer : CBCustomer? = nil ,productType : ProductType, completion handler: @escaping ((_ result: Result) -> Void)) { + + func purchaseNonSubscriptionProduct(product: CBProduct, customer: CBCustomer? = nil, productType: ProductType, completion handler: @escaping ((_ result: Result) -> Void)) { buyNonSubscriptionProductHandler = handler activeProduct = product self.productType = productType self.customer = customer self.purchaseProductHandler(product: product, completion: handler) } - - //Buy the product + @available(*, deprecated, message: "This will be removed in upcoming versions, Please use this API func purchaseProduct(product: CBProduct, customer : CBCustomer? = nil, completion)") - func purchaseProduct(product: CBProduct, customerId : String? = "",completion handler: @escaping ((_ result: Result<(status:Bool, subscriptionId:String?, planId:String?), Error>) -> Void)) { + func purchaseProduct(product: CBProduct, customerId: String? = "", completion handler: @escaping ((_ result: Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = CBCustomer(customerID: customerId ?? "") self.purchaseProductHandler(product: product, completion: handler) } - - func purchaseProduct(product: CBProduct, customer : CBCustomer? = nil, completion handler: @escaping ((_ result: Result<(status:Bool, subscriptionId:String?, planId:String?), Error>) -> Void)) { + + func purchaseProduct(product: CBProduct, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = customer self.purchaseProductHandler(product: product, completion: handler) } - - func restorePurchases(includeInActiveProducts:Bool = false, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result<[InAppSubscription], RestoreError>) -> Void)) { + + func restorePurchases(includeInActiveProducts: Bool = false, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result<[InAppSubscription], RestoreError>) -> Void)) { self.restoreResponseHandler = handler self.includeInActiveProducts = includeInActiveProducts self.restoredPurchasesCount = 0 self.restoreCustomer = customer SKPaymentQueue.default().restoreCompletedTransactions() } - - func purchaseProductHandler(product: CBProduct,completion handler: @escaping ((_ result: Result) -> Void)) { - + + func purchaseProductHandler(product: CBProduct, completion handler: @escaping ((_ result: Result) -> Void)) { + guard CBAuthenticationManager.isSDKKeyPresent() else { handler(.failure(CBPurchaseError.cannotMakePayments)) return } - + if !CBPurchase.shared.canMakePayments() { handler(.failure(CBPurchaseError.cannotMakePayments)) } else { @@ -158,7 +155,6 @@ public extension CBPurchase { if status { let payment = SKPayment(product: product.product) SKPaymentQueue.default().add(payment) - } else { handler(.failure(CBPurchaseError.invalidSDKKey)) } @@ -198,7 +194,7 @@ extension CBPurchase: SKProductsRequestDelegate { debugPrint("Error: \(error.localizedDescription)") if request is SKReceiptRefreshRequest { completedRefresh(error: error) - }else{ + } else { receiveProductsHandler?(.failure(.skRequestFailed)) } request.cancel() @@ -216,16 +212,16 @@ extension CBPurchase: SKPaymentTransactionObserver { if let product = activeProduct { if let _ = product.product.subscriptionPeriod { validateReceipt(product, customer: self.customer, completion: buyProductHandler) - }else{ - validateReceiptForNonSubscriptions(product, self.productType,customer: self.customer, completion: buyNonSubscriptionProductHandler) + } else { + validateReceiptForNonSubscriptions(product, self.productType, customer: self.customer, completion: buyNonSubscriptionProductHandler) } } case .restored: SKPaymentQueue.default().finishTransaction(transaction) receivedRestoredTransaction() case .failed: - if let error = transaction.error as? SKError{ - debugPrint("Error :",error) + if let error = transaction.error as? SKError { + debugPrint("Error :", error) switch error.errorCode { case 0: self.invokeProductHandler(forProduct: self.activeProduct?.product, error: CBPurchaseError.unknown) @@ -258,7 +254,7 @@ extension CBPurchase: SKPaymentTransactionObserver { default: if let _ = activeProduct?.product.subscriptionPeriod { buyProductHandler?(.failure(error)) - }else { + } else { buyNonSubscriptionProductHandler?(.failure(error)) } } @@ -286,19 +282,19 @@ extension CBPurchase: SKPaymentTransactionObserver { // chargebee methods public extension CBPurchase { - - func validateReceiptForNonSubscriptions(_ product: CBProduct?,_ productType: - ProductType?,customer: CBCustomer? = nil, completion: ((Result) -> Void)?) { + + func validateReceiptForNonSubscriptions(_ product: CBProduct?, + _ productType: ProductType?, + customer: CBCustomer? = nil, + completion: ((Result) -> Void)?) { self.productType = productType - - guard let receipt = getReceipt(product: product?.product,customer: customer) else { + guard let receipt = getReceipt(product: product?.product, customer: customer) else { debugPrint("Couldn't read receipt data with error") completion?(.failure(CBError.defaultSytemError(statusCode: 0, message: "Could not read receipt data"))) return } - - CBReceiptValidationManager.validateReceiptForNonSubscriptions(receipt: receipt) { - (receiptResult) in DispatchQueue.main.async { + + CBReceiptValidationManager.validateReceiptForNonSubscriptions(receipt: receipt) { (receiptResult) in DispatchQueue.main.async { switch receiptResult { case .success(let result): debugPrint("Receipt: \(result)") @@ -311,17 +307,16 @@ public extension CBPurchase { } } } - - func validateReceipt(_ product: CBProduct?,customer: CBCustomer? = nil,completion: ((Result<(status:Bool, subscriptionId:String?, planId:String?), Error>) -> Void)?) { - - guard let receipt = getReceipt(product: product?.product,customer: customer) else { + + func validateReceipt(_ product: CBProduct?, customer: CBCustomer? = nil, completion: ((Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)?) { + + guard let receipt = getReceipt(product: product?.product, customer: customer) else { debugPrint("Couldn't read receipt data with error") completion?(.failure(CBError.defaultSytemError(statusCode: 0, message: "Could not read receipt data"))) return } - - CBReceiptValidationManager.validateReceipt(receipt: receipt) { - (receiptResult) in DispatchQueue.main.async { + + CBReceiptValidationManager.validateReceipt(receipt: receipt) { (receiptResult) in DispatchQueue.main.async { switch receiptResult { case .success(let receipt): debugPrint("Receipt: \(receipt)") @@ -330,7 +325,7 @@ public extension CBPurchase { return } self.activeProduct = nil - completion?(.success((true, receipt.subscriptionId, receipt.planId))) + completion?(.success((true, receipt.subscriptionId, receipt.planId, receipt.customerId))) case .error(let error): debugPrint(" Chargebee - Receipt Upload - Failure") completion?(.failure(error)) @@ -338,7 +333,7 @@ public extension CBPurchase { } } } - + private func getReceipt(product: SKProduct?, customer: CBCustomer? = nil) -> CBReceipt? { var receipt: CBReceipt? guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, @@ -351,20 +346,20 @@ public extension CBPurchase { } do { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) - + let receiptString = receiptData.base64EncodedString(options: []) debugPrint("Apple Purchase - success") receipt = CBReceipt(name: product.localizedTitle, token: receiptString, productID: product.productIdentifier, price: "\(product.price)", currencyCode: currencyCode, period: product.subscriptionPeriod?.numberOfUnits ?? 0, periodUnit: Int(product.subscriptionPeriod?.unit.rawValue ?? 0),customer: customer,productType: self.productType ?? .unknown) - }catch { + } catch { print("Couldn't read receipt data with error: " + error.localizedDescription) } return receipt } - + private func invokeProductHandler(forProduct product: SKProduct?, error: CBPurchaseError) { if let _ = product?.subscriptionPeriod { buyProductHandler?(.failure(error)) - }else { + } else { buyNonSubscriptionProductHandler?(.failure(error)) } } diff --git a/Chargebee/Classes/Restore/BackgroundOperationQueue.swift b/Chargebee/Classes/Restore/BackgroundOperationQueue.swift index 390ebf2..d7381af 100644 --- a/Chargebee/Classes/Restore/BackgroundOperationQueue.swift +++ b/Chargebee/Classes/Restore/BackgroundOperationQueue.swift @@ -8,26 +8,26 @@ import UIKit class BackgroundOperationQueue: OperationQueue { - + private static var operationsKeyPath: String { return "operations" } - + deinit { self.removeObserver(self, forKeyPath: "operations") } - + var completionBlock: (() -> Void)? { didSet { self.addObserver(self, forKeyPath: BackgroundOperationQueue.operationsKeyPath, options: .new, context: nil) } } - + override init() { super.init() } - - func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) { + + func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String: AnyObject]?, context: UnsafeMutableRawPointer) { if let operationPath = keyPath, operationPath == BackgroundOperationQueue.operationsKeyPath { if self.operations.isEmpty { OperationQueue.main.addOperation({ @@ -36,6 +36,5 @@ class BackgroundOperationQueue: OperationQueue { } } } - -} +} diff --git a/Chargebee/Classes/Restore/CBPurchaseManager+Extension.swift b/Chargebee/Classes/Restore/CBPurchaseManager+Extension.swift index b7e29c1..0670255 100644 --- a/Chargebee/Classes/Restore/CBPurchaseManager+Extension.swift +++ b/Chargebee/Classes/Restore/CBPurchaseManager+Extension.swift @@ -16,26 +16,26 @@ extension CBPurchase { func receivedRestoredTransaction() { self.restoredPurchasesCount += 1 } - + func receiveRestoredTransactionsFinished(_ error: RestoreError?) { if let error = error { debugPrint("Failed to restore purchases: \(error.localizedDescription)") self.restoreResponseHandler?(.failure(.restoreFailed)) return } - + if self.restoredPurchasesCount == 0 { debugPrint("Successfully restored zero purchases.") } - + self.validateReceipt(refreshIfEmpty: true) } - + func getReceipt(refreshIfEmpty: Bool, _ completion: @escaping RestoreResultCompletion) { var result: ReceiptResult result = self.bundleReceipt() self.refreshHandler = completion - + switch result { case .success: completion(result) @@ -45,13 +45,13 @@ extension CBPurchase { self.refreshReceipt(completion) } else { completion(result) - self.refreshReceipt{_ in} + self.refreshReceipt {_ in} } } } - + func bundleReceipt() -> ReceiptResult { - + guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) else { debugPrint("No receipt Exist") @@ -61,12 +61,12 @@ extension CBPurchase { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) let receiptString = receiptData.base64EncodedString(options: []) return .success(receiptString) - }catch{ + } catch { debugPrint("Couldn't read receipt data with error: " + error.localizedDescription) return .failure(.invalidReceiptData) } } - + private func refreshReceipt(_ completion: @escaping RestoreResultCompletion) { self.refreshHandler = completion debugPrint("Start refresh receipt") @@ -76,35 +76,35 @@ extension CBPurchase { } } -extension CBPurchase{ +extension CBPurchase { public func validateReceipt(refreshIfEmpty: Bool) { - + getReceipt(refreshIfEmpty: refreshIfEmpty) { receiptString in switch receiptString { case .success(let receiptString): self.receiptVerification(receipt: receiptString, self.restoreResponseHandler) case .failure(let error): - debugPrint("Error While trying to fetch receipt",error) + debugPrint("Error While trying to fetch receipt", error) self.restoreResponseHandler?(.failure(error)) } } } - + func receiptVerification(receipt: String, _ completion: ((Result<[InAppSubscription], RestoreError>) -> Void)?) { - + CBRestorePurchaseManager().restorePurchases(receipt: receipt) { result in - switch result{ + switch result { case .success(let restoreResult): - if self.includeInActiveProducts{ + if self.includeInActiveProducts { completion?(.success(restoreResult.inAppSubscriptions)) - }else{ + } else { let activeSubscriptionsList = restoreResult.inAppSubscriptions.filter { return $0.storeStatus.rawValue == StoreStatus.Active.rawValue || $0.storeStatus.rawValue == StoreStatus.InTrail.rawValue } completion?(.success(activeSubscriptionsList)) } - + let productIdsList = restoreResult.inAppSubscriptions.map { planID in return planID.planID } @@ -112,32 +112,32 @@ extension CBPurchase{ self.getPruchaseProductsList(productIds: productIdsList) } case .error(let error): - debugPrint("Error While Restoring:",error.localizedDescription) + debugPrint("Error While Restoring:", error.localizedDescription) completion?(.failure(.serviceError(error: error.localizedDescription))) } } } - - func getPruchaseProductsList(productIds:[String]) { - - self.retrieveProducts(withProductID:productIds) { result in + + func getPruchaseProductsList(productIds: [String]) { + + self.retrieveProducts(withProductID: productIds) { result in switch result { case .success(let products): self.syncPurhcasesWithChargebee(products: products) case.failure(let error): - debugPrint("Error While retriving products to sync with Chargebeee:",error) + debugPrint("Error While retriving products to sync with Chargebeee:", error) } } } - - func syncPurhcasesWithChargebee(products:[CBProduct]) { - + + func syncPurhcasesWithChargebee(products: [CBProduct]) { + var operationQueue: BackgroundOperationQueue? = BackgroundOperationQueue() for product in products { - operationQueue?.addOperation{ + operationQueue?.addOperation { if let _ = product.product.subscriptionPeriod { - self.validateReceipt(product,customer: self.restoreCustomer, completion: nil) - }else{ + self.validateReceipt(product, customer: self.restoreCustomer, completion: nil) + } else { self.validateReceiptForNonSubscriptions(product, .unknown, customer: self.restoreCustomer, completion: nil) } } @@ -147,7 +147,7 @@ extension CBPurchase{ operationQueue = nil } } - + func completedRefresh(error: Error?) { var refreshResult: ReceiptResult if let error = error { @@ -159,7 +159,7 @@ extension CBPurchase{ self.refreshHandler?(refreshResult) } } - + } extension CBPurchase: SKRequestDelegate { @@ -169,6 +169,4 @@ extension CBPurchase: SKRequestDelegate { } request.cancel() } - } - diff --git a/Chargebee/Classes/UIApplicationSettings/UIApplication+Settings.swift b/Chargebee/Classes/UIApplicationSettings/UIApplication+Settings.swift index 1502082..8f79aca 100644 --- a/Chargebee/Classes/UIApplicationSettings/UIApplication+Settings.swift +++ b/Chargebee/Classes/UIApplicationSettings/UIApplication+Settings.swift @@ -11,19 +11,21 @@ import StoreKit extension UIApplication { - @objc class func showExternalManageSubscriptions() { + @objc class + func showExternalManageSubscriptions() { guard let url = URL(string: "https://apps.apple.com/account/subscriptions") else { return } self.shared.open(url, options: [:], completionHandler: nil) } - + @available(iOS 15.0, *) - @objc class func showManageSubscriptions() { + @objc class + func showManageSubscriptions() { guard let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene, !ProcessInfo.processInfo.isiOSAppOnMac else { UIApplication.showExternalManageSubscriptions() return } - + Task { do { try await AppStore.showManageSubscriptions(in: scene) diff --git a/Example/Chargebee/CBSDKProductsTableViewController.swift b/Example/Chargebee/CBSDKProductsTableViewController.swift index 2af4a18..6db944e 100644 --- a/Example/Chargebee/CBSDKProductsTableViewController.swift +++ b/Example/Chargebee/CBSDKProductsTableViewController.swift @@ -285,6 +285,8 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result.status) print(result.subscriptionId ?? "") print(result.planId ?? "") + print(result.customerId ?? "") + DispatchQueue.main.async { self.view.activityStopAnimating() let alertController = UIAlertController(title: "Chargebee", message: "success", preferredStyle: .alert) @@ -354,7 +356,7 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { CBDemoPersistance.saveProductIdentifierOnPurchase(for: withProduct.product.productIdentifier) } self.view.activityStartAnimating(activityColor: UIColor.white, backgroundColor: UIColor.black.withAlphaComponent(0.5)) - CBPurchase.shared.purchaseProduct(product: withProduct,customerId: customerID) { result in + CBPurchase.shared.purchaseProduct(product: withProduct, customerId: customerID) { result in print(result) switch result { @@ -362,6 +364,9 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result.status) print(result.subscriptionId ?? "") print(result.planId ?? "") + print(result.customerId ?? "") + + DispatchQueue.main.async { self.view.activityStopAnimating() From 8c4494e06191fe78aa1624cc6679ad77372c10cb Mon Sep 17 00:00:00 2001 From: Ramesh Gadagoju Date: Tue, 29 Aug 2023 11:51:31 +0530 Subject: [PATCH 2/3] As per Pr comments updated the tuple as Type for better code readability --- .../Classes/Purchase/CBPurchaseManager.swift | 16 +++++++------- .../CBReceiptValidationManager.swift | 2 +- .../CBSDKProductsTableViewController.swift | 22 +++++++++---------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Chargebee/Classes/Purchase/CBPurchaseManager.swift b/Chargebee/Classes/Purchase/CBPurchaseManager.swift index 074ad23..a8ba50a 100644 --- a/Chargebee/Classes/Purchase/CBPurchaseManager.swift +++ b/Chargebee/Classes/Purchase/CBPurchaseManager.swift @@ -12,7 +12,7 @@ public class CBPurchase: NSObject { public static let shared = CBPurchase() private var productIDs: [String] = [] public var receiveProductsHandler: ((_ result: Result<[CBProduct], CBPurchaseError>) -> Void)? - public var buyProductHandler: ((Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)? + private var buyProductHandler: ((Result) -> Void)? private var buyNonSubscriptionProductHandler: ((Result) -> Void)? private var authenticationManager = CBAuthenticationManager() @@ -119,14 +119,14 @@ public extension CBPurchase { } @available(*, deprecated, message: "This will be removed in upcoming versions, Please use this API func purchaseProduct(product: CBProduct, customer : CBCustomer? = nil, completion)") - func purchaseProduct(product: CBProduct, customerId: String? = "", completion handler: @escaping ((_ result: Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)) { + func purchaseProduct(product: CBProduct, customerId: String? = "", completion handler: @escaping ((_ result: Result) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = CBCustomer(customerID: customerId ?? "") self.purchaseProductHandler(product: product, completion: handler) } - func purchaseProduct(product: CBProduct, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)) { + func purchaseProduct(product: CBProduct, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = customer @@ -308,7 +308,7 @@ public extension CBPurchase { } } - func validateReceipt(_ product: CBProduct?, customer: CBCustomer? = nil, completion: ((Result<(status: Bool, subscriptionId: String?, planId: String?, customerId: String?), Error>) -> Void)?) { + func validateReceipt(_ product: CBProduct?, customer: CBCustomer? = nil, completion: ((Result) -> Void)?) { guard let receipt = getReceipt(product: product?.product, customer: customer) else { debugPrint("Couldn't read receipt data with error") @@ -318,14 +318,14 @@ public extension CBPurchase { CBReceiptValidationManager.validateReceipt(receipt: receipt) { (receiptResult) in DispatchQueue.main.async { switch receiptResult { - case .success(let receipt): - debugPrint("Receipt: \(receipt)") - if receipt.subscriptionId.isEmpty { + case .success(let result): + debugPrint("Receipt: \(result)") + if result.subscriptionId.isEmpty { completion?(.failure(CBError.defaultSytemError(statusCode: 400, message: "Invalid Purchase"))) return } self.activeProduct = nil - completion?(.success((true, receipt.subscriptionId, receipt.planId, receipt.customerId))) + completion?(.success(result)) case .error(let error): debugPrint(" Chargebee - Receipt Upload - Failure") completion?(.failure(error)) diff --git a/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift b/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift index 6522a1d..ce1e8d0 100644 --- a/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift +++ b/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift @@ -38,7 +38,7 @@ struct CBValidateReceiptWrapper: Decodable { } } -struct CBValidateReceipt: Decodable { +public struct CBValidateReceipt: Decodable { public let subscriptionId: String public let customerId: String public let planId: String diff --git a/Example/Chargebee/CBSDKProductsTableViewController.swift b/Example/Chargebee/CBSDKProductsTableViewController.swift index 6db944e..951f4f1 100644 --- a/Example/Chargebee/CBSDKProductsTableViewController.swift +++ b/Example/Chargebee/CBSDKProductsTableViewController.swift @@ -87,9 +87,10 @@ final class CBSDKProductsTableViewController: UITableViewController, UITextField CBPurchase.shared.validateReceipt(product,customer: nil) { result in switch result { case .success(let result): - print(result.status ) - print(result.subscriptionId ?? "") - print(result.planId ?? "") + print(result.subscriptionId ) + print(result.planId) + print(result.customerId) + if CBDemoPersistance.isPurchaseProductIDAvailable(){ CBDemoPersistance.clearPurchaseIDFromCache() } @@ -282,10 +283,9 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result) switch result { case .success(let result): - print(result.status) - print(result.subscriptionId ?? "") - print(result.planId ?? "") - print(result.customerId ?? "") + print(result.subscriptionId) + print(result.planId) + print(result.customerId) DispatchQueue.main.async { self.view.activityStopAnimating() @@ -361,11 +361,9 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result) switch result { case .success(let result): - print(result.status) - print(result.subscriptionId ?? "") - print(result.planId ?? "") - print(result.customerId ?? "") - + print(result.subscriptionId) + print(result.planId) + print(result.customerId) DispatchQueue.main.async { From d41234e10ff67db9534fb91c4611f9a3bf60eb9c Mon Sep 17 00:00:00 2001 From: Ramesh Gadagoju Date: Tue, 29 Aug 2023 14:53:09 +0530 Subject: [PATCH 3/3] Code Updated for status also as Andriod --- .../Classes/Purchase/CBPurchaseManager.swift | 10 +++++----- .../CBReceiptValidationManager.swift | 5 +++++ .../CBSDKProductsTableViewController.swift | 15 ++++++--------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Chargebee/Classes/Purchase/CBPurchaseManager.swift b/Chargebee/Classes/Purchase/CBPurchaseManager.swift index a8ba50a..1b37d08 100644 --- a/Chargebee/Classes/Purchase/CBPurchaseManager.swift +++ b/Chargebee/Classes/Purchase/CBPurchaseManager.swift @@ -12,7 +12,7 @@ public class CBPurchase: NSObject { public static let shared = CBPurchase() private var productIDs: [String] = [] public var receiveProductsHandler: ((_ result: Result<[CBProduct], CBPurchaseError>) -> Void)? - private var buyProductHandler: ((Result) -> Void)? + private var buyProductHandler: ((Result) -> Void)? private var buyNonSubscriptionProductHandler: ((Result) -> Void)? private var authenticationManager = CBAuthenticationManager() @@ -119,14 +119,14 @@ public extension CBPurchase { } @available(*, deprecated, message: "This will be removed in upcoming versions, Please use this API func purchaseProduct(product: CBProduct, customer : CBCustomer? = nil, completion)") - func purchaseProduct(product: CBProduct, customerId: String? = "", completion handler: @escaping ((_ result: Result) -> Void)) { + func purchaseProduct(product: CBProduct, customerId: String? = "", completion handler: @escaping ((_ result: Result) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = CBCustomer(customerID: customerId ?? "") self.purchaseProductHandler(product: product, completion: handler) } - func purchaseProduct(product: CBProduct, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result) -> Void)) { + func purchaseProduct(product: CBProduct, customer: CBCustomer? = nil, completion handler: @escaping ((_ result: Result) -> Void)) { buyProductHandler = handler activeProduct = product self.customer = customer @@ -308,7 +308,7 @@ public extension CBPurchase { } } - func validateReceipt(_ product: CBProduct?, customer: CBCustomer? = nil, completion: ((Result) -> Void)?) { + func validateReceipt(_ product: CBProduct?, customer: CBCustomer? = nil, completion: ((Result) -> Void)?) { guard let receipt = getReceipt(product: product?.product, customer: customer) else { debugPrint("Couldn't read receipt data with error") @@ -325,7 +325,7 @@ public extension CBPurchase { return } self.activeProduct = nil - completion?(.success(result)) + completion?(.success(PurchaseSubscription(status: true, subscriptionDetails: result))) case .error(let error): debugPrint(" Chargebee - Receipt Upload - Failure") completion?(.failure(error)) diff --git a/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift b/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift index ce1e8d0..aed7347 100644 --- a/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift +++ b/Chargebee/Classes/ReceiptValidation/CBReceiptValidationManager.swift @@ -37,6 +37,11 @@ struct CBValidateReceiptWrapper: Decodable { case inAppSubscription = "in_app_subscription" } } +// MARK: - Subscription +public struct PurchaseSubscription: Decodable { + public let status: Bool + public let subscriptionDetails: CBValidateReceipt +} public struct CBValidateReceipt: Decodable { public let subscriptionId: String diff --git a/Example/Chargebee/CBSDKProductsTableViewController.swift b/Example/Chargebee/CBSDKProductsTableViewController.swift index 951f4f1..a7424dc 100644 --- a/Example/Chargebee/CBSDKProductsTableViewController.swift +++ b/Example/Chargebee/CBSDKProductsTableViewController.swift @@ -87,9 +87,8 @@ final class CBSDKProductsTableViewController: UITableViewController, UITextField CBPurchase.shared.validateReceipt(product,customer: nil) { result in switch result { case .success(let result): - print(result.subscriptionId ) - print(result.planId) - print(result.customerId) + print(result.status) + print(result.subscriptionDetails) if CBDemoPersistance.isPurchaseProductIDAvailable(){ CBDemoPersistance.clearPurchaseIDFromCache() @@ -283,9 +282,8 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result) switch result { case .success(let result): - print(result.subscriptionId) - print(result.planId) - print(result.customerId) + print(result.status) + print(result.subscriptionDetails) DispatchQueue.main.async { self.view.activityStopAnimating() @@ -361,9 +359,8 @@ extension CBSDKProductsTableViewController: ProductTableViewCellDelegate { print(result) switch result { case .success(let result): - print(result.subscriptionId) - print(result.planId) - print(result.customerId) + print(result.status) + print(result.subscriptionDetails) DispatchQueue.main.async {