关于iOS支付的小结
项目中碰到过的支付方式一直以来没有系统的梳理过,今天就开发过程中的问题来做一次详细的总结,为今后的使用提供参考。
目前为止较为广泛使用的支付方式:
三方支付:
- 微信支付
- 支付宝支付
- 银行支付
- 内购(简称 IAP)
- 感想: 所有三方支付最好写一个单例来封装,便于维护
微信支付
关于微信支付后的回调处理
- 问题触发场景:当我们使用微信支付成功后,
没有
在微信App内,点击完成
按钮,跳转
到我们自己的App,而是
点击了系统提供的左上角快捷键
返回到了我们自己的App。 - 场景导致的bug:由于没有点击
完成
按钮,微信SDK无法调用其回调接口:
-(void)onResp:(BaseResp*)resp{
if ([resp isKindOfClass:[PayResp class]]){
PayResp*response=(PayResp*)resp;
switch(response.errCode){
case WXSuccess:
//服务器端查询支付通知或查询API返回的结果再提示成功
NSlog(@"支付成功");
break;
default:
NSlog(@"支付失败,retcode=%d",resp.errCode);
break;
}
}
}
如此一来,我们就无法完整的处理微信支付流程了。
- 解决方案:app每次从后台进入时会调用
applicationWillEnterForeground(注:应用将要进入前台)
方法,我们可以在这里发送一个微信支付的通知,并在相应的界面获取该通知,在通知方法里,向服务端发送微信支付结果请求,获取支付结果。
#pragma mark - AppDelegate中发送通知
- (void)applicationWillEnterForeground:(UIApplication *)application
{
DTLog(@"applicationWillEnterForeground");
[[NSNotificationCenter defaultCenter] postNotificationName:@"wxPayBack" object:nil];
}
#pragma mark - 相关接收通知的controller
// 考虑到添加时机的问题,最好是在获取订单信息的回调中添加该通知,确保该通知回调一定会被执行
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wxPayBack) name:@"wxPayBack" object:nil];
- (void)wxPayBack
{
//判断订单号是否存在,以及当前回调是否为微信支付(若有其它支付,也会调起该通知),然后通过订单号向服务端发起支付结果请求
if (订单号.length > 0 && _isWxPay)
{
[self judgeWxPayStationWithWxPayOrder:订单号];
}
}
#pragma mark - 对请求结果的处理
- (void)judgeWxPayStationWithWxPayOrder:(NSString *)wxOrder
{
NSDictionary *dic = 请求体参数;
[[NetWorkManager shareManager] postDataSourceFromURL:请求订单信息的接口 Dic:dic Success:^(NSDictionary *success) {
// 参考微信SDK自定义PayResp对象
PayResp *resp = [[PayResp alloc] init];
resp.errCode = 0;
resp.errStr = nil;
resp.type = 0;
NSString *state = success[支付结果对应的key];
if (![state isEqualToString:支付成功对应的值])
{
resp.errCode = 0;
}
[self wxPaySResp:resp];
} AndFail:^(NSError *fail) {
}];
}
- (void)wxPaySResp:(PayResp *)resp
{
NSString *strTitle = nil;
if ([resp isKindOfClass:[PayResp class]]) {
switch (resp.errCode) {
case WXSuccess:
{
strTitle = @"支付成功!";
break;
}
case WXErrCodeUserCancel: //用户点击取消并返回
strTitle = @"您未成功支付";
break;
default:
break;
}
}
if (strTitle != nil)
{
弹出相应的提示
}
}
- 微信支付流程(客户端具体实现)
- 下载对应iOS平台的Demo,添加依赖库,修改plist
- 真机测试时,需要在build setting里修改bitcode为NO
- 设置project——>info——>URL types——>新增微信URLSchemes
- 商户APP工程中引入微信lib库和头文件,调用API前,需要先向微信注册APPID(在APPDelete中注册即可)
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[WXApi registerApp: @"自己的appid替换" withDescription: @"自己定义一个描述"];
}
- 向服务端发起请求,获取订单号——>通过商品价格等参数拼接请求体发起订单号请求
- 在订单号返回成功的回调中添加微信客户端回调通知
- 在点击微信支付后,拼接请求体(请求体中包含订单号)发起支付请求
- 支付回调成功处,设置PayReq对象,调起微信客户端(最好增加客户端是否安装的判断)
- 微信客户端调起自己的app,此时获取到微信支付处理的通知,接下来处理该通知(处理细节请在上面查看)
名称 | 描述 | 解决方案 |
---|---|---|
0 | 成功 | 展示成功页面 |
-1 | 错误 | 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。 |
-2 | 用户取消 | 无需处理。发生场景:用户不支付了,点击取消,返回APP。 |
支付宝支付
- 支付宝支付,分为网页快捷支付、支付宝客户端支付两种情况
- 支付宝支付流程(客户端具体实现)
-
下载对应iOS平台的Demo,添加依赖库
-
设置project——>info——>URL types——>新增支付宝URLSchemes
-
向服务端发起请求,获取订单号——>通过商品价格等参数拼接请求体发起订单号请求
- 在点击支付宝支付后,拼接请求体order(请求体中包含订单号、服务端提供的notifyURL)发起支付宝签名请求
- 获取签名请求成功回调中,处理订单字符串、回调URLScheme(保证支付处理完成回调到自己的app),使用[AlipaySDK defaultService]调起支付宝处理支付
- 支付处理完成,APPDelegate中,处理一次回调,然后在调起支付宝的controller中进行第二次回调处理
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary*)options
{
if ([[url absoluteString] hasPrefix:URLScheme])
{
// 支付宝客户端二次回调条件
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
}];
[[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"result = %@",resultDic);
// 解析 auth code
NSString *result = resultDic[@"result"];
NSString *authCode = nil;
if (result.length>0) {
NSArray *resultArr = [result componentsSeparatedByString:@"&"];
for (NSString *subResult in resultArr) {
if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
authCode = [subResult substringFromIndex:10];
break;
}
}
}
NSLog(@"授权结果 authCode = %@", authCode?:@"");
}];
}
}
银联支付
银联移动支付平台地址:https://open.unionpay.com/ajweb/product/detail?id=3 下载SDK
-
导入SDK
SDK包括两个文件:UPPaymentControl.h文件和libPaymentControl.a,将SDK目录添加到工程. -
添加依赖
使用UPPaymentControl需要添加CFNetwork.framework、SystemConfiguration.framework 、libz、libPaymentControl.a到工程中, -
修改配置
3.1 在工程info.plist设置中添加一个URL Types回调协议(在UPPayDemo工程中使用“UPPayDemo”作为协议),用于在支付完成后返回商户客户端。
3.2 在Xcode7.0之后的版本中进行http请求时,需要在工程对应的plist文件中添加
NSAppTransportSecurity Dictionary
并同时设置里面
NSAllowsArbitraryLoads 属性值为 YES.
3.3 添加协议白名单
在Xcode7.0之后的版本中进行开发,需要在工程对应的plist文件中,添加LSApplicationQueriesSchemes Array并加入uppaysdk、uppaywallet、uppayx1、uppayx2、uppayx3五个item
或者直接添加如下代码到plist文件中:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>uppaysdk</string>
<string>uppaywallet</string>
<string>uppayx1</string>
<string>uppayx2</string>
<string>uppayx3</string>
</array>
3.4 修改编译选项
选择工程targets——>build settings ->Linking->other linker flags
如果该设置和其他第三方库冲突,或银联客户端莫名失败,则可尝试去掉-ObjC,改为-force_load+空格+控件路径,如:-force_load $(PROJECT_DIR)/xxxx/ libPaymentControl.a
3.5 凡是引入银联头文件UPPaymentControl.h,需要将源文件后缀.m改为.mm
-
接口说明
4.1 支付接口
支付接口.png
4.2 回调接口
返回接口.png
内购
- 内购需要在开发者账号发布app界面,添加内购物品
- 完善银行卡信息
- 添加测试人员账号
- 在app中添加内购代码
内购实现流程
- 客户端向自己公司服务端请求productIds(可能是多个商品)
- 使用这些produceId创建SKProductsRequest对象,调用start方法,设置该对象代理,在代理方法中获取可以销售的product(代理方法中获取创建UI所需要的数据)
- 当用户点击某个商品后,我们客户端获取被点击商品的SKProduct对象,创建SKPayment票据对象(使用SKProduct对象创建),添加SKPayment对象的监听者,将票据添加到购买队列,在监听商品更新信息回调方法中获取商品购买状态
- 如果商品返回状态为:成功 | 失败 | 恢复,那么将该票据从队列移除,成功购买,应返回给本公司服务器商品购买成功信息,用于下次更新商品状态