支付宝、微信、银联移动支付集成

一、支付宝移动支付

SDK开放平台:https://b.alipay.com/order/productDetail.htm?productId=2013080604609654&tabId=4#ps-tabinfo-hash

业务流程:

支付宝、微信、银联移动支付集成_第1张图片

SDK集成:

解压接口压缩文件(文件名是 WS_MOBILE_PAY_SDK_BASE.zip),找到 IOS 的压缩文件(文件名是支付宝移动支付 SDK 标准版(iOS).zip)。

  1. 导入代码

步骤1: 启动IOS开发软件(如Xcode),把IOS的压缩文件中以下文件拷贝到项目文件夹下,并导入到开发环境中。

AlipaySDK.bundle
AlipaySDK.framework

步骤2: 在需要使用SDK的文件中,增加头文件引用。

#import 

步骤3: 配置请求信息。

Order *order = [[Order alloc] init];
order.partner = partner;
order.seller = seller;
order.tradeNO = [self generateTradeNO]; //订单ID(由商家自行制定) 
order.productName = product.subject; //商品标题 order.productDescription = product.body; //商品描述
order.amount = [NSString stringWithFormat:@"%.2f",product.price]; //商 品价格
order.notifyURL = @"http://www.xxx.com"; //回调URL
order.service = @"mobile.securitypay.pay"; order.paymentType = @"1";
order.inputCharset = @"utf-8";
order.itBPay = @"30m";

//应用注册scheme,在AlixPayDemo-Info.plist定义URL types 
NSString *appScheme = @"alisdkdemo";

//将商品信息拼接成字符串
NSString *orderSpec = [order description]; NSLog(@"orderSpec = %@",orderSpec);
//获取私钥并将商户信息签名,外部商户可以根据情况存放私钥和签名,只需要遵循 RSA 签名规范, 并将签名字符串 base64 编码和 UrlEncode
id signer = CreateRSADataSigner(privateKey);
NSString *signedString = [signer signString:orderSpec];

//将签名成功字符串格式化为订单字符串,请严格按照该格式 
NSString *orderString = nil;
if (signedString != nil) {
orderString = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
                   orderSpec, signedString, @"RSA"];
[[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
          NSLog(@"reslut = %@",resultDic);
}];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; }

详细可参见示例文件

  • AliSDKDemo\APViewController.h
  • AliSDKDemo\APViewController.m
  • AliSDKDemo\Order.h
  • A liSDKDemo\Order.m

步骤4: 配置返回处理代码
在 AliSDKDemo\APAppDelegate.m 文件中,增加引用代码:

#import 

在@implementation AppDelegate 中增加如下代码:

 1. (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
//如果极简 SDK 不可用,会跳转支付宝钱包进行支付,需要将支付宝钱包的支付结果回传给 SDK 
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
          NSLog(@"result = %@",resultDic);
       }];
}
if ([url.host isEqualToString:@"platformapi"]){//支付宝钱包快登授权返回 authCode
[[AlipaySDK defaultService] processAuthResult:url standbyCallback:^(NSDictionary *resultDic) {
          NSLog(@"result = %@",resultDic);
       }];
}
return YES; 
}
  1. 针对Demo的运行注意
    (1) 关于签名代码问题
    AliSDKDemo\Util 及下面所有文件
    AliSDKDemo\openssl 及下面所有文件
    libcrypto.a
    libssl.a
    这些文件是为客户端签名功能服务的,仅作为示例使用。商户在接入支付宝产品时, 请使用商户项目自己的服务端的签名验签代码。
    (2) 点击项目名称,点击“Build Settings”选项卡,在搜索框中,以关键字“search” 搜索,对“Framework Search Paths”、“Header Search Paths”、“Libraray Search Paths”增加头文件路径:$(SRCROOT)/项目名称。如果头文件信息已增加,可不必再增加。
    (3) 点击项目名称,点击“Build Phases”选项卡,在“Link Binary with Librarles” 选项中,新增“AlipaySDK.framework”和“SystemConfiguration.framework” 两个系统库文件。如果商户项目中已有这两个库文件,可不必再增加。
    (4) 点击项目名称,点击“Info”选项卡,在“URL Types”选项中,点击“+”, 在“URL Schemes”中输入“alisdkdemo”,Schemes要于发起支付请求时设置的一致。
  2. 配置基本信息
NSString *partner = @"";
NSString *seller = @"";
NSString *privateKey = @"";//商户制作生成的PKCS8 编码的私钥

PS:
回调后台需要设置指定后台的URL,支付完成后,支付宝服务器会异步调用该地址
order.notifyURL = @"http://www.xxx.com"; //回调URL

关于privateKey私钥,是需要商户去生成密钥(生成的pem公钥最后需要上传到支付宝,请查看https://b.alipay.com/order/pidAndKey.htm )。
制作私钥步骤:
1.打开下载的SDK包,打开 openssl 文件夹下的 bin 文件夹,执行 openssl.exe 文件
2.生成RSA私钥
输入genrsa -out rsa_private_key.pem 1024命令,回车后,在当前 bin 文件目 录中会新增一个 rsa_private_key.pem 文件,其文件为原始的商户私钥(请妥善保 存该文件,PHP 开发语言中需要使用该文件)
3.生成RSA公钥,通过私钥生成公钥,上传到支付宝商户中心。
输入rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem命令回车 后,在当前 bin 文件目录中会新增一个 rsa_public_key.pem 文件,其文件为原始 的商户公钥(请妥善保存该文件,PHP 开发语言中需要使用该文件)

4.生成PKCS8 编码的私钥
输入命令pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt并回车,当前界面中会直接显示出生成结果(很长的一串字符串),
此时继续右键点击 openssl 窗口上边边缘,选择“编辑→复制”,把复制的内容粘 贴进一个新的记事本中,可随意命名,只要知道这个是 PKCS8 格式的私钥即可(请妥善保存该文件),改私钥便是移动支付签名需要用到的私钥。

2分钟快速集成支付宝支付

对支付宝SDK Demo做了调整封装,方便快速集成,集成方法和SDK查看博客:http://blog.csdn.net/bluefish89/article/details/50512080

二、微信移动支付

SDK开放平台:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=11_1

业务流程:

这里写图片描述

商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】

SDK集成:

1.解压SDK包,把里面的libWeChatSDK.a,WXApi.h,WXApiObject.h导入工程。
2.配置项目
(1)“Library Search Paths”增加库文件路径:$(PROJECT_DIR)/SDKDir
(2)用XCode打开项目,【项目属性】-【Info】-【URL Schemes】设置微信开放平台申请的应用APPID(如wxb4ba3c02aa476ea1)。如果这的APPID设置不正确将无法调起微信支付
参考查看https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_5
(3)引人系统库“libc++.tbd”“libz.tbd”“libsqlite3.0.tbd”“CoreTelephony.framework”“Security.framework”“SystemConfiguration.framework”(xcode7的静态库后缀名为tbd,旧版本为a)

3.发起支付

PayReq *request = [[[PayReq alloc] init] autorelease];
request.partnerId = @"10000100";//商家向财付通申请的商家id
request.prepayId= @"1101000000140415649af9fc314aa427";//预支付订单
request.package = @"Sign=WXPay";//商家根据财付通文档填写的数据和签名
request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";//随机串,防重发
request.timeStamp= @"1397527777";//时间戳,防重发
request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";//商家根据微信开放平台文档对数据做的签名
[WXApi sendReq:request];

PS:开发过程中,以上发起调用起支付的参数,包括预支付订单这些,一般是先请求后台,后台通过微信统一API请求(包括APP请求支付后,微信支付系统回调后台的回调地址也是在这里由后台设置好传给支付系统)获得预支付单号等信息后返回给APP,返回时的内容请参考http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios

4.支付结果回调
实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码,如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意 一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    return  [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    return [WXApi handleOpenURL:url delegate:self];
}

//实现委托
-(void)onResp:(BaseResp*)resp{
    if ([resp isKindOfClass:[PayResp class]]){
        PayResp*response=(PayResp*)resp;
        switch(response.errCode){
            case WXSuccess:
                //服务器端查询支付通知或查询API返回的结果再提示成功
            {
                ALERT_SHOW(@"支付成功");
            }

                break;
            default:
            {
                NSLog(@"支付失败,retcode=%d",resp.errCode);
                ALERT_SHOW(@"支付失败");
            }
                break;
        }
    }
}

三、银联移动集成

SDK开放台:

https://open.unionpay.com/ajweb/help/file/toDetailPage?id=302&flag=1

业务流程:

支付宝、微信、银联移动支付集成_第2张图片

SDK集成:

1.使用UPPaymentControl需要将paymentcontrol/inc目录下UPPaymentControl.h文件和paymentcontrol/libs目录下的libPaymentControl.a文件添加到商户应用的工程中

2.配置项目
(1)“Library Search Paths”增加库文件路径,将libPaymentControl.a目录设置进去,如$(PROJECT_DIR)/SDKDir
(2)在工程info.plist设置中添加一个URL Types回调协议(如工程中使用“UPPayDemo”),用于在支付完成后返回商户客户端。
(3)引人系统库
CFNetwork.framework、SystemConfiguration.framework 、libz.a(tbd)
(4)选择工程targets——》build settings ->Linking->other linker flags ,添加-ObjC

3.发起支付
tn为交易流水号,商户App从商户服务器获取tn,当tn不为空时,调用支付接口;
model 为环境参数,00表示正式环境,01表示开发环境;
scheme为info.plist设置中设置URL Types;
viewController为 发起调用的视图控制器,商户应用程序调用银联手机支付控件的视图控制器;

//当获得的tn不为空时,调用支付接口
  if (tn != nil && tn.length > 0)
  {
        [[UPPaymentControl defaultControl] 
                startPay:tn 
fromScheme:@"UPPayDemo" 
       mode:@"01" 
    viewController:self]; 
}

检测是否已安装银联App接口调用可使用:

if([[UPPaymentControl defaultControl] isPaymentAppInstalled])   
  {
      //当判断用户手机上已安装银联App,商户客户端可以做相应个性化处理
}

4.返回结果接口调用
支付控件结果处理函数handlePaymentResult: completeBlock:需要在工程AppDelegate文件的application: openURL: sourceApplication: annotation: 方法中进行调用。
支付控件结果处理函数handlePaymentResult: completeBlock:包含两个参数,参数1url为支付结果串,由handlePaymentResult: completeBlock:方法解析url内容;参数2completionBlock为商户APP定义的结果处理方法,包含两个传入参数code和data,其中code表示支付结果,取值为suceess,fail,cancel分别表示支付成功、支付失败和支付取消,data表示结果签名数据,商户使用银联公钥验证结果真实性。

对于新增的签名信息需注意以下几点:

  • 前台返回的支付结果中包含银联签名,要在商户后台对签名进行校验后才能展示结果。
  • 前台签名使用的密钥和算法与后台结果中的签名一致。
  • 如果商户APP在客户端内进行签名验证,要自行实现签名密钥更新的机制,否则更换密钥后会导致验签失败。(不推荐)
  • 商户订单是否成功支付应该以商户后台收到全渠道返回的支付结果为准,此处支付控件返回的结果仅作为参考。
- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
        [[UPPaymentControl defaultControl] handlePaymentResult:url completeBlock:^(NSString *code, NSDictionary *data) {

        //结果code为成功时,先校验签名,校验成功后做后续处理
        if([code isEqualToString:@"success"]) {

            //数据从NSDictionary转换为NSString
            NSDictionary *data;
            NSData *signData = [NSJSONSerialization dataWithJSONObject:data
                                                               options:0
                                                                 error:nil];
            NSString *sign = [[NSString alloc] initWithData:signData encoding:NSUTF8StringEncoding];

            //判断签名数据是否存在
            if(data == nil){
                //如果没有签名数据,建议商户app后台查询交易结果
                return;
            }

            //验签证书同后台验签证书
            //此处的verify,商户需送去商户后台做验签
            if([self verify:sign]) {
                //支付成功且验签成功,展示支付成功提示
            }
            else {
                //验签失败,交易结果数据被篡改,商户app后台查询交易结果
            }
        }
        else if([code isEqualToString:@"fail"]) {
            //交易失败
        }
        else if([code isEqualToString:@"cancel"]) {
            //交易取消
        }
    }];

  return YES;
}

completeBlock中的NSDictionary *data结构如下:

sign —— 签名后做Base64的数据 
data —— 用于签名的原始数据,结构如下:
     pay_result —— 支付结果success,fail,cancel
     tn          —— 订单号

Data转换为String后的示例如下:

"{"sign":"ZnZY4nqFGu/ugcXNIhniJh6UDVriWANlHtIDRzV9w120E6tUgpL9Z7jIFzWrSV73hmrkk8BZMXMc/9b8u3Ex1ugnZn0OZtWfMZk2I979dxp2MmOB+1N+Zxf8iHr7KNhf9xb+VZdEydn3Wc/xX/B4jncg0AwDJO/0pezhSZqdhSivTEoxq7KQTq2KaHJmNotPzBatWI5Ta7Ka2l/fKUv8zr6DGu3/5UaPqHhnUq1IwgxEWOYxGWQgtyTMo/tDIRx0OlXOm4iOEcnA9DWGT5hXTT3nONkRFuOSyqS5Rzc26gQE6boD+wkdUZTy55ns8cDCdaPajMrnuEByZCs70yvSgA==","data":"pay_result=success&tn=201512151321481233778"}"

5.常见问题总结
更多请参见https://open.unionpay.com帮助中心-FAQ
5.1 怎样加-ObjC宏
选择工程targets——》build settings ->Linking->other linker flags

5.2 编译时提示Undefined for architecture XXX 错误
1)由于支付控件使用到了C、C++和OC混编的情况,所以商户工程引入UPPaymentControl.h头文件以后可能会出现链接错误,这个时候可以通过以下三种方式解决:
① 将涉及到引用UPPaymentControl.h的源文件的后缀名都改为.mm;
② 如果商户不想修改源文件的后缀名,可以在工程中添加一个空的继承自NSObject的类,并将文件.m后缀名该改为.mm即可方法为new file->Objective-C class->类名自取->保存->修改后缀名为.mm;
③ 将工程的compile source as 选项的值设置为Objective–C++;
2)由于在UPPayDemo工程中添加了自定义的库文件libPaymentControl.a
,当编译Demo工程时,应该检查工程设置Search Paths里的Framework Search Paths、Header Search Paths、Library Search Paths的路径设置,看设置路径是否正确,另外还要注意里边是否多余一些不确定的路径。
3) 将xcode工程中Build Setting -> Apple LLVM compiler Language 标签下的C++ Language Dialect 和 C++ Standard Library 属性值修改为 Compiler Default如实在无法解决,尝试添加-ObjC宏的地方修改为 -force_load+空格+控件路径,如:-force_load $(PROJECT_DIR)/ libPaymentControl.a,如果还报错,上下文应该会有libPaymentControl.a文件找不到异常,比如ld: file not found: /Users/apple/Desktop/Communication 2/ libPaymentControl.a
clang: error: linker command failed with exit code 1 (use -v to see invocation)
请确定libPaymentControl.a文件确实存在于此路径,可能为上下文路径配错。

5.3 控件闪退异常’NSInvalidArgumentException’, reason: ‘-[__NSCFConstantString newSizeWithFont:详略]’
‘NSInvalidArgumentException’, reason: ‘-[__NSCFConstantString newSizeWithFont:以下略]: unrecognized selector sent to instance 0x[随机数]’
出现以上问题是由于添加-ObjC宏的地方没有配置正确,如果配-ObjC实在解决不了的话,可尝试去掉-ObjC,改为-force_load+空格+控件路径,如:-force_load $(PROJECT_DIR)/ libPaymentControl.a。

5.4 orientation异常
详细现象为控件崩溃,报错信息:Terminating app due to uncaught exception ‘UIApplicationInvalidInterfaceOrientation’, reason: ‘Supported orientations has no common orientation with the application, and shouldAutorotate is returning YES’
请勿修改横竖屏配置,iphone控件只能竖屏,ipad控件才支持横竖屏。

关于iOS9适配问题

1.以iOS9 SDK编译的工程会默认以SSL安全协议进行网络传输,即HTTPS,如果依然使用HTTP协议请求网络会报系统异常并中断请求。可在info.plist的NSAppTransportSecurity下新增NSAllowsArbitraryLoads并设置为YES,指定所有HTTP连接都可正常请求

<key>NSAppTransportSecuritykey>
<dict>
    <key>NSAllowsArbitraryLoadskey>
    <true/>
dict>

2.应用跳转(SSO等)
如果你的应用使用了如SSO授权登录或跳转分享功能,在iOS9下就需要增加一个可跳转的白名单,指定对应跳转App的URL Scheme,否则将在第三方平台判断是否跳转时用到的canOpenURL时返回NO,进而只进行webview授权或授权/分享失败。
同样在info.plist增加:

<key>LSApplicationQueriesSchemeskey>
<array>
    
    <string>wechatstring>
    <string>weixinstring>

    
    <string>alipayauthstring>
    <string>alipaystring>

    
    <string>uppaysdkstring>
    <string>uppaywalletstring>
    <string>uppayx1string>
    <string>uppayx2string>
    <string>uppayx3string>
array>

3.应用瘦身(App Thining)
iOS9 SDK新增了对App瘦身的功能,详情见App Thining。目前各个第三方平台正在进行App thining的支持,所以为了正常使用第三方SDK及分享SDK,需要在Build Setting中将Enable bitcode关闭,或设置编译标识ENABLE_BITCODE=NO。
注:bitcode仅在Xcode7以上显示并默认开启。

你可能感兴趣的:(iOS)