iOS 接入支付宝SDK封装的工具类HHAliPaySDK(服务
开篇吐槽,文档看不懂,文档看不懂,文档看不懂.重要的事说三遍.
对于没集成过支付宝SDK的人来说,官方文档看完也是一脸懵逼,在网上搜完别人写的什么文档流程,看完也是一脸懵逼.
官方demo运行不了,还有各种什么order类生成签名,返回结果验签巴拉巴拉,反正说的我心累.
看一下支付宝给的流程图
图中的“商户客户端”其实就是我们的iOS客户端需要做的事情:
1.用后台给的签名后订单信息调用支付宝支付接口
2.处理支付宝返回的支付结果
完了,这就完事了,很简单的事情,只需要这两步,官方文档给的那么乱七八糟的复杂东西,什么order类啥的,都是把后台做的事情,放在了客户端来做,所以才那么复杂.
{7D03662F-CC3F-AE65-ECAB-8DA5F7FF92F9}.png
那么为什么官方demo中能放在客户端做的事情(签名,验签)要给后台来做.
看下面官方给的提示:
第一条:私钥要保存在服务端,那么决定了调用支付宝接口需要的签名后的订单信息(也就是参数payOrder)需要后台来生成,传给我们.
第二条:到底付款成没成功,需要依赖服务端收到的异步通知结果来进行判断,我们客户端这边收到成功的提示也没用,必须后台告诉我们,他们也成功了,才算成功
第三条:说到了难住大多数人的验签,官方建议验签规则参考异步通知验签,而异步通知验签,是在服务端来完成的,官方在服务端的SDK中提供了一个工具类用来验签,所以我们也是在服务端完成的.
了解以上就可以集成支付宝SDK了
这里是官方支付宝SDK集成流程
这里是创建应用获取APPID和配置秘钥的方法
设置URL Scheme(要记住这个标识,调用支付宝方法时会用到这个参数)
修改 info.plist 文件 URL types 项中后面的URL Schemes内容,官方建议跟商户的app有一定的标示度,要做到和其他的商户app不重复,否则可能会导致支付宝返回的结果无法正确跳回商户app。(比如你们项目名称+AliPay,我这里设置了支付宝,qq,微信,微博,只用到支付宝的话,就添加支付宝的就ok)
2EA80578-7343-433E-8803-73E4E826BE11.png
添加依赖库
把下载的SDK中AlipaySDK.bundle和AlipaySDK.framework拖入工程,然后按官方给的图添加依赖库.官方文档看到这张图这里就够了,其余以下的不用管,都是教你demo怎么运行,签名和验签也没我们客户端的事,看他demo干嘛.
{6F9683DA-F3FC-EB1C-9F34-C2D3EA014542}.png
设置头文件路径
点击“Build Settings”选项卡,在搜索框中,以关键字“search”搜索,对“Header Search Paths”增加头文件路径:$(SRCROOT)/项目名称。如果头文件信息已增加,可不必再增加.
添加URL Schemes白名单
在“Info.plist”中增加一个LSApplicationQueriesSchemes值,设置为array, 添加支付宝需要的item:
alipay
也就是图中的最后一项,其他的都是微信微博和QQ的.
设置https访问
在“Info.plist”中增加一个App Transport Security Settings值, 其中有一个Allow Arbitrary Loads对应的值要设置为YES
39DD2532-0055-4AC0-AC11-920D6A18E179.png
然后就就可以愉快的写代码了
创建了一个继承于NSObject的 HHAliPaySDK工具类,我在其中封装了一下向后台请求签名后的订单信息(也就是payOrder)的方法和处理回调结果用到的方法.
ps:这个类中有网络请求,HHttpManager是我自己对AFNetworking3.0的二次封装,感兴趣的可以看一看,不感兴趣的同学可以用AFN或者自己的网络框架.
.h文件
#import <Foundation/Foundation.h>
@interface HHAliPaySDK : NSObject
/**
根据订单信息向后台申请prepayId以调用支付宝支付接口
@param amount 金额
@param orderId 订单信息(支付前请求后台给的没有签名过的订单号)
*/
+ (void)sendAliPayRequestWithAmount:(NSString *)amount
orderId:(NSString *)orderId;
/**
openURL
*/
+(BOOL)handleOpenURL:(NSURL *)url;
@end
.m文件
#import "HHAliPaySDK.h"
#import <AlipaySDK/AlipaySDK.h>
@implementation HHAliPaySDK
//post请求后台,获取签名后的订单信息(也就是payOrder)
+ (void)sendAliPayRequestWithAmount:(NSString *)amount
orderId:(NSString *)orderId{
NSDictionary *paramDict = @{
@"totalAmount":amount,
@"orderId":orderId,
};
//getAliPayOrder:后台提供的接口,用来请求签名后的订单信息(也就是payOrder)
[HHttpManager POST:getAliPayOrder parameters:paramDict success:^(id responseObject) {
NSNumber *state = responseObject[@"state"];
if (state.integerValue == 0) {
NSString *orderStr = responseObject[@"data"][0][@"AlipaySign"];
[self sendPayRequstWithPayOrder:orderStr];
}else{
NSLog(@"请求失败--%@",responseObject[@"msg"]);
}
} failure:^(NSError * error) {
NSLog(@"请求失败--%@",error);
}];
}
//调用支付宝支付(无支付宝客户端时的结果回调也在此方法中)
+ (void)sendPayRequstWithPayOrder:(NSString *)payOrder{
NSLog(@"%@", payOrder);
//AliPay_Scheme:设置URL Scheme时让你记住的参数.
[[AlipaySDK defaultService] payOrder:payOrder fromScheme:AliPay_Scheme callback:^(NSDictionary *resultDic) {
NSLog(@"%@", resultDic);
[self handleAliPayCallBackResultWithDictionary:resultDic];
}];
}
/**
openURL(有支付宝客户端时返回的结果)
*/
+(BOOL)handleOpenURL:(NSURL *)url{
if ([url.host isEqualToString:@"safepay"] ) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
[self handleAliPayCallBackResultWithDictionary:resultDic];
}];
}
if ([url.host isEqualToString:@"platformapi"]){
[[AlipaySDK defaultService] processAuthResult:url standbyCallback:^(NSDictionary *resultDic) {
[self handleAliPayCallBackResultWithDictionary:resultDic];
}];
}
return YES;
}
+ (void)handleAliPayCallBackResultWithDictionary:(NSDictionary *)resultDic{
if ([resultDic[@"resultStatus"] isEqual:@"9000"])
{
//客户端支付成功,然后向后台请求,看他是否验签成功,他也成功了,才证明支付成功,
}
if ([resultDic[@"resultStatus"] isEqual:@"4000"])
{
//支付失败
}
if ([resultDic[@"resultStatus"] isEqual:@"6001"])
{
//取消支付
}
if ([resultDic[@"resultStatus"] isEqual:@"6002"])
{
//网络连接失败
}
}
@end
设置(个人喜欢类方法,调用方便,可自己修改)
在AppDelegate.m文件中
openURL方法中设置回调,支付宝不用初始化,是不是很开森0.0(这里一个是iOS9以下的系统调用的系统方法和一个是iOS9以上的系统调用的系统方法,都要设置)
#pragma mark - OpenURL回调结果
//iOS9-
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
[self handleOpenURLWithURLHost:url];
return YES;
}
//iOS9和9+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options{
[self handleOpenURLWithURLHost:url];
return YES;
}
- (void)handleOpenURLWithURLHost:(NSURL *)url{
NSLog(@"url.host:%@", url.host);
//支付宝
if ([url.host isEqualToString:@"safepay"]||[url.host isEqualToString:@"platformapi"]) {
[HHAliPaySDK handleOpenURL:url];
}
}
调用
类方法直接调用封装好的接口就行,orderId是需要向后台请求的订单号(比如:D123457890)
/**
根据订单信息向后台申请prepayId以调用支付宝支付接口
@param amount 金额
@param orderId 订单信息(支付前请求后台给的没有签名过的订单号)
*/
+ (void)sendAliPayRequestWithAmount:(NSString *)amount
orderId:(NSString *)orderId;
客户端题外话,关于RSA
(上面引入的是阮一峰写的一篇RSA算法原理,都是专业数学知识,看的我一愣一愣的,感谢大大的讲解)
签名和验签都交给后台的来做,对我们客户端来说,既安全有方便(真不是我们客户端不做,是官方建议的,上面我有说明过,涉及钱的问题,一切为了安全),客户端这边是不需要了解RSA加密解密的(没错,反正我是不会),但是需要后台了解,最简单来说,在生产订单时,需要使用私钥生成签名,在处理返回的支付结果时,需要使用公钥验证返回结果是否被篡改.验证通过才算支付成功.具体后台怎么验签,支付宝提供了方法,他给服务端的SDK提供了一个工具类用来验签,官方举的例子是Java
Map<String, String> paramsMap = ... //将异步通知中收到的待验证所有参数都存放到map中
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, ALIPAY_PUBLIC_KEY, CHARSET) //调用SDK验证签名
if(signVerfied){
// TODO 验签成功后
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
}else{
// TODO 验签失败则记录异常日志,并在response中返回failure.
}