iOS 接入支付宝SDK封装的工具类HHAliPaySDK(服务端签名和验签)

开篇吐槽,文档看不懂,文档看不懂,文档看不懂.重要的事说三遍.
对于没集成过支付宝SDK的人来说,官方文档看完也是一脸懵逼,在网上搜完别人写的什么文档流程,看完也是一脸懵逼.
官方demo运行不了,还有各种什么order类生成签名,返回结果验签巴拉巴拉,反正说的我心累.


看一下支付宝给的流程图

图中的“商户客户端”其实就是我们的iOS客户端需要做的事情:
1.用后台给的签名后订单信息调用支付宝支付接口
2.处理支付宝返回的支付结果
完了,这就完事了,很简单的事情,只需要这两步,官方文档给的那么乱七八糟的复杂东西,什么order类啥的,都是把后台做的事情,放在了客户端来做,所以才那么复杂.


{7D03662F-CC3F-AE65-ECAB-8DA5F7FF92F9}.png

那么为什么官方demo中能放在客户端做的事情(签名,验签)要给后台来做.

看下面官方给的提示:

{ABDC1148-DE4D-8D69-61BD-E3B6741D5818}.png

第一条:私钥要保存在服务端,那么决定了调用支付宝接口需要的签名后的订单信息(也就是参数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的.

4ECDABF8-640E-4CF9-BBB8-C93037F155D9.png

设置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.
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容