iOS - 三大支付系之核心流程

本文附带我在公司遇到的一些特别注意的小问题 请认真阅读

马云的支付宝SDK_iOS 移动支付集成开发包--《支付宝钱包支付接口开发包2.0标准版.pdf》

发哥的支付宝流程图绘制.png
  • 我们还需要先生成一个订单,文档中描述时,是将这步也放在客户端来做了,但也可以在服务器端生成这个订单(图中支付宝会在支付成功后通知服务器端,所以在服务器端生成订单的话,你可以掌握所有订单,而且也会更安全):
1.生成订单(可以在iOS客户端内生成,也可以在服务器端生成)
2.调用支付宝支付接口,发送订单
3.处理支付宝返回的支付结果
  • 其实对于业务来说,这些步骤已经够了,但是有一个安全性问题,你肯定不希望你接收到的支付结果被截获修改,所以,这就需要在生成订单和处理支付结果的时候做一个安全性校验:
    生成订单时对数据签名,收到支付结果时对数据进行签名验证,以检验数据是否被篡改过。
    支付宝目前只支持采用RSA加密方式做签名验证。

  • [RSA加密算法] 除了可加解密外,还可用来作签名校验。简单的说,RSA会生成一个私钥和一个公钥,私钥你应该独自保管,公钥你可以分发出去。做签名验证时,你可以用私钥对需要传输的数据做签名加密,生成一个签名值,之后分发数据,接收方通过公钥对签名值做校验,如果一致则认为数据无篡改。

具体到支付宝使用RSA做签名验证,就是在生产订单时,需要使用私钥生成签名值;在处理返回的支付结果时,需要使用公钥验证返回结果是否被篡改了。

具体需要对哪些值,怎样生成签名,对哪些值最签名验证,在第一个文档中有

resultStatus,状态码,SDK里没对应信息,第一个文档里有提到:9000 订单支付成功
8000 正在处理中
4000 订单支付失败
6001 用户中途取消
6002 网络连接出错

  • memo, 提示信息,比如状态码为6001时,memo就是“用户中途取消”。但千万别完全依赖这个信息,如果未安装支付宝app,采用网页支付时,取消时状态码是6001,但这个memo是空的。。(当我发现这个问题的时候,我就决定,对于这么不靠谱的SDK,还是尽量靠自己吧。。)
    result,订单信息,以及签名验证信息。如果你不想做签名验证,那这个字段可以忽略了。
以上就是马云宝的一些通用流程和注意事项,如果有什么不对后期再更新...谢谢!
  • 微信支付

他跟马云宝最大的区别在于 你的设备上没有安装支付宝的话会自动掉用网页版支付,然而马化腾的微信不会
发哥的微信支付集成流程图.png
//需要的依赖库,环境搭建可以参见文档,或者直接用cocoapods倒入
//取的订单金额是0,真实的是0.01元,微信接口需要单位是分的 那应该是1才对啊。
{"errcode":1001,"errmsg":""}
这个错误的原因是 package里必填的参数缺少。
我之前遇到的教训就是一个文档的细节--->  total_fee这个参数只支持整数 单位是分!
/**

*  微信开放平台申请得到的 appid, 需要同时添加在 URL schema

*/

NSString * const WXAppId = @"**************";

/**

* 微信开放平台和商户约定的支付密钥

*

* 注意:不能hardcode在客户端,建议genSign这个过程由服务器端完成

*/

NSString * const WXAppKey = @"*********************************";

/**

* 微信开放平台和商户约定的密钥

*

* 注意:不能hardcode在客户端,建议genSign这个过程由服务器端完成

*/

NSString * const WXAppSecret = @"********************";

/**

* 微信开放平台和商户约定的支付密钥

*

* 注意:不能hardcode在客户端,建议genSign这个过程由服务器端完成

*/

NSString * const WXPartnerKey = @"*******************";

/**

*  微信公众平台商户模块生成的ID

*/

NSString * const WXPartnerId = @"****************";

调用支付的代码就比较简单了,如下所示

#pragma mark - 主体流程

- (void)getAccessToken

{

NSString *getAccessTokenUrl = [NSString stringWithFormat:@"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%@&secret=%@", WXAppId, WXAppSecret];

NSLog(@"--- GetAccessTokenUrl: %@", getAccessTokenUrl);

self.request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:getAccessTokenUrl]];

__weak WXPayClient *weakSelf = self;

__weak ASIHTTPRequest *weakRequest = self.request;

[self.request setCompletionBlock:^{

NSError *error = nil;

NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[weakRequest responseData]

options:kNilOptions

error:&error];


if (error) {

[weakSelf showAlertWithTitle:@"错误" msg:@"获取 AccessToken 失败"];

return;

} else {

NSLog(@"--- %@", [weakRequest responseString]);

}

NSString *accessToken = dict[AccessTokenKey];

if (accessToken) {

NSLog(@"--- AccessToken: %@", accessToken);

__strong WXPayClient *strongSelf = weakSelf;

[strongSelf getPrepayId:accessToken];

} else {

NSString *strMsg = [NSString stringWithFormat:@"errcode: %@, errmsg:%@", dict[errcodeKey], dict[errmsgKey]];

[weakSelf showAlertWithTitle:@"错误" msg:strMsg];

}

}];

[self.request setFailedBlock:^{

[weakSelf showAlertWithTitle:@"错误" msg:@"获取 AccessToken 失败"];

}];

[self.request startAsynchronous];

}

- (void)getPrepayId:(NSString *)accessToken

{

//token传入到此链接

NSString *getPrepayIdUrl = [NSString stringWithFormat:@"https://api.weixin.qq.com/pay/genprepay?access_token=%@", accessToken];

NSLog(@"--- GetPrepayIdUrl: %@", getPrepayIdUrl);

NSMutableData *postData = [self getProductArgs];

// 文档: 详细的订单数据放在 PostData 中,格式为 json

self.request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:getPrepayIdUrl]];

[self.request addRequestHeader:@"Content-Type" value:@"application/json"];

[self.request addRequestHeader:@"Accept" value:@"application/json"];

[self.request setRequestMethod:@"POST"];

[self.request setPostBody:postData];

__weak WXPayClient *weakSelf = self;

__weak ASIHTTPRequest *weakRequest = self.request;

[self.request setCompletionBlock:^{

NSError *error = nil;

NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[weakRequest responseData]

options:kNilOptions

error:&error];

//获取到了支付参数

if (error) {

[weakSelf showAlertWithTitle:@"错误" msg:@"获取 PrePayId 失败"];

return;

} else {

NSLog(@"--- %@", [weakRequest responseString]);

}

NSString *prePayId = dict[PrePayIdKey];

if (prePayId) {

NSLog(@"--- PrePayId: %@", prePayId);

// 调起微信支付

//将支付参数传入到sdk,唤起微信客户端

PayReq *request  = [[PayReq alloc] init];

request.partnerId = WXPartnerId;

request.prepayId  = prePayId;

request.package  = @"Sign=WXPay";      // 文档为 `Request.package = _package;` , 但如果填写上面生成的 `package` 将不能支付成功

request.nonceStr  = weakSelf.nonceStr;

request.timeStamp = [weakSelf.timeStamp longLongValue];

// 构造参数列表

NSMutableDictionary *params = [NSMutableDictionary dictionary];

[params setObject:WXAppId forKey:@"appid"];

[params setObject:WXAppKey forKey:@"appkey"];

[params setObject:request.nonceStr forKey:@"noncestr"];

[params setObject:request.package forKey:@"package"];

[params setObject:request.partnerId forKey:@"partnerid"];

[params setObject:request.prepayId forKey:@"prepayid"];

[params setObject:weakSelf.timeStamp forKey:@"timestamp"];

request.sign = [weakSelf genSign:params];

// 在支付之前,如果应用没有注册到微信,应该先调用 [WXApi registerApp:appId] 将应用注册到微信

[WXApi safeSendReq:request];

} else {

NSString *strMsg = [NSString stringWithFormat:@"errcode: %@, errmsg:%@", dict[errcodeKey], dict[errmsgKey]];

[weakSelf showAlertWithTitle:@"错误" msg:strMsg];

}

}];

[self.request setFailedBlock:^{

[weakSelf showAlertWithTitle:@"错误" msg:@"获取 PrePayId 失败"];

}];

[self.request startAsynchronous];

}

//这是微信官方给的demo,直接调用getAccessToken方法即可完成支付
  • 银联支付 【最简单了】

发哥的银联支付集成流程图.png
- (void)viewDidLoad {
//如果您发现 【银联报文错误8100008】一般直接去找你的后台就好  
//一定是你们服务端改东西
//流程图说明:
(1)用户在客户端中点击购买商品,客户端发起订单生成请求到商户后台;
(2)商户后台收到订单生成请求后,按照《手机控件支付产品接口规范》组织并推送订单信息至银联后台;
(3)银联后台接收订单信息并检查通过后,生成对应交易流水号(即TN),并回复交易流水号至商户后台(应答要素:交易流水号等);
(4)商户后台接收到交易流水号,将交易流水号返回给客户端;
(5)客户端通过交易流水号(TN)调用支付控件;
//可能服务端在第二步出了问题,导致第三步银联返回给服务端一个错误信息,第四部服务端又接着把错误信息给了你.跟服务端沟通下试试就好
[super viewDidLoad];

//开始支付

//第一个参数是流水号,一般是后台返回给我们的
//第二个参数传00,01,00标示正式环境,01标示测试环境
//第三个参数是支付完成回到的控制器
//第四个参数是设置代理

[UPPayPlugin startPay:@"******" mode:@"01" viewController:self.navigationController delegate:self];

// Do any additional setup after loading the view, typically from a nib.

}

//监听支付结果

- (void)UPPayPluginResult:(NSString *)result

{

}
  • 还有一个是我在外包公司经常使用的---这家伙集成所有支付功能于一身:---->Ping++

支付流程:

ping++ //------>支持支付宝支付,微信支付,银联支付,百度钱包支付,applepay
(1)根据呢需要介入的支付方式去对应的支付平台申请账号和参数
(2)传说中的7行代码搞定所有的支付

发哥的ping++继承流程图

直接上代码:

NSDictionary* dict = @{    @"channel" : channel, // 渠道 alipay, wx, upacp, bfb
@"amount"  : amount  // 金额};
NSData* data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
NSString *bodyData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[postRequest setHTTPBody:[NSData dataWithBytes:[bodyData UTF8String] length:strlen([bodyData UTF8String])]];
[postRequest setHTTPMethod:@"POST"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:postRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSString* charge = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    // ...
[Pingpp createPayment:charge viewController:viewController appURLScheme:kUrlScheme withCompletion:^(NSString *result, PingppError *error) {   
if ([result isEqualToString:@"success"]) {        // ...
} else {      
NSLog(@"PingppError: code=%lu msg=%@", error.code, [error getMsg]);
}
}];
}];
//AppDelegate添加这行代码适用于监听支付结果的
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[Pingpp handleOpenURL:url withCompletion:^(NSString *result, PingppError *error) {        if ([result isEqualToString:@"success"]) {            // ...
} else {            NSLog(@"PingppError: code=%lu msg=%@", error.code, [error getMsg]);
}
}];    return  YES;
}

个人感觉第三方支付终究还是第三方,只是站着公司和开发者的角度上考虑问题,减少开发难度和成本,我还是推荐大家自己去亲自集成一次支付的功能,你才会有成长

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

推荐阅读更多精彩内容