iOS 集成 AliPay 应该从哪里开始?
结论
iOS 集成支付宝时,应从支付宝开放平台对应产品的官方文档和 iOS SDK 入手,不要直接套用网页支付接口文档。
通常的接入流程如下:
- 在支付宝开放平台创建应用,并申请所需的支付产品。
- 服务端生成支付订单,使用商户私钥完成签名。
- iOS App 获取服务端返回的完整
orderString。 - App 调用支付宝 iOS SDK 发起支付。
- 服务端接收支付宝异步通知,验签通过后更新订单状态。
问题中提到的 globalprod.alipay.com/order/integrationGuide.htm 主要面向跨境或网页支付接口。它是否适合当前业务,要看商户签约主体、用户所在地区以及已经开通的支付产品。中国大陆支付宝开放平台、Alipay+ 和国际支付宝商户平台的产品、SDK 与接口不能混用。
集成前确认业务类型
写代码前,先确认以下信息:
- 商户签约主体属于哪个国家或地区。
- 用户使用中国大陆支付宝,还是海外钱包或 Alipay+。
- 支付发生在原生 App、移动网页还是服务端。
- 商户后台实际开通了哪个支付产品。
- 支付宝后台是否已经审核并启用对应应用。
如果服务对象是中国大陆支付宝用户,通常应查找支付宝开放平台的「App 支付」产品及其 iOS SDK。
如果使用国际支付宝、Alipay+ 或跨境收单,则应以商户后台提供的产品文档为准。即使接口名称相似,网关地址、签名规则、请求参数和客户端 SDK 也可能不同。
推荐的系统结构
不要在 iOS 客户端组装和签名支付请求。客户端与服务端可以按下面的方式分工:
iOS App
│ 创建业务订单
▼
商户服务端
│ 检查金额、库存和订单状态
│ 生成支付宝请求参数
│ 使用商户私钥签名
▼
iOS App
│ 获取完整 orderString
│ 调用 AlipaySDK
▼
支付宝客户端 / 支付页面
│
├── 返回客户端支付结果
└── 异步通知商户服务端
商户私钥只能保存在服务端。把私钥、签名逻辑或可修改的订单金额放进 App,会增加密钥泄露和订单篡改的风险。
服务端生成支付订单
服务端应使用支付宝提供的服务端 SDK 构造 App 支付请求。下面的代码只说明职责和数据流,具体类名与参数应以所选平台当前版本的官方 SDK 为准:
// 示例仅展示职责和数据流
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(order.getOrderNo());
model.setSubject(order.getTitle());
model.setTotalAmount(order.getAmount().toPlainString());
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl("https://api.example.com/payments/alipay/notify");
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
// 将完整 orderString 返回给 iOS,不要只返回 biz_content
String orderString = response.getBody();
服务端至少要完成以下校验:
out_trade_no由服务端生成,并且必须唯一。total_amount取自服务端订单,不能信任客户端传入的金额。- 回调地址使用可公开访问的 HTTPS 地址。
- 相同订单的重复请求需要进行幂等处理。
- 日志中不得记录商户私钥、完整用户信息或其他敏感数据。
iOS 安装并配置 SDK
iOS SDK 的安装方式可能因支付宝平台和 SDK 版本而异。应从商户后台或官方文档下载 SDK,再根据接入说明选择 CocoaPods、Swift Package Manager 或手动集成。
原生 App 通常还要配置 URL Scheme,以便支付结束后返回应用。例如:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.payment</string>
<key>CFBundleURLSchemes</key>
<array>
<string>examplealipay</string>
</array>
</dict>
</array>
examplealipay 只是示例。实际使用的 Scheme 必须在应用内保持一致,并尽量避免与其他 App 冲突。
部分新版 SDK 或特定平台可能还要求配置 Universal Link。具体要求取决于 SDK 版本,不能只参考旧示例,应以下载 SDK 时附带的接入说明为准。
iOS 调起支付
下面是常见的 Swift 调用方式。支付宝 iOS SDK 提供的 Objective-C API 通常可以通过桥接在 Swift 中直接使用:
import UIKit
import AlipaySDK
final class PaymentService {
func pay(orderString: String) {
let appScheme = "examplealipay"
AlipaySDK.defaultService().payOrder(
orderString,
fromScheme: appScheme
) { result in
guard let result else {
print("No payment result")
return
}
let resultStatus = result["resultStatus"] as? String
let memo = result["memo"] as? String
print("resultStatus: \(resultStatus ?? "unknown")")
print("memo: \(memo ?? "")")
// 客户端结果只用于更新界面。
// 最终支付状态应向商户服务端查询。
}
}
}
调用 SDK 前,先从商户服务端获取 orderString:
func createAndPay() async throws {
let url = URL(string: "https://api.example.com/orders/123/alipay")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw PaymentError.createOrderFailed
}
let payload = try JSONDecoder().decode(PaymentPayload.self, from: data)
await MainActor.run {
PaymentService().pay(orderString: payload.orderString)
}
}
struct PaymentPayload: Decodable {
let orderString: String
}
enum PaymentError: Error {
case createOrderFailed
}
如果当前 SDK 要求应用显式处理返回 URL,可以根据项目使用的生命周期实现相应入口。下面是使用 SceneDelegate 的示例:
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let url = URLContexts.first?.url else {
return
}
AlipaySDK.defaultService().processOrder(
withPaymentResult: url
) { result in
print("Alipay callback: \(result ?? [:])")
}
}
不同版本的 SDK 可能使用不同的回调方法,也可能增加 Universal Link 参数。如果当前方法签名与示例不同,应查阅 SDK 头文件和官方示例工程,不要为了兼容旧代码而降级 SDK。
服务端处理异步通知
客户端显示「支付成功」,不等于商户已经可靠收到款项。App 可能被关闭,网络也可能中断,而且客户端返回值不能作为可信的记账依据。
服务端收到支付宝通知后,应按以下步骤处理:
- 使用支付宝公钥或平台证书验证通知签名。
- 检查通知中的应用身份和商户身份。
- 确认
out_trade_no对应的订单存在。 - 比较通知中的金额、币种与本地订单是否一致。
- 确认交易状态属于业务认可的成功状态。
- 以幂等方式更新订单。
- 按平台要求返回成功应答。
伪代码如下:
if not verifySignature(notification):
reject
order = findOrder(notification.out_trade_no)
if order does not exist:
reject
if notification.amount != order.amount:
reject
if notification.currency != order.currency:
reject
if notification indicates successful payment:
markOrderPaidIdempotently(order)
return requiredSuccessResponse
iOS 收到支付回调后,可以向商户自己的服务端查询订单状态:
func refreshOrderStatus(orderID: String) async throws -> OrderStatus {
// 调用商户服务端查询本地订单状态。
// 如果异步通知尚未到达,可由服务端按需调用支付宝查询接口核实。
fatalError("Implement with your backend API")
}
常见问题
找不到适用于 iOS 的步骤
一般是因为当前打开的是网页支付、收单网关或旧版国际站文档。先进入商户实际签约的平台,再从已经开通的产品中查找 App 支付的客户端 SDK 和服务端 API。
SDK 能拉起支付宝,但无法返回 App
可以依次检查:
Info.plist是否已经配置 URL Scheme。- 调用支付时传入的 Scheme 是否与配置完全一致。
AppDelegate或SceneDelegate是否正确处理返回 URL。- Universal Link 是否按照当前 SDK 的要求配置。
- 真机上是否安装了支付宝客户端。
客户端返回成功,服务端订单仍未支付
异步通知可能还没有送达,也可能因验签失败而未被处理。应检查服务端回调日志,并通过支付宝交易查询接口核实支付状态,不能直接相信客户端结果。
可以完全在 iOS 中完成支付吗
不建议这样做,通常也无法保证安全。订单签名、私钥管理、金额确认、异步通知验签和最终记账都应由服务端完成。
注意事项
- 不要把商户私钥打包进 iOS App。
- 不要让客户端决定最终支付金额。
- 不要根据客户端回调直接发货或充值。
- 不要混用测试环境和正式环境的应用标识、密钥、证书与网关配置。
- 支付宝公钥、应用公钥和商户私钥是不同的概念。
- 使用证书模式时,按照官方要求管理应用证书、支付宝公钥证书和根证书。
- SDK 接口可能随版本调整。集成时要同时核对官方示例工程、头文件和商户后台文档。
- 上线前至少测试支付成功、取消、失败、重复支付、网络中断、App 被杀死、异步通知重试和订单金额不匹配等场景。
备注:内容仅供参考。
