最近客户提出要开发一个IOS上的app作为访问他们网站的途径之一,为什么说之一呢,因为目前PC和Mobile这两个站目前都已经上线了。
所以问题就简单了,我们只需要把mobile站UI修改一下,然后在根据客户的新需求修改下程序,之后把这个站套个IOS的壳子就可以了。
那么问题来了,我们的mobile支付的时候调用的是支付宝的网页支付,这个页面是支付宝的,我们没办法修改,所以如果套在壳子里继续使用这种支付方式会有点不伦不类的感觉,所以我们就决定调用支付宝钱包,也就是支付宝客户端。
下面是我参考支付宝移动支付文档整理出来的一些资料:
这是demo的下载地址,包括IOS和Android两个版本
http://aopsdkdownload.cn-hangzhou.alipay-pub.aliyun-inc.com/demo/WS_MOBILE_PAY_SDK_BASE.zip?spm=0.0.0.0.LJBZ4o&file=WS_MOBILE_PAY_SDK_BASE.zip
首先要调用支付宝钱包你必须你登录商户的支付宝账户的“签约管理”里面去申请签约开通“移动支付”
PID和密钥管理
支付宝提供商户接口产品时,会自主提供一个保障商户接入安全的一组信息及其对应的配置平台,这组信息就是密钥。由商户密钥与支付宝密钥交换后与支付宝商户标识(如partnerID、APPID等)绑定。
合作伙伴密钥
在服务市场签约获得的大部分接口,签约主体是商户,接口权限属于具体的商户,这部分接口需要使用PID和密钥来调用,此类接口网关一般为mapi(https://mapi.alipay.com/gateway.do),PID对应的密钥一共有三种签名方式,分别是MD5、RSA、DSA。由于产品的特殊性,每个产品支持哪些签名方式不尽相同,具体需参考各个产品的接口技术文档。
使用合作伙伴密钥的典型功能包括:快捷登录、移动支付、即时到账收款、手机网站支付等。
查看PID和密钥
查看PID
查看MD5(Key)
查看RSA商户公钥与支付宝公钥
查看DSA商户公钥与支付宝公钥
上传公钥
上传RSA公钥
上传DSA公钥
密钥生成
RSA私钥及公钥生成
DSA私钥及公钥生成
这块流程可以让你上层领导来处理,这里的相关操作都要在商户支付宝账户中操作。
我们来看一下我们开发人员需要care的点
①交互流程:
功能流程
流程说明(以Android平台为例):
第2步:调用支付接口:此消息就是本接口所描述的开发包提供的支付对象PayTask,将商户订单信息传进pay方法唤起支付宝收银台,订单格式具体参见“请求参数说明”。
第3步:手机支付宝支付开发包将会按照商户App提供的参数发送支付请求。
第5步:异步发送支付通知:手机支付宝支付服务器端发送异步通知消息给商户服务器端,参见“服务器异步通知参数说明”。
第7步:接口返回支付结果:商户应用客户端通过当前调用支付的Activity的Handler对象,通过它的回调函数获取支付结果,参见“同步通知参数说明”。
数据交互
1.构造订单数据并签名
商户客户端根据手机支付宝支付开发包的接口规则,通过程序生成得到签名结果及要传输给手机支付宝支付开发包的数据集合。签名相关的公私钥生成及配置规则,见PID和密钥管理。
2.发送请求数据
把构造完成的数据集合传递给手机支付宝支付开发包。
手机支付宝支付开发包对请求数据进行处理
手机支付宝支付开发包将请求数据根据业务规则包装后传递给支付宝服务端,服务端得到这些集合后,会先进行安全校验等验证,一系列验证通过后便会处理完成这次发送过来的数据请求。
3.返回处理的结果数据
对于处理完成的交易,支付宝会以两种方式把数据分别反馈给商户应用和商户服务器。
在手机客户端上,开发包客户端直接把处理的数据结果反馈给商户客户端;
支付宝服务器主动发起通知,调用商户在请求时设定好的页面路径(参数notify_url,如果商户没设定,则不会进行该操作)。
4.对获取的返回结果数据进行处理
商户在客户端同步通知接收模块或服务端异步通知接收模块获取支付宝返回的结果数据后,可以结合商户自身业务逻辑进行数据处理(如:订单更新、自动充值到会员账号中等)。同步通知结果仅用于结果展示,入库数据需以异步通知为准。
②客户端调用
iOS
Alipay接口主要为商户提供订单支付功能。接口所提供的方法,如下表所示:
方法名称 | 方法描述 |
---|---|
+(Alipay *)defaultService; |
获取服务实例。 |
-(BOOL)isLogined; |
检测本地是否曾登录使用过。 |
-(void)payOrder:(NSString *)orderStr fromScheme:(NSString *)schemeStr callback:(CompletionBlock)completionBlock; |
支付并通过回调返回结果。 |
快捷订单支付iOS
方法名称:pay方法
方法原型:(void)payOrder:(NSString *)orderStr fromScheme:(NSString *)schemeStr callback:(CompletionBlock)completionBlock;
方法功能:提供给商户快捷订单支付功能。
参数名称 | 参数描述 |
---|---|
NSString* scheme | 商户程序注册的URL protocol,供支付完成后回调商户程序使用。 |
(CompletionBlock)completionBlock | 快捷支付开发包回调函数,返回免登、支付结果。相应的结果参考“同步通知参数说明”。 |
NSString* orderStr | 主要包含商户的订单信息,key=“value”形式,以&连接。 |
支付参数示例如下,参数说明见“请求参数说明”:
partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="测试"&body="测试测试"&total_fee="0.01"¬ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&sign="lBBK%2F0w5LOajrMrji7DUgEqNjIhQbidR13GovA5r3TgIbNqv231yC1NksLdw%2Ba3JnfHXoXuet6XNNHtn7VE%2BeCoRO1O%2BR1KugLrQEZMtG5jmJIe2pbjm%2F3kb%2FuGkpG%2BwYQYI51%2BhA3YBbvZHVQBYveBqK%2Bh8mUyb7GM1HxWs9k4%3D"&sign_type="RSA"
处理客户端返回url
方法名称:处理客户端方法
方法原型:-(void)processOrderWithPaymentResult:(NSURL*)resultUrl standbyCallback:(CompletionBlock)completionBlock;
方法功能:设备已安装支付宝客户端情况下,处理支付宝客户端返回的url。
注意:该方法必须实现,否则将会导致在安装手机支付宝的情况下,支付结果无法正常同步返回。
参数名称 | 参数描述 |
---|---|
NSURL *resultUrl | 支付宝客户端回传的url |
CompletionBlock completionBlock | 当支付宝客户端在操作时,商户app进程在后台被结束,只能通过这个block输出支付结果。 |
备注:请在APPDelegate的- (BOOL)application:(UIApplication )application openURL:(NSURL )url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation中调用该方法,具体可参见Demo。
回调接口
在支付过程结束后,会通过callbackBlock同步返回支付结果。
返回结果需要通过resultStatus以及result字段的值来综合判断并确定支付结果。在resultStatus=9000,并且success=“true”以及sign=“xxx”校验通过的情况下,证明支付成功。其它情况归为失败。较低安全级别的场合,也可以只通过检查resultStatus以及success=“true”来判定支付结果。以下为订单支付成功的完成信息示例:
ResultStatus={9000};memo={};result={partner="2088101568358171"&seller_id="[email protected]"&out_trade_no="0819145412-6177"&subject="测试"&body="测试测试"&total_fee="0.01"¬ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&success="true"&sign_type="RSA"&sign="hkFZr+zE9499nuqDNLZEF7W75RFFPsly876QuRSeN8WMaUgcdR00IKy5ZyBJ4eldhoJ/2zghqrD4E2G2mNjs3aE+HCLiBXrPDNdLKCZgSOIqmv46TfPTEqopYfhs+o5fZzXxt34fwdrzN4mX6S13cr3UwmEV4L3Ffir/02RBVtU="}
注意:
支付结果的提取,必须通过CompletionBlock获取,禁止开发者私自解析支付结果返回的URL。获取值的Key对应resultStatus、memo与result(result中的值,开发者可以自行解析);
为了保障已有商户的正常使用,返回参数ResultStatus首字母为大写。在新的SDK中已经用统一的工具类实现了ResultStatus到resultStatus的转换,商户从CompletionBlock中获取resultStatus即可。
③导入代码:
步骤1:启动IDE(如Xcode),把iOS包中的压缩文件中以下文件拷贝到项目文件夹下,并导入到项目工程中。
AlipaySDK.bundle
AlipaySDK.framework
在Build Phases选项卡的Link Binary With Libraries中,增加以下依赖:
其中,需要注意的是:
如果是Xcode 7.0之后的版本,需要添加libc++.tbd、libz.tbd;
如果是Xcode 7.0之前的版本,需要添加libc++.dylib、libz.dylib(如下图)。
步骤2:在需要调用AlipaySDK的文件中,增加头文件引用。
#import <AlipaySDK/AlipaySDK.h>
步骤3:如果你的app基于9.0编译,那么为了适配iOS9.0中的App Transport Security(ATS)对http的限制,这里需要对支付宝的请求地址alipay.com、alipayobjects.com做例外,在app对应的info.list中添加如下配置(文中以XML格式描述)。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>alipay.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSTemporaryExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
<key>alipayobjects.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSTemporaryExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
说明:
如果商户配置了如下的配置:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
则上述的NSAppTransportSecurity可以不配置。
步骤4:配置请求信息。
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<DataSigner> 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) {
//【callback处理支付结果】
NSLog(@"reslut = %@",resultDic);
}];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
详细可参见Demo中示例文件
AliSDKDemo\APViewController.h
AliSDKDemo\APViewController.m
AliSDKDemo\Order.h
AliSDKDemo\Order.m
步骤5:配置支付宝客户端返回url处理方法。
(外部存在支付包钱包,支付宝钱包将处理结果通过url返回。)
如示例AliSDKDemo\APAppDelegate.m文件中,增加引用代码:
#import <AlipaySDK/AlipaySDK.h>
在@implementation AppDelegate中增加如下代码:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
//如果极简开发包不可用,会跳转支付宝钱包进行支付,需要将支付宝钱包的支付结果回传给开发包
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
//【由于在跳转支付宝客户端支付的过程中,商户app在后台很可能被系统kill了,所以pay接口的callback就会失效,请商户对standbyCallback返回的回调结果进行处理,就是在这个方法里面处理跟callback一样的逻辑】
NSLog(@"result = %@",resultDic);
}];
}
if ([url.host isEqualToString:@"platformapi"]){//支付宝钱包快登授权返回authCode
[[AlipaySDK defaultService] processAuthResult:url standbyCallback:^(NSDictionary *resultDic) {
//【由于在跳转支付宝客户端支付的过程中,商户app在后台很可能被系统kill了,所以pay接口的callback就会失效,请商户对standbyCallback返回的回调结果进行处理,就是在这个方法里面处理跟callback一样的逻辑】
NSLog(@"result = %@",resultDic);
}];
}
return YES;
}
④针对demo的运行注意:
1、关于签名代码问题
AliSDKDemo\Util及下面所有文件
AliSDKDemo\openssl及下面所有文件
libcrypto.a
libssl.a
这些文件是为示例签名所在客户端本地使用。出于安全考虑,请商户尽量把私钥保存在服务端,在服务端进行签名验签。
2、如果遇到运行后报错,类似于以下提示信息:
Cannot find interface declaration for 'NSObject', superclass of 'Base64'
那么需要打开报错了的文件,增加头文件。
#import <Foundation/Foundation.h>
3、如果商户要在某个文件中使用支付宝的开发包类库,需增加引用头文件。
#import <AlipaySDK/AlipaySDK.h>
4、点击项目名称,点击“Build Settings”选项卡,在搜索框中,以关键字“search”搜索,对“Header Search Paths”增加头文件路径:$(SRCROOT)/项目名称。如果头文件信息已增加,可不必再增加。
5、点击项目名称,点击“Build Phases”选项卡,在“Link Binary with Librarles”选项中,新增“AlipaySDK.framework”和“SystemConfiguration.framework”两个系统库文件。如果商户项目中已有这两个库文件,可不必再增加。
6、点击项目名称,点击“Info”选项卡,在“URL Types”选项中,点击“+”,在“URL Schemes”中输入“alisdkdemo”。“alisdkdemo”来自于文件“APViewController.m”的NSString *appScheme = @“alisdkdemo”;。
注意:这里的URL Schemes中输入的alisdkdemo,为测试demo,实际商户的app中要填写独立的scheme,建议跟商户的app有一定的标示度,要做到和其他的商户app不重复,否则可能会导致支付宝返回的结果无法正确跳回商户app。
⑤基本配置信息:
打开“APViewController.m”文件,对以下三个参数进行编辑。
NSString *partner = @"";
NSString *seller = @"";
NSString *privateKey = @"";
IOS基本信息配置表:
参数 | 含义 |
---|---|
partner | 合作身份者ID,以2088开头由16位纯数字组成的字符串。请参考查看PID。 |
seller | 支付宝收款账号,手机号码或邮箱格式。 |
private_key | 商户方的私钥,pkcs8格式。请参考RSA私钥及公钥生成。 |
注意:这些参数配置是为客户端签名功能服务的,仅作为示例使用。商户在接入支付宝产品时,请把这些信息通过商户项目自己的服务端传递。
⑥代码示例运行逻辑:
步骤1:调用order.m里的函数description将商品信息拼接成字符串作为待签名字符串,如:
"partner=\"2088101568353491\"&seller_id=\"2088101568353491\"&out_trade_no=\"YR2VGG3G1I31XDZ\"&subject=\"1\"&body=\"我是测试数据\"&total_fee=\"0.02\"¬ify_url=\"http://www.xxx.com\"&service=\"mobile.securitypay.pay\"&payment_type=\"1\"&_input_charset=\"utf-8\"&it_b_pay=\"30m\""
步骤2:使用类CreateRSADataSigner,调用signString签名函数做签名,如:
"GsSZgPloF1vn52XAItRAldwQAbzIgkDyByCxMfTZG%2FMapRoyrNIJo4U1LUGjHp6gdBZ7U8jA1kljLPqkeGv8MZigd3kH25V0UK3Jc3C94Ngxm5S%2Fz5QsNr6wnqNY9sx%2Bw6DqNdEQnnks7PKvvU0zgsynip50lAhJmflmfHvp%2Bgk%3D"
步骤3:把签名结果赋值给参数sign,并把sign加入之前的待签名数组中,此时得到的便是要请求给支付宝的全部数据。
"partner=\"2088101568353491\"&seller_id=\"2088101568353491\"&out_trade_no=\"YR2VGG3G1I31XDZ\"&subject=\"1\"&body=\"我是测试数据\"&total_fee=\"0.02\"¬ify_url=\"http://www.xxx.com\"&service=\"mobile.securitypay.pay\"&payment_type=\"1\"&_input_charset=\"utf-8\"&it_b_pay=\"30m\"&sign=\"GsSZgPloF1vn52XAItRAldwQAbzIgkDyByCxMfTZG%2FMapRoyrNIJo4U1LUGjHp6gdBZ7U8jA1kljLPqkeGv8MZigd3kH25V0UK3Jc3C94Ngxm5S%2Fz5QsNr6wnqNY9sx%2Bw6DqNdEQnnks7PKvvU0zgsynip50lAhJmflmfHvp%2Bgk%3D\"&sign_type=\"RSA\""
步骤4:调用(AlipaySDK *)defaultService类下面的支付接口函数,唤起支付宝支付页面。
(void)payOrder:(NSString *)orderStr
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock
appScheme为app在info.plist注册的scheme。
支付宝支付页面:
后面的动作全由买家在支付宝收银台中操作完成。如果设备中有支付宝客户端,会优先调用支付宝客户端进行支付,支付完成后会重新唤起商户app。
步骤5:当这笔交易被买家支付成功后支付宝收银台上显示该笔交易成功,并提示用户“返回”。此时在APAppDelegate.m的 - (BOOL)application:(UIApplication )application openURL:(NSURL )url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 中调用获取返回数据的代码:
[[AlipaySDK defaultService]
processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"result = %@",resultDic);//返回的支付结果
//【由于在跳转支付宝客户端支付的过程中,商户app在后台很可能被系统kill了,所以pay接口的callback就会失效,请商户对standbyCallback返回的回调结果进行处理,就是在这个方法里面处理跟callback一样的逻辑】
}];
拿到返回数据:
点取消后返回
"alisdkdemo://safepay/?%7B%22memo%22:%7B%22result%22:%22%22,%22memo%22:%22%E7%94%A8%E6%88%B7%E4%B8%AD%E9%80%94%E5%8F%96%E6%B6%88%22,%22ResultStatus%22:%226001%22%7D,%22requestType%22:%22safepay%22%7D"
对其做URLDecode
"alisdkdemo://safepay/?{"memo":{"result":"","memo":"用户中途取消","ResultStatus":"6001"},"requestType":"safepay"}"
点确认后返回
"alisdkdemo://safepay/?%7B%22memo%22:%7B%22result%22:%22partner=%5C%222088101568353491%5C%22&seller_id=%5C%222088101568353491%5C%22&out_trade_no=%5C%22QU6ZOD85K4HVQFN%5C%22&subject=%5C%221%5C%22&body=%5C%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%5C%22&total_fee=%5C%220.02%5C%22¬ify_url=%5C%22http:%5C/%5C/www.xxx.com%5C%22&service=%5C%22mobile.securitypay.pay%5C%22&payment_type=%5C%221%5C%22&_input_charset=%5C%22utf-8%5C%22&it_b_pay=%5C%2230m%5C%22&success=%5C%22true%5C%22&sign_type=%5C%22RSA%5C%22&sign=%5C%22pg16DPA%5C/cIRg1iUFCl8lYZG54de+kfw+vCj32hGWye97isZ1A4bW6RNaDXHhZXVaI5Vk2YDxhNUl85EHRd+EL7%5C/+ogQTnsaEHl+D13PuZExIXRKGBnkYqaNV6kH6hDygnf5IOtoojHWLQyem7oRBVzB0vlF%5C/+YGFpzFHZyTVpM8=%5C%22%22,%22memo%22:%22%22,%22ResultStatus%22:%229000%22%7D,%22requestType%22:%22safepay%22%7D"
对其做URLDecode
"alisdkdemo://safepay/?{"memo":{"result":"partner=\"2088101568353491\"&seller_id=\"2088101568353491\"&out_trade_no=\"QU6ZOD85K4HVQFN\"&subject=\"1\"&body=\"我是测试数据\"&total_fee=\"0.02\"¬ify_url=\"http:\/\/www.xxx.com\"&service=\"mobile.securitypay.pay\"&payment_type=\"1\"&_input_charset=\"utf-8\"&it_b_pay=\"30m\"&success=\"true\"&sign_type=\"RSA\"&sign=\"pg16DPA\/cIRg1iUFCl8lYZG54de+kfw+vCj32hGWye97isZ1A4bW6RNaDXHhZXVaI5Vk2YDxhNUl85EHRd+EL7\/+ogQTnsaEHl+D13PuZExIXRKGBnkYqaNV6kH6hDygnf5IOtoojHWLQyem7oRBVzB0vlF\/+YGFpzFHZyTVpM8=\"","memo":"","ResultStatus":"9000"},"requestType":"safepay"}"
之后,对这些数据做处理。
注意:
由于在跳转支付宝客户端支付的过程中,商户app在后台很可能被系统kill了,所以pay接口的callback就会失效,请商户对standbyCallback返回的回调结果进行处理;
同步返回数据时,建议通过服务端的验签功能代码做验签处理,之后再对返回的数据做业务逻辑处理;
须以服务器异步通知的结果数据为准,并对其做业务逻辑处理;
SDK付款有两种模式:如果外部存在支付宝钱包,则直接跳转到支付宝钱包付款;不存在的场景下,在SDK内部进行H5支付。测试同学需要关注这两类测试场景。
至此文档结束,由于我本人对于IOS开发并不精通,所以以上绝大部分借鉴支付宝相关文档,但都是我认为比较重要的地方,仅供参考。