|
...
|
...
|
@@ -26,6 +26,15 @@ enum StoreKitError: Error, LocalizedError { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct IAPRestoreTransactions: Codable {
|
|
|
|
var product_id: String?
|
|
|
|
var transaction_id: String?
|
|
|
|
var original_transaction_id: String?
|
|
|
|
var app_account_token: String?
|
|
|
|
var web_order_line_item_id: String?
|
|
|
|
}
|
|
|
|
|
|
|
|
class ApplePayment {
|
|
|
|
var delegate: AppDelegate?
|
|
|
|
|
|
...
|
...
|
@@ -217,6 +226,47 @@ class ApplePayment { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func verifyRestoreWithServer(transaction: IAPRestoreTransactions) async -> Bool {
|
|
|
|
guard AppShared.shared.isLogin else {
|
|
|
|
print("Apple payment restore verify skipped: missing login info")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let baseURL = URL(string: AppShared.shared.baseUrl),
|
|
|
|
let url = URL(string: "/client/doublefeel/payment/order_restore/apple/", relativeTo: baseURL)?.absoluteURL else {
|
|
|
|
print("Apple payment restore verify failed: invalid baseUrl \(AppShared.shared.baseUrl)")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
var request = URLRequest(url: url)
|
|
|
|
request.httpMethod = "POST"
|
|
|
|
request.timeoutInterval = 30
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
|
|
request.setValue(AppShared.shared.userSummary?.accessToken, forHTTPHeaderField: "access_token")
|
|
|
|
request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent")
|
|
|
|
|
|
|
|
do {
|
|
|
|
request.httpBody = try JSONEncoder().encode(transaction)
|
|
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
|
|
guard let httpResponse = response as? HTTPURLResponse else {
|
|
|
|
print("Apple payment restore verify failed: invalid response")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
let bodyText = String(data: data, encoding: .utf8) ?? ""
|
|
|
|
print("Apple payment restore verify response: status=\(httpResponse.statusCode), body=\(bodyText)")
|
|
|
|
|
|
|
|
guard (200..<300).contains(httpResponse.statusCode) else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
} catch {
|
|
|
|
print("Apple payment restore verify failed: \(error.localizedDescription)")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func paymentResult(
|
|
|
|
productId: String,
|
|
|
|
appAccountToken: String? = nil,
|
|
...
|
...
|
@@ -241,6 +291,42 @@ class ApplePayment { |
|
|
|
}
|
|
|
|
return error.localizedDescription
|
|
|
|
}
|
|
|
|
|
|
|
|
func restore() async -> Bool {
|
|
|
|
var transactions = [Transaction]()
|
|
|
|
for await result in Transaction.currentEntitlements {
|
|
|
|
if case let .verified(trans) = result {
|
|
|
|
|
|
|
|
if trans.appAccountToken != nil &&
|
|
|
|
trans.revocationDate == nil &&
|
|
|
|
trans.productType == .autoRenewable {
|
|
|
|
transactions.append(trans)
|
|
|
|
}else {
|
|
|
|
await trans.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let restoreTransaction = transactions.compactMap({ IAPRestoreTransactions(
|
|
|
|
product_id: $0.productID,
|
|
|
|
transaction_id: "\($0.id)",
|
|
|
|
original_transaction_id: "\($0.originalID)",
|
|
|
|
app_account_token: $0.appAccountToken?.uuidString.lowercased() ?? "",
|
|
|
|
web_order_line_item_id: $0.webOrderLineItemID ?? "") }
|
|
|
|
).last else{
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
guard await verifyRestoreWithServer(transaction: restoreTransaction) else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for tran in transactions {
|
|
|
|
await tran.finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension ApplePayment {
|
...
|
...
|
|