diff --git a/Sources/Brave/Frontend/Settings/SettingsViewController.swift b/Sources/Brave/Frontend/Settings/SettingsViewController.swift index a8579fdc2012..c758b72a84e0 100644 --- a/Sources/Brave/Frontend/Settings/SettingsViewController.swift +++ b/Sources/Brave/Frontend/Settings/SettingsViewController.swift @@ -612,7 +612,7 @@ class SettingsViewController: TableViewController { case .notPurchased, .expired: return BraveVPN.vpnState.enableVPNDestinationVC case .purchased: - let vc = BraveVPNSettingsViewController() + let vc = BraveVPNSettingsViewController(iapObserver: BraveVPN.iapObserver) vc.openURL = { [unowned self] url in self.settingsDelegate?.settingsOpenURLInNewTab(url) self.dismiss(animated: true) @@ -623,7 +623,18 @@ class SettingsViewController: TableViewController { }() guard let vcToShow = vc else { return } - self.navigationController?.pushViewController(vcToShow, animated: true) + + if VPNProductInfo.isComplete { + self.navigationController?.pushViewController(vcToShow, animated: true) + } else { + let alert = UIAlertController( + title: Strings.VPN.errorCantGetPricesTitle, + message: Strings.VPN.errorCantGetPricesBody, + preferredStyle: .alert) + + alert.addAction(UIAlertAction(title: Strings.OKString, style: .default, handler: nil)) + self.present(alert, animated: true, completion: nil) + } }, image: Preferences.VPN.vpnReceiptStatus.value == BraveVPN.ReceiptResponse.Status.retryPeriod.rawValue ? UIImage(braveSystemNamed: "leo.warning.triangle-filled")? diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index a09646fa5aeb..28d1113636d6 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -2820,6 +2820,11 @@ extension Strings { value: "Manage Subscription", comment: "Button to manage your VPN subscription") + public static let settingsRedeemOfferCode = + NSLocalizedString("vpn.settingsManageSubscription", tableName: "BraveShared", bundle: .module, + value: "Redeem Offer Code", + comment: "Button to redeem offer code subscription") + public static let settingsLinkReceipt = NSLocalizedString("vpn.settingsLinkReceipt", tableName: "BraveShared", bundle: .module, value: "Link purchase to your Brave account", @@ -2904,6 +2909,11 @@ extension Strings { NSLocalizedString("vpn.vpnConfigPermissionDeniedErrorTitle", tableName: "BraveShared", bundle: .module, value: "Permission denied", comment: "Title for an alert when the user didn't allow to install VPN profile") + + public static let vpnRedeemCodeButtonActionTitle = + NSLocalizedString("vpn.vpnRedeemCodeButtonActionTitle", tableName: "BraveShared", bundle: .module, + value: "Redeem Code", + comment: "Title for a button for enabling the Redeem Code flow") public static let vpnConfigPermissionDeniedErrorBody = NSLocalizedString("vpn.vpnConfigPermissionDeniedErrorBody", tableName: "BraveShared", bundle: .module, @@ -2929,6 +2939,11 @@ extension Strings { NSLocalizedString("vpn.vpnErrorPurchaseFailedBody", tableName: "BraveShared", bundle: .module, value: "Unable to complete purchase. Please try again, or check your payment details on Apple and try again.", comment: "Message for error when VPN could not be purchased.") + + public static let vpnErrorOfferCodeFailedBody = + NSLocalizedString("vpn.vpnErrorOfferCodeFailedBody", tableName: "BraveShared", bundle: .module, + value: "Unable to redeem offer code. Please try again, or check your offer code details and try again.", + comment: "Message for error when VPN offer code could not be redeemed.") public static let vpnResetAlertTitle = NSLocalizedString("vpn.vpnResetAlertTitle", tableName: "BraveShared", bundle: .module, diff --git a/Sources/BraveVPN/BraveVPNSettingsViewController.swift b/Sources/BraveVPN/BraveVPNSettingsViewController.swift index f05c58dfc6cb..8485ec8182a3 100644 --- a/Sources/BraveVPN/BraveVPNSettingsViewController.swift +++ b/Sources/BraveVPN/BraveVPNSettingsViewController.swift @@ -15,8 +15,11 @@ import GuardianConnect public class BraveVPNSettingsViewController: TableViewController { public var openURL: ((URL) -> Void)? + let iapObserver: IAPObserver + + public init(iapObserver: IAPObserver) { + self.iapObserver = iapObserver - public init() { super.init(style: .grouped) } @@ -146,6 +149,13 @@ public class BraveVPNSettingsViewController: TableViewController { UIApplication.shared.open(url, options: [:]) } }, + cellClass: ButtonCell.self), + Row(text: Strings.VPN.settingsRedeemOfferCode, + selection: { + self.isLoading = false + // Open the redeem code sheet + SKPaymentQueue.default().presentCodeRedemptionSheet() + }, cellClass: ButtonCell.self) ] + linkReceiptRows, footer: .title(Strings.VPN.settingsLinkReceiptFooter)) @@ -339,3 +349,42 @@ public class BraveVPNSettingsViewController: TableViewController { isLoading = false } } + +// MARK: - IAPObserverDelegate + +extension BraveVPNSettingsViewController: IAPObserverDelegate { + public func purchasedOrRestoredProduct(validateReceipt: Bool) { + DispatchQueue.main.async { + self.isLoading = false + } + + if validateReceipt { + BraveVPN.validateReceiptData() + } + } + + public func purchaseFailed(error: IAPObserver.PurchaseError) { + // Handle Offer Code error + guard isLoading else { + return + } + + handleOfferCodeError(error: error) + } + + private func handleOfferCodeError(error: IAPObserver.PurchaseError) { + DispatchQueue.main.async { + self.isLoading = false + + let message = Strings.VPN.vpnErrorOfferCodeFailedBody + + let alert = UIAlertController( + title: Strings.VPN.vpnErrorPurchaseFailedTitle, + message: message, + preferredStyle: .alert) + let ok = UIAlertAction(title: Strings.OKString, style: .default, handler: nil) + alert.addAction(ok) + self.present(alert, animated: true) + } + } +} diff --git a/Sources/BraveVPN/BuyVPNViewController.swift b/Sources/BraveVPN/BuyVPNViewController.swift index fb5070d801ed..83f105000804 100644 --- a/Sources/BraveVPN/BuyVPNViewController.swift +++ b/Sources/BraveVPN/BuyVPNViewController.swift @@ -44,15 +44,15 @@ class BuyVPNViewController: VPNSetupLoadingController { title: Strings.VPN.restorePurchases, style: .done, target: self, action: #selector(restorePurchasesAction)) - let actionTitle = Preferences.VPN.freeTrialUsed.value + let buyTitle = Preferences.VPN.freeTrialUsed.value ? Strings.VPN.activateSubscriptionAction.capitalized : Strings.VPN.freeTrialPeriodAction.capitalized - let actionButton = BraveGradientButton(gradient: .backgroundGradient1).then { + let buyButton = BraveGradientButton(gradient: .backgroundGradient1).then { $0.titleLabel?.font = .systemFont(ofSize: 15, weight: .bold) $0.titleLabel?.textAlignment = .center - $0.setTitle(actionTitle, for: .normal) + $0.setTitle(buyTitle, for: .normal) $0.snp.makeConstraints { $0.height.equalTo(50) @@ -66,7 +66,15 @@ class BuyVPNViewController: VPNSetupLoadingController { $0.addTarget(self, action: #selector(startSubscriptionAction), for: .touchUpInside) } - + + let redeemButton = UIButton().then { + $0.setTitle(Strings.VPN.vpnRedeemCodeButtonActionTitle, for: .normal) + $0.titleLabel?.font = .systemFont(ofSize: 13, weight: .medium) + $0.titleLabel?.textAlignment = .center + + $0.addTarget(self, action: #selector(redeemOfferSubscriptionCode), for: .touchUpInside) + } + let seperator = UIView().then { $0.backgroundColor = UIColor.white.withAlphaComponent(0.1) $0.snp.makeConstraints { make in @@ -76,7 +84,8 @@ class BuyVPNViewController: VPNSetupLoadingController { view.addSubview(buyVPNView) view.addSubview(seperator) - view.addSubview(actionButton) + view.addSubview(buyButton) + view.addSubview(redeemButton) buyVPNView.snp.makeConstraints { $0.leading.trailing.top.equalToSuperview() @@ -87,9 +96,15 @@ class BuyVPNViewController: VPNSetupLoadingController { $0.leading.trailing.equalToSuperview() } - actionButton.snp.makeConstraints() { + buyButton.snp.makeConstraints() { $0.top.equalTo(seperator.snp.bottom).inset(-12) - $0.leading.trailing.bottom.equalToSuperview().inset(24) + $0.leading.trailing.equalToSuperview().inset(24) + } + + redeemButton.snp.makeConstraints() { + $0.top.equalTo(buyButton.snp.bottom).inset(-12) + $0.bottom.equalToSuperview().inset(24) + $0.centerX.equalToSuperview() } buyVPNView.monthlySubButton @@ -154,6 +169,11 @@ class BuyVPNViewController: VPNSetupLoadingController { addPaymentForSubcription(type: activeSubcriptionChoice) } + @objc func redeemOfferSubscriptionCode() { + // Open the redeem code sheet + SKPaymentQueue.default().presentCodeRedemptionSheet() + } + private func addPaymentForSubcription(type: SubscriptionType) { var subscriptionProduct: SKProduct?