summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-03-21 16:53:13 +0100
committerAndrej Mihajlov <and@mullvad.net>2022-03-25 10:14:30 +0100
commit601c9873c57259e008d8adc9cc57ffb88691d49a (patch)
tree8e17044ae77e913ffa3fe4ff770034e35850a3bd
parent3755fd206a8c2220a0f5ebdfc6436338022c32da (diff)
downloadmullvadvpn-601c9873c57259e008d8adc9cc57ffb88691d49a.tar.xz
mullvadvpn-601c9873c57259e008d8adc9cc57ffb88691d49a.zip
Validate account token before adding payment to the payment queue
-rw-r--r--ios/MullvadVPN/Account.swift2
-rw-r--r--ios/MullvadVPN/AccountViewController.swift4
-rw-r--r--ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift48
-rw-r--r--ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift5
-rw-r--r--ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentObserver.swift4
-rw-r--r--ios/MullvadVPN/DisplayChainedError.swift25
-rw-r--r--ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings6
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: %@";