Swift集成Apple Pay:如何实现3DS验证后的支付成功回调?
问题解答
一、打开redirect_url的方式是否正确?
你当前使用UIApplication.shared.open的方式基本正确,但有几个细节需要优化:
- 必须保证
url是合法解析后的URL类型,避免字符串转URL失败:guard let redirectUrlStr = response["redirect_url"] as? String, let redirectUrl = URL(string: redirectUrlStr) else { // 处理URL解析失败逻辑 return } - UI操作需在主线程执行,建议包裹在
DispatchQueue.main.async中:DispatchQueue.main.async { UIApplication.shared.open(redirectUrl, options: [:]) { success in print("Open url : \(success)") } } - 针对3DS支付验证场景,更推荐使用
ASWebAuthenticationSession(iOS 12+),它能更好地管理网页会话,且支持验证完成后自动回调回APP:
需额外实现import AuthenticationServices let session = ASWebAuthenticationSession(url: redirectUrl, callbackURLScheme: "your-app-scheme") { callbackUrl, error in if let callbackUrl = callbackUrl { // 解析回调URL中的交易结果参数 let components = URLComponents(url: callbackUrl, resolvingAgainstBaseURL: false) let status = components?.queryItems?.first(where: { $0.name == "status" })?.value let transId = components?.queryItems?.first(where: { $0.name == "trans_id" })?.value // 根据参数更新本地订单状态 } } session.presentationContextProvider = self session.start()ASWebAuthenticationPresentationContextProviding协议提供展示上下文。
二、如何获取支付回调?
根据支付系统说明,回调分为两种常见类型,以下是对应处理方案及伪代码:
1. 服务器端回调(推荐)
支付系统会在交易完成后主动调用你预先提供的后端接口,传递交易状态、订单ID等核心信息。你需要在后端实现一个接收回调的接口:
Swift Vapor后端伪代码:
import Vapor struct PaymentCallbackRequest: Content { let order_id: String let trans_id: String let status: String // 例如SUCCESS/FAILED let trans_date: String } func routes(_ app: Application) throws { app.post("payment", "callback") { req async throws -> HTTPStatus in let callbackData = try req.content.decode(PaymentCallbackRequest.self) // 关键:验证回调签名,防止伪造请求 guard verifyCallbackSignature(req.headers, callbackData) else { return .badRequest } // 更新数据库中对应订单的状态 if let order = try await Order.query(on: req.db).filter(\.$id == callbackData.order_id).first() { order.status = callbackData.status try await order.save(on: req.db) } // 返回成功响应给支付系统 return .ok } } // 签名验证逻辑(需按支付系统提供的规则实现) func verifyCallbackSignature(_ headers: HTTPHeaders, _ data: PaymentCallbackRequest) -> Bool { let receivedSignature = headers.first(name: "X-Payment-Signature") ?? "" let generatedSignature = generateSignature(from: data, secret: "your-payment-secret") return receivedSignature == generatedSignature }
2. APP端跳转回调
若支付系统配置的是APP端回调(通过URL Scheme或Universal Links),需先配置APP的跳转能力:
步骤1:配置URL Scheme
在Xcode的Info.plist中添加:
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>your-app-scheme</string> <!-- 例如:myapp-pay --> </array> </dict> </array>
步骤2:处理跳转回调
在SceneDelegate(iOS 13+)中实现回调方法:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } // 解析回调URL中的参数 let components = URLComponents(url: url, resolvingAgainstBaseURL: false) guard let status = components?.queryItems?.first(where: { $0.name == "status" })?.value, let orderId = components?.queryItems?.first(where: { $0.name == "order_id" })?.value else { return } // 更新本地订单状态,或请求后端确认交易真实性 updateLocalOrderStatus(orderId: orderId, status: status) } // 本地更新订单状态示例 func updateLocalOrderStatus(orderId: String, status: String) { if let order = LocalDB.shared.getOrder(by: orderId) { order.status = status LocalDB.shared.saveOrder(order) // 通知UI更新 NotificationCenter.default.post(name: .orderStatusUpdated, object: order) } }
注意:无论哪种回调方式,都必须做合法性验证(如签名校验),避免恶意请求伪造交易状态。
内容的提问来源于stack exchange,提问作者imjohnnotsina
相关产品推荐
相关产品推荐

