如何在 iOS 项目中按正确步骤集成 Alipay?
结论
iOS 接入支付宝支付,一般按以下流程完成:
- 在支付宝开放平台创建应用并开通支付能力。
- 配置应用公钥、签名方式和服务端异步通知地址。
- 将支付宝官方 iOS SDK 加入项目。
- 配置独立的 URL Scheme。
- 由服务端创建支付订单,并使用 RSA2 签名。
- 客户端调用
AlipaySDK发起支付。 - 在应用的回调入口处理支付宝返回。
- 服务端验证异步通知的签名,并以服务端查询结果或异步通知为最终支付依据。
接入时常见的问题包括:在客户端保存商户私钥或生成签名、URL Scheme 配置不一致、遗漏回调处理,以及直接将客户端返回结果视为支付成功。
一、准备支付宝开放平台配置
先在支付宝开放平台创建应用,并确认应用已获得所需支付产品的权限。通常需要准备:
app_id- 商户签约信息
- RSA2 密钥对
- 应用公钥或支付宝公钥配置
- 服务端异步通知地址
notify_url - 支付宝 iOS SDK
支付产品、账号类型或 SDK 版本不同,配置入口也可能不同。具体应以当前支付宝开放平台控制台和相应产品文档为准。
配置密钥时,需要区分以下三项:
- 商户应用私钥:只保存在服务端,用于订单签名。
- 应用公钥:上传到支付宝开放平台。
- 支付宝公钥:保存在服务端,用于验证支付宝通知签名。
不要将商户应用私钥打包进 iOS 应用。IPA 可以被分析,私钥一旦泄露,攻击者可能伪造签名请求。
二、集成官方 iOS SDK
从支付宝开放平台下载与当前支付产品对应的 iOS SDK,再按照 SDK 包中的接入说明导入项目。常见做法是添加官方提供的 framework、xcframework、静态库和资源文件。
导入后检查以下配置:
- SDK 是否加入了正确的 Target。
Framework Search Paths或相关构建路径是否正确。- SDK 要求的系统依赖库是否已经链接。
- Objective-C 项目能否导入对应头文件。
- Swift 项目是否已通过模块导入或 Bridging Header 使用 SDK。
Objective-C 项目通常采用类似下面的导入方式:
#import <AlipaySDK/AlipaySDK.h>
如果当前 SDK 的模块名或头文件路径与示例不同,应使用下载包中实际提供的名称,不要直接套用旧版本示例。
三、配置 URL Scheme
支付宝 App 支付完成后,会通过 URL Scheme 返回商户应用。
在 Xcode 中打开:
Target
→ Info
→ URL Types
添加一个 URL Type,并设置唯一的 URL Scheme,例如:
com.example.myapp.alipay
建议使用反向域名形式,以免与其他应用冲突。代码传给支付宝 SDK 的 Scheme 必须与此处配置完全一致,大小写也要相同。
还可以直接检查 Info.plist 是否包含对应配置:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.example.myapp.alipay</string>
</array>
</dict>
</array>
如果 SDK 需要通过 canOpenURL: 检查支付宝客户端,可能还要在 LSApplicationQueriesSchemes 中添加官方文档指定的 Scheme。该列表可能随 SDK 版本变化,应以当前 SDK 接入文档为准,不要根据旧示例随意添加。
四、在服务端创建并签名订单
用户在客户端点击支付后,客户端应先向业务服务器请求支付参数:
iOS App → 业务服务器 → 支付宝
服务端负责:
- 创建业务订单。
- 生成唯一的商户订单号。
- 组装支付宝支付请求参数。
- 使用商户应用私钥执行 RSA2 签名。
- 将完整的支付订单字符串返回给 iOS 客户端。
客户端接口可以返回以下格式:
{
"orderString": "app_id=...&biz_content=...&sign=...&sign_type=RSA2..."
}
服务端还要自行校验金额、商品信息和用户身份,不能直接信任客户端提交的金额。例如,客户端只提交商品 ID 和数量,实际支付金额由服务端根据数据库计算。
以下操作不应放在客户端:
生成商户订单号
决定最终金额
保存商户私钥
执行 RSA2 签名
这些操作都应在可信的服务端环境中完成。
五、客户端发起支付
取得服务端返回的完整 orderString 后,即可调用支付宝 SDK。
Objective-C 示例
#import <AlipaySDK/AlipaySDK.h>
- (void)startAlipayWithOrderString:(NSString *)orderString {
if (orderString.length == 0) {
return;
}
NSString *scheme = @"com.example.myapp.alipay";
[[AlipaySDK defaultService] payOrder:orderString
fromScheme:scheme
callback:^(NSDictionary *resultDic) {
NSLog(@"Alipay result: %@", resultDic);
NSString *resultStatus = resultDic[@"resultStatus"];
[self handleAlipayResultStatus:resultStatus];
}];
}
- (void)handleAlipayResultStatus:(NSString *)resultStatus {
if ([resultStatus isEqualToString:@"9000"]) {
// SDK 返回支付完成,但仍需向业务服务器确认最终订单状态
[self queryOrderStatusFromServer];
} else if ([resultStatus isEqualToString:@"6001"]) {
// 用户取消支付
} else {
// 支付失败、处理中或其他状态
[self queryOrderStatusFromServer];
}
}
payOrder:fromScheme:callback: 是常见 SDK 版本中的接口。如果所用 SDK 的公开方法签名不同,应以该版本的头文件和官方示例为准。
客户端回调通常包含 resultStatus、result 和 memo 等字段,但业务代码不能仅凭某个状态码直接发货。收到回调后,应尽快调用自己的订单查询接口,确认服务端记录的订单状态。
六、处理支付宝返回应用的回调
使用 AppDelegate
通过 AppDelegate 接收 URL 的项目,可以这样处理:
#import <AlipaySDK/AlipaySDK.h>
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"Alipay callback: %@", resultDic);
// 回到业务服务器查询订单状态
[self queryOrderStatusFromServer];
}];
}
return YES;
}
部分旧项目还要实现旧版 URL 回调方法:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"Alipay callback: %@", resultDic);
}];
}
return YES;
}
是否需要实现旧方法,取决于项目支持的 iOS 版本和生命周期配置。
使用 SceneDelegate
启用 Scene 生命周期的项目,还要在 SceneDelegate 中接收 URL:
#import <AlipaySDK/AlipaySDK.h>
- (void)scene:(UIScene *)scene
openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts API_AVAILABLE(ios(13.0)) {
NSURL *url = URLContexts.allObjects.firstObject.URL;
if (url == nil) {
return;
}
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"Alipay scene callback: %@", resultDic);
// 查询服务端订单状态
}];
}
}
如果项目同时支持不同的 iOS 生命周期,需要确认实际生效的入口已经实现。支付可以正常跳转到支付宝,却无法返回应用时,通常应检查 Scheme 和回调入口配置。
七、服务端处理异步通知
支付宝会向配置的 notify_url 发送服务端异步通知。服务端收到通知后,至少要完成以下处理:
- 使用支付宝公钥验证签名。
- 检查通知中的
app_id、商户号等身份信息。 - 检查商户订单号是否存在。
- 核对订单金额、币种和商品信息。
- 判断订单是否已经处理,保证接口幂等。
- 更新订单状态。
- 按支付宝协议返回正确响应。
处理流程可以写成:
收到支付宝通知
↓
验证 RSA2 签名
↓
检查 app_id、订单号、金额
↓
检查订单是否已经支付
↓
以事务方式更新订单状态
↓
返回协议要求的成功响应
同一条通知可能被重复发送,因此不能每收到一次通知就再次发货。可以通过订单状态、唯一索引或数据库事务实现幂等处理。
八、不要只依赖客户端回调
客户端回调可能因以下情况而丢失或失真:
- 用户支付后直接结束应用。
- 网络在返回应用时中断。
- App 没有收到 URL 回调。
- 回调到达时应用发生崩溃。
- 客户端回调内容被篡改。
- 支付结果仍处于处理中。
最终判断流程应为:
客户端回调
↓
提示用户支付结果正在确认
↓
向业务服务器查询订单
↓
业务服务器根据异步通知或支付宝查询接口确认状态
↓
展示最终结果
只有业务服务器确认订单支付成功后,才能发货、开通会员或增加余额。
九、常见排查点
如果仍然无法唤起支付宝,可以依次检查:
- SDK 是否正确加入当前 Target。
- 服务端返回的
orderString是否完整。 orderString是否被重复 URL 编码或错误转义。app_id是否与开放平台中的应用一致。- RSA2 签名使用的私钥是否与上传的应用公钥匹配。
- 应用是否已经签约并开通对应支付产品。
- URL Scheme 是否已经写入
Info.plist。 fromScheme是否与项目配置完全一致。- 真机是否安装了支付宝客户端。
- 当前 SDK 是否支持项目使用的 Xcode 和 iOS 版本。
- 使用 Scene 生命周期时,是否遗漏了
SceneDelegate回调。 - 服务端和客户端是否混用了测试环境、沙箱环境与正式环境。
如果能够跳转到支付宝,但支付完成后无法返回应用,应重点检查 URL Scheme 和 URL 回调方法。如果支付宝立即提示订单错误,则要检查服务端订单参数、签名、公钥配置和支付产品权限。
注意事项
- 必须使用 HTTPS 与业务服务器通信。
- 商户私钥只能保存在服务端。
- 日志中不要输出私钥、完整签名或用户敏感信息。
- 商户订单号必须唯一。
- 金额应由服务端计算并再次校验。
- 客户端状态码只能用于提示用户,不能作为最终账务依据。
- 服务端收到异步通知后必须验签,并进行幂等处理。
- SDK 方法、系统依赖和回调配置可能随版本变化,应以所下载 SDK 的头文件和当期官方接入文档为准。
备注:内容仅供参考。
