diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-03-21 16:53:13 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-03-25 10:14:30 +0100 |
| commit | 601c9873c57259e008d8adc9cc57ffb88691d49a (patch) | |
| tree | 8e17044ae77e913ffa3fe4ff770034e35850a3bd | |
| parent | 3755fd206a8c2220a0f5ebdfc6436338022c32da (diff) | |
| download | mullvadvpn-601c9873c57259e008d8adc9cc57ffb88691d49a.tar.xz mullvadvpn-601c9873c57259e008d8adc9cc57ffb88691d49a.zip | |
Validate account token before adding payment to the payment queue
7 files changed, 75 insertions, 19 deletions
diff --git a/ios/MullvadVPN/Account.swift b/ios/MullvadVPN/Account.swift index 3d6ac59d29..f6f0ab141a 100644 --- a/ios/MullvadVPN/Account.swift +++ b/ios/MullvadVPN/Account.swift @@ -259,7 +259,7 @@ extension Account: AppStorePaymentObserver { paymentManager.addPaymentObserver(self) } - func appStorePaymentManager(_ manager: AppStorePaymentManager, transaction: SKPaymentTransaction, accountToken: String?, didFailWithError error: AppStorePaymentManager.Error) { + func appStorePaymentManager(_ manager: AppStorePaymentManager, transaction: SKPaymentTransaction?, payment: SKPayment, accountToken: String?, didFailWithError error: AppStorePaymentManager.Error) { // no-op } diff --git a/ios/MullvadVPN/AccountViewController.swift b/ios/MullvadVPN/AccountViewController.swift index 34cc20345c..88a34167f5 100644 --- a/ios/MullvadVPN/AccountViewController.swift +++ b/ios/MullvadVPN/AccountViewController.swift @@ -319,7 +319,7 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, AccountO // MARK: - AppStorePaymentObserver - func appStorePaymentManager(_ manager: AppStorePaymentManager, transaction: SKPaymentTransaction, accountToken: String?, didFailWithError error: AppStorePaymentManager.Error) { + func appStorePaymentManager(_ manager: AppStorePaymentManager, transaction: SKPaymentTransaction?, payment: SKPayment, accountToken: String?, didFailWithError error: AppStorePaymentManager.Error) { let alertController = UIAlertController( title: NSLocalizedString( "CANNOT_COMPLETE_PURCHASE_ALERT_TITLE", @@ -343,7 +343,7 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, AccountO alertPresenter.enqueue(alertController, presentingController: self) - if transaction.payment == pendingPayment { + if payment == pendingPayment { compoundInteractionRestriction.decrease(animated: true) } } diff --git a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift index 709ebff131..8c0567c777 100644 --- a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift +++ b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift @@ -109,12 +109,33 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { } func addPayment(_ payment: SKPayment, for accountToken: String) { - if Thread.isMainThread { - _addPayment(payment, for: accountToken) - } else { - DispatchQueue.main.async { - self._addPayment(payment, for: accountToken) + var cancellableTask: Cancellable? + let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "Validate account token") { + cancellableTask?.cancel() + } + + // Validate account token before adding new payment to the queue. + cancellableTask = REST.Client.shared.getAccountExpiry(token: accountToken, retryStrategy: .default) { result in + dispatchPrecondition(condition: .onQueue(.main)) + + switch result { + case .success: + self.associateAccountToken(accountToken, and: payment) + self.paymentQueue.add(payment) + + case .failure(let error): + self.observerList.forEach { observer in + observer.appStorePaymentManager( + self, + transaction: nil, + payment: payment, + accountToken: accountToken, + didFailWithError: .validateAccount(error) + ) + } } + + UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier) } } @@ -142,13 +163,6 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { } } - private func _addPayment(_ payment: SKPayment, for accountToken: String) { - assert(Thread.isMainThread) - - associateAccountToken(accountToken, and: payment) - paymentQueue.add(payment) - } - private func sendAppStoreReceipt(accountToken: String, forceRefresh: Bool, completionHandler: @escaping (OperationCompletion<REST.CreateApplePaymentResponse, Error>) -> Void) -> Cancellable { let operation = SendAppStoreReceiptOperation(restClient: REST.Client.shared, accountToken: accountToken, forceRefresh: forceRefresh, receiptProperties: nil) { completion in completionHandler(completion) @@ -207,18 +221,20 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { paymentQueue.finishTransaction(transaction) if let accountToken = deassociateAccountToken(transaction.payment) { - observerList.forEach { (observer) in + observerList.forEach { observer in observer.appStorePaymentManager( self, transaction: transaction, + payment: transaction.payment, accountToken: accountToken, didFailWithError: .storePayment(transaction.error!)) } } else { - observerList.forEach { (observer) in + observerList.forEach { observer in observer.appStorePaymentManager( self, transaction: transaction, + payment: transaction.payment, accountToken: nil, didFailWithError: .noAccountSet) } @@ -227,10 +243,11 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { private func didFinishOrRestorePurchase(transaction: SKPaymentTransaction) { guard let accountToken = deassociateAccountToken(transaction.payment) else { - observerList.forEach { (observer) in + observerList.forEach { observer in observer.appStorePaymentManager( self, transaction: transaction, + payment: transaction.payment, accountToken: nil, didFailWithError: .noAccountSet) } @@ -255,6 +272,7 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { observer.appStorePaymentManager( self, transaction: transaction, + payment: transaction.payment, accountToken: accountToken, didFailWithError: error) } diff --git a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift index 5d395c4e48..5e1be9b6fc 100644 --- a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift +++ b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift @@ -14,6 +14,9 @@ extension AppStorePaymentManager { /// Failure to find the account token associated with the transaction. case noAccountSet + /// Failure to validate the account token. + case validateAccount(REST.Error) + /// Failure to handle payment transaction. Contains error returned by StoreKit. case storePayment(Swift.Error) @@ -27,6 +30,8 @@ extension AppStorePaymentManager { switch self { case .noAccountSet: return "Account is not set" + case .validateAccount: + return "Account validation error" case .storePayment: return "Store payment error" case .readReceipt: diff --git a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentObserver.swift b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentObserver.swift index 9aa4cc9481..ad37c707c4 100644 --- a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentObserver.swift +++ b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentObserver.swift @@ -10,9 +10,11 @@ import Foundation import StoreKit protocol AppStorePaymentObserver: AnyObject { + func appStorePaymentManager( _ manager: AppStorePaymentManager, - transaction: SKPaymentTransaction, + transaction: SKPaymentTransaction?, + payment: SKPayment, accountToken: String?, didFailWithError error: AppStorePaymentManager.Error) diff --git a/ios/MullvadVPN/DisplayChainedError.swift b/ios/MullvadVPN/DisplayChainedError.swift index 8baef34ece..aab3fce95e 100644 --- a/ios/MullvadVPN/DisplayChainedError.swift +++ b/ios/MullvadVPN/DisplayChainedError.swift @@ -343,6 +343,31 @@ extension AppStorePaymentManager.Error: DisplayChainedError { comment: "" ) + case .validateAccount(let restError): + let reason = restError.errorChainDescription ?? "" + + if case .server(.invalidAccount) = restError { + return String( + format: NSLocalizedString( + "INVALID_ACCOUNT_ERROR", + tableName: "AppStorePaymentManager", + value: "Cannot add credit to invalid account.", + comment: "" + ), reason + ) + } else { + let reason = restError.errorChainDescription ?? "" + + return String( + format: NSLocalizedString( + "VALIDATE_ACCOUNT_ERROR", + tableName: "AppStorePaymentManager", + value: "Failed to validate account token: %@", + comment: "" + ), reason + ) + } + case .readReceipt(let readReceiptError): switch readReceiptError { case .refresh(let storeError): diff --git a/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings b/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings index 54c9c348c4..a168dfc6ff 100644 --- a/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings +++ b/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings @@ -1,4 +1,7 @@ /* No comment provided by engineer. */ +"INVALID_ACCOUNT_ERROR" = "Cannot add credit to invalid account."; + +/* No comment provided by engineer. */ "NO_ACCOUNT_SET_ERROR" = "Internal error: account is not set"; /* No comment provided by engineer. */ @@ -15,3 +18,6 @@ /* No comment provided by engineer. */ "SEND_RECEIPT_RECOVERY_SUGGESTION" = "Please retry by using the \"Restore purchases\" button."; + +/* No comment provided by engineer. */ +"VALIDATE_ACCOUNT_ERROR" = "Failed to validate account token: %@"; |
