-
Notifications
You must be signed in to change notification settings - Fork 95
Expand file tree
/
Copy pathProductPurchasable.swift
More file actions
239 lines (227 loc) · 12 KB
/
ProductPurchasable.swift
File metadata and controls
239 lines (227 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//
// ProductPurchasable.swift
// Lockdown
//
// Created by Aliaksandr Dvoineu on 4.05.23.
// Copyright © 2023 Confirmed Inc. All rights reserved.
//
import CocoaLumberjackSwift
import Foundation
import PromiseKit
import StoreKit
protocol ProductPurchasable: Loadable {
func purchaseProduct(withId productId: String, onSuccess: (() -> Void)?, onFailure: (() -> Void)?)
func restorePurchases()
}
extension ProductPurchasable where Self: BaseViewController {
func purchaseProduct(withId productId: String = VPNSubscription.selectedProductId,
onSuccess: (() -> Void)? = nil,
onFailure: (() -> Void)? = nil) {
VPNSubscription.selectedProductId = productId
showLoadingView()
VPNSubscription.purchase(
succeeded: {
BaseUserService.shared.updateUserSubscription { [weak self] _ in
self?.hideLoadingView()
self?.handleSuccessfulPurchase()
onSuccess?()
}
},
errored: { error in
onFailure?()
self.hideLoadingView()
DDLogError("Start Trial Failed: \(error)")
if let skError = error as? SKError {
var errorText = ""
switch skError.code {
case .unknown:
errorText = .localized("Unknown error. Please contact support at team@lockdownprivacy.com.")
case .clientInvalid:
errorText = .localized("Not allowed to make the payment")
case .paymentCancelled:
errorText = .localized("Payment was cancelled")
case .paymentInvalid:
errorText = .localized("The purchase identifier was invalid")
case .paymentNotAllowed:
errorText = .localized("""
Payment not allowed.\nEither this device is not allowed to make purchases, or In-App Purchases have been disabled. \
Please allow them in Settings App -> Screen Time -> Restrictions -> App Store -> In-app Purchases. Then try again.
""")
case .storeProductNotAvailable:
errorText = .localized("The product is not available in the current storefront")
case .cloudServicePermissionDenied:
errorText = .localized("Access to cloud service information is not allowed")
case .cloudServiceNetworkConnectionFailed:
errorText = .localized("Could not connect to the network")
case .cloudServiceRevoked:
errorText = .localized("User has revoked permission to use this cloud service")
default:
errorText = skError.localizedDescription
}
self.showPopupDialog(title: .localized("Error Making Purchase"), message: errorText, acceptButton: .localizedOkay)
} else if self.popupErrorAsNSURLError(error) {
return
} else if self.popupErrorAsApiError(error) {
return
} else {
self.showPopupDialog(
title: .localized("Error Making Purchase"),
message: .localized("Please contact team@lockdownprivacy.com.\n\nError details:\n") + "\(error)",
acceptButton: .localizedOkay)
}
})
}
func restorePurchases() {
showLoadingView()
firstly {
try Client.signIn(forceRefresh: true)
}
.done { _ in
// we were able to get key, so subscription is valid -- follow pathway from HomeViewController to associate this with the email account if there is one
self.dismiss(animated: true, completion: {
self.hideLoadingView()
let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
let vc = SplashScreenViewController()
let navigation = UINavigationController(rootViewController: vc)
keyWindow?.rootViewController = navigation
})
}
.catch { error in
self.hideLoadingView()
DDLogError("Restore Failed: \(error)")
if let apiError = error as? ApiError {
switch apiError.code {
case kApiCodeNoSubscriptionInReceipt, kApiCodeNoActiveSubscription:
// now try email if it exists
if let apiCredentials = getAPICredentials(), getAPICredentialsConfirmed() == true {
DDLogInfo("restore: have confirmed API credentials, using them")
self.showLoadingView()
firstly {
try Client.signInWithEmail(email: apiCredentials.email, password: apiCredentials.password)
}
.then { (signin: SignIn) -> Promise<GetKey> in
DDLogInfo("restore: signin result: \(signin)")
return try Client.getKey()
}
.done { (getKey: GetKey) in
BaseUserService.shared.updateUserSubscription { [weak self] _ in
self?.hideLoadingView()
do {
try setVPNCredentials(id: getKey.id, keyBase64: getKey.b64)
} catch {
DDLogInfo("restore: setting VPN creds with ID and Dismissing: \(getKey.id)")
let presentingViewController = self?.presentingViewController as? HomeViewController
self?.dismiss(animated: true, completion: {
if presentingViewController != nil {
presentingViewController?.toggleVPN("me")
} else {
// VPNController.shared.setEnabled(true)
}
})
}
}
}
.catch { error in
self.hideLoadingView()
DDLogError("restore: Error doing restore with email-login: \(error)")
if self.popupErrorAsNSURLError(error) {
return
} else if let apiError = error as? ApiError {
switch apiError.code {
case kApiCodeNoSubscriptionInReceipt, kApiCodeNoActiveSubscription:
self.showPopupDialog(title: .localized("No Active Subscription"),
message: .localized("""
Please ensure that you have an active subscription. If you're attempting to share a subscription from the same account, \
you'll need to sign in with the same email address. Otherwise, start your free trial or e-mail team@lockdownprivacy.com
"""),
acceptButton: .localizedOK)
default:
_ = self.popupErrorAsApiError(error)
}
}
}
} else {
let message = """
Please ensure that you have an active subscription. If you're attempting to share a subscription from the same account, \
you'll need to sign in with the same email address. Otherwise, start your free trial or e-mail team@lockdownprivacy.com
"""
self.showPopupDialog(title: .localized("No Active Subscription"),
message: .localized(message),
acceptButton: .localizedOK)
}
default:
let pleaseEmail: String = .localized("Please email team@lockdownprivacy.com with the following Error Code ")
self.showPopupDialog(
title: .localized("Error Restoring Subscription"),
message: pleaseEmail + "\(apiError.code) : \(apiError.message)",
acceptButton: .localizedOK)
}
} else {
let message: String = .localized("""
Please make sure your Internet connection is active. If this error persists, email team@lockdownprivacy.com with
the following error message: \
""")
self.showPopupDialog(title: .localized("Error Restoring Subscription"), message: message + "\(error)", acceptButton: .localizedOK)
}
}
}
// MARK: - Handling Purchase Results
private func handleSuccessfulPurchase() {
dismiss(animated: true, completion: {
// force refresh receipt, and sync with email if it exists, activate VPNte
if let apiCredentials = getAPICredentials(), getAPICredentialsConfirmed() == true {
DDLogInfo("purchase complete: syncing with confirmed email")
firstly {
try Client.signInWithEmail(email: apiCredentials.email, password: apiCredentials.password)
}
.then { (signin: SignIn) -> Promise<SubscriptionEvent> in
DDLogInfo("purchase complete: signin result: \(signin)")
return try Client.subscriptionEvent(forceRefresh: true)
}
.then { (result: SubscriptionEvent) -> Promise<GetKey> in
DDLogInfo("purchase complete: subscriptionevent result: \(result)")
return try Client.getKey()
}
.done { (getKey: GetKey) in
try setVPNCredentials(id: getKey.id, keyBase64: getKey.b64)
DDLogInfo("purchase complete: setting VPN creds with ID: \(getKey.id)")
VPNController.shared.setEnabled(true)
}
.catch { error in
DDLogError("purchase complete: Error: \(error)")
if self.popupErrorAsNSURLError("Error activating Secure Tunnel: \(error)") {
return
} else if let apiError = error as? ApiError {
switch apiError.code {
default:
_ = self.popupErrorAsApiError("API Error activating Secure Tunnel: \(error)")
}
}
}
} else {
firstly {
try Client.signIn(forceRefresh: true) // this will fetch and set latest receipt, then submit to API to get cookie
}
.then { _ in
// TODO: don't always do this -- if we already have a key, then only do it once per day max
try Client.getKey()
}
.done { (getKey: GetKey) in
try setVPNCredentials(id: getKey.id, keyBase64: getKey.b64)
VPNController.shared.setEnabled(true)
}
.catch { error in
DDLogError("purchase complete - no email: Error: \(error)")
if self.popupErrorAsNSURLError("Error activating Secure Tunnel: \(error)") {
return
} else if let apiError = error as? ApiError {
switch apiError.code {
default:
_ = self.popupErrorAsApiError("API Error activating Secure Tunnel: \(error)")
}
}
}
}
})
}
}