105 lines
4.3 KiB
Swift
105 lines
4.3 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import DypaySDK
|
|
|
|
public class TbDouyinPayNative {
|
|
private static var pending: ((String) -> Void)?
|
|
private static var generation = UUID()
|
|
|
|
public static func initialize(_ appId: String, _ scheme: String, _ link: String) {
|
|
if link.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
// Use the SDK's Scheme-only API instead of registering an empty
|
|
// Universal Link with the higher-priority Universal Link API.
|
|
DypayAPI.register(withAppID: appId, callbackScheme: scheme)
|
|
} else {
|
|
DypayAPI.register(withAppID: appId, universalLink: link, callbackScheme: scheme)
|
|
}
|
|
}
|
|
|
|
public static func available() -> Bool { DypayAPI.canOpenDypay() }
|
|
|
|
public static func resetPending() {
|
|
DispatchQueue.main.async {
|
|
generation = UUID()
|
|
pending = nil
|
|
}
|
|
}
|
|
|
|
public static func pay(_ payload: String, _ done: @escaping (String) -> Void) {
|
|
DispatchQueue.main.async {
|
|
guard pending == nil else {
|
|
done("{\"resultCode\":\"103\",\"errorMsg\":\"已有支付正在处理,请先查单\"}")
|
|
return
|
|
}
|
|
guard DypayAPI.canOpenDypay() else {
|
|
done("{\"resultCode\":\"100\",\"errorMsg\":\"请安装或升级抖音客户端\"}")
|
|
return
|
|
}
|
|
guard let bytes = payload.data(using: .utf8),
|
|
let info = try? JSONSerialization.jsonObject(with: bytes) as? [String: String],
|
|
let controller = foregroundController() else {
|
|
done("{\"resultCode\":\"2\",\"errorMsg\":\"支付参数或当前页面不可用\"}")
|
|
return
|
|
}
|
|
generation = UUID()
|
|
let ticket = generation
|
|
pending = done
|
|
DypayAPI.openDypay(withInfo: info, from: controller) { result in
|
|
finish(result as? [AnyHashable: Any], ticket)
|
|
}
|
|
// Only authenticated server reconciliation can release a missing callback's lock.
|
|
// URL callbacks are hints, never payment success; the host always queries its saved order.
|
|
}
|
|
}
|
|
|
|
public static func processURL(_ url: URL) -> Bool {
|
|
let ticket = generation
|
|
return DypayAPI.processDypayResult(with: url) { result in
|
|
finish(result as? [AnyHashable: Any], ticket)
|
|
}
|
|
}
|
|
|
|
public static func processActivity(_ activity: NSUserActivity) -> Bool {
|
|
let ticket = generation
|
|
return DypayAPI.processDypayResult(with: activity) { result in
|
|
finish(result as? [AnyHashable: Any], ticket)
|
|
}
|
|
}
|
|
|
|
private static func finish(_ result: [AnyHashable: Any]?, _ ticket: UUID) {
|
|
DispatchQueue.main.async {
|
|
guard ticket == generation, let callback = pending else { return }
|
|
pending = nil
|
|
let code = result?["resultCode"].map { String(describing: $0) } ?? "3"
|
|
let message = result?["errorMsg"] as? String ?? ""
|
|
let normalized = ["resultCode": code, "errorMsg": message]
|
|
guard let data = try? JSONSerialization.data(withJSONObject: normalized),
|
|
let json = String(data: data, encoding: .utf8) else {
|
|
callback("{\"resultCode\":\"3\",\"errorMsg\":\"请查询订单状态\"}")
|
|
return
|
|
}
|
|
callback(json)
|
|
}
|
|
}
|
|
|
|
private static func foregroundController() -> UIViewController? {
|
|
var windows: [UIWindow] = []
|
|
if #available(iOS 13.0, *) {
|
|
windows = UIApplication.shared.connectedScenes
|
|
.compactMap { $0 as? UIWindowScene }
|
|
.filter { $0.activationState == .foregroundActive }
|
|
.flatMap { $0.windows }
|
|
} else {
|
|
windows = UIApplication.shared.windows
|
|
}
|
|
var controller = windows.first(where: { $0.isKeyWindow })?.rootViewController
|
|
while let current = controller {
|
|
if let presented = current.presentedViewController { controller = presented }
|
|
else if let nav = current as? UINavigationController { controller = nav.visibleViewController }
|
|
else if let tabs = current as? UITabBarController { controller = tabs.selectedViewController }
|
|
else { break }
|
|
}
|
|
return controller
|
|
}
|
|
}
|