From 2be77b1a293a7ce2ef7496de42069ae431c7233c Mon Sep 17 00:00:00 2001 From: Brandon T Date: Tue, 22 Oct 2024 16:52:40 -0400 Subject: [PATCH] Add proper error handling to VPN code so it throws an exception when something wrong happens instead of just returning null. --- ios/brave-ios/Sources/BraveVPN/BraveVPN.swift | 15 ++++++---- .../BuyVPN/BuyVPNViewController.swift | 2 +- .../BraveVPNSettingsViewController.swift | 2 +- .../BraveVPN+ReceiptResponse.swift | 25 ++++++++++++---- .../BraveVPNInAppPurchaseObserver.swift | 29 +++++++++++-------- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/ios/brave-ios/Sources/BraveVPN/BraveVPN.swift b/ios/brave-ios/Sources/BraveVPN/BraveVPN.swift index 4dec8278376c..b063c113e554 100644 --- a/ios/brave-ios/Sources/BraveVPN/BraveVPN.swift +++ b/ios/brave-ios/Sources/BraveVPN/BraveVPN.swift @@ -74,12 +74,15 @@ public class BraveVPN { } Task { - let receiptResponse = await validateReceiptData() - // Clear configuration if only if the receipt response is expired (not retryPeriod) - if receiptResponse?.status == .expired { - clearConfiguration() - logAndStoreError("Receipt expired") - return + do { + let receiptResponse = try await validateReceiptData() + // Clear configuration if only if the receipt response is expired (not retryPeriod) + if receiptResponse?.status == .expired { + clearConfiguration() + logAndStoreError("Receipt expired") + } + } catch { + logAndStoreError(String(describing: error)) } } } diff --git a/ios/brave-ios/Sources/BraveVPN/Components/BuyVPN/BuyVPNViewController.swift b/ios/brave-ios/Sources/BraveVPN/Components/BuyVPN/BuyVPNViewController.swift index c65cbdae0e12..2cbf0db43366 100644 --- a/ios/brave-ios/Sources/BraveVPN/Components/BuyVPN/BuyVPNViewController.swift +++ b/ios/brave-ios/Sources/BraveVPN/Components/BuyVPN/BuyVPNViewController.swift @@ -222,7 +222,7 @@ extension BuyVPNViewController: BraveVPNInAppPurchaseObserverDelegate { if validateReceipt { Task { - _ = await BraveVPN.validateReceiptData() + _ = try await BraveVPN.validateReceiptData() } } } diff --git a/ios/brave-ios/Sources/BraveVPN/Components/Settings/BraveVPNSettingsViewController.swift b/ios/brave-ios/Sources/BraveVPN/Components/Settings/BraveVPNSettingsViewController.swift index 1c59cd0be064..9416aa292ee2 100644 --- a/ios/brave-ios/Sources/BraveVPN/Components/Settings/BraveVPNSettingsViewController.swift +++ b/ios/brave-ios/Sources/BraveVPN/Components/Settings/BraveVPNSettingsViewController.swift @@ -452,7 +452,7 @@ extension BraveVPNSettingsViewController: BraveVPNInAppPurchaseObserverDelegate if validateReceipt { Task { - _ = await BraveVPN.validateReceiptData() + _ = try await BraveVPN.validateReceiptData() } } } diff --git a/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPN+ReceiptResponse.swift b/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPN+ReceiptResponse.swift index 12b36ef966d9..d8b894440e1f 100644 --- a/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPN+ReceiptResponse.swift +++ b/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPN+ReceiptResponse.swift @@ -8,6 +8,19 @@ import Preferences extension BraveVPN { + public enum BraveVPNError: Error { + case noReceipt + case noReceiptData + case noReceiptStatus + case noReceiptExpiryDate + case noReceiptGraceExpiryDate + case noReceiptIsInTrialPeriod + case noReceiptAutoRenewEnabled + case noReceiptExpiryIntent + case noReceiptExpiryIntentUnknown + case noResponse + } + public struct ReceiptResponse { public enum Status: Int { case active, expired, retryPeriod @@ -35,12 +48,12 @@ extension BraveVPN { } /// Connects to Guardian's server to validate locally stored receipt. - /// Returns ReceiptResponse whoich hold information about status of receipt expiration etc - public static func validateReceiptData() async -> ReceiptResponse? { + /// Returns ReceiptResponse which hold information about status of receipt expiration etc + public static func validateReceiptData() async throws -> ReceiptResponse? { guard let receipt = await loadReceipt(), let bundleId = Bundle.main.bundleIdentifier else { - return nil + throw BraveVPNError.noReceipt } if Preferences.VPN.skusCredential.value != nil { @@ -49,7 +62,7 @@ extension BraveVPN { return nil } - return await withCheckedContinuation { continuation in + return try await withCheckedThrowingContinuation { continuation in housekeepingApi.verifyReceiptData(receipt, bundleId: bundleId) { response, error in if let error = error { // Error while fetching receipt response, the variations of error can be listed @@ -57,13 +70,13 @@ extension BraveVPN { // Failed to retrieve receipt data from server // Failed to decode JSON response data logAndStoreError("Call for receipt verification failed: \(error.localizedDescription)") - continuation.resume(returning: nil) + continuation.resume(throwing: error) return } guard let response = response else { logAndStoreError("Receipt verification response is empty") - continuation.resume(returning: nil) + continuation.resume(throwing: BraveVPNError.noResponse) return } diff --git a/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPNInAppPurchaseObserver.swift b/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPNInAppPurchaseObserver.swift index fd891a6cf279..013b799b2089 100644 --- a/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPNInAppPurchaseObserver.swift +++ b/ios/brave-ios/Sources/BraveVPN/Components/Subscription/BraveVPNInAppPurchaseObserver.swift @@ -71,18 +71,23 @@ public class BraveVPNInAppPurchaseObserver: NSObject, SKPaymentTransactionObserv Preferences.VPN.subscriptionProductId.value = transaction.payment.productIdentifier Task { - let response = await BraveVPN.validateReceiptData() - if response?.status == .expired { - // Receipt either expired or receipt validation returned some error. - self.delegate?.purchaseFailed(error: .receiptError) - } else { - self.delegate?.purchasedOrRestoredProduct(validateReceipt: false) - // If we purchased via Apple's IAP we reset the Brave SKUs credential - // to avoid mixing two purchase types in the app. - // - // The user will be able to retrieve the shared credential - // after log in to account.brave website. - BraveVPN.clearSkusCredentials(includeExpirationDate: false) + do { + let response = try await BraveVPN.validateReceiptData() + if response?.status == .expired { + // Receipt either expired or receipt validation returned some error. + self.delegate?.purchaseFailed(error: .receiptError) + } else { + self.delegate?.purchasedOrRestoredProduct(validateReceipt: false) + // If we purchased via Apple's IAP we reset the Brave SKUs credential + // to avoid mixing two purchase types in the app. + // + // The user will be able to retrieve the shared credential + // after log in to account.brave website. + BraveVPN.clearSkusCredentials(includeExpirationDate: false) + } + } catch { + Logger.module.error("Error validating receipt: \(error)") + self.delegate?.purchaseFailed(error: .transactionError(error: SKError(.unknown))) } } }