iOS___oc app中接入支付宝详细流程
第一步:创建应用并获取APPID,添加应用功能
要在您的应用中使用支付宝开放产品的接口能力,您需要先去蚂蚁金服开放平台(open.alipay.com),在开发者中心创建登记您的应用,并提交审核,审核通过后会为您生成应用唯一标识(APPID),并且可以申请开通开放产品使用权限,通过APPID您的应用才能调用开放产品的接口能力。
1、添加应用功能
某些功能必须签约才能使用:签约 如下:
应用上线后,可选择自己使用和推广给他人使用。供他人使用指的是应用开发者和应用使用者不是同一个人。使用者在使用应用前,需要做两件事:授权、签约(如果应用中包含需要签约的功能则需要签约,不包含则不需要)
开发者和使用者的区别,开发者:应用(功能或者服务)的开发人员(或者企业),使用者:购买和使用应用(功能或者服务)的人员(或者企业)。当开发者自己使用自己开发的应用时,开发者和使用者的两个身份是重合的。
商户(例如:是某个电商网站)队开发者进行应用授权后,开发者可以帮助商户完成相应的业务逻辑,例如代替商户发起当面付的收单请求。
2、授权采用标准
3、要进行第三方调用,开发者需要在应用中添加对应功能并获得商户授权,商户需要申请开通相应权限(例如对于当面付,开发者只需在应用中添加“当面付”功能并获得商户授权,商户则需要开通“当面付”产品),之后开发者就可以帮助商户发起当面付的收单请求)。
如应用中包含服务窗功能,且使用者需要使用此功能,则在给开发者授权后,还需开通服务窗并在服务器应用“事件订阅与重试机制”中进行如下设置,只有这样开发者才能帮助使用者管理服务窗。
开发者所需配置内容请参考:
必须填写“接口加密方式”(加密方式只需填写一个),才可以提交审核
第二步:配置密钥
上传应用公钥并获取支付宝公钥,(https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.TvAv9s&treeId=291&articleId=105972&docType=1);支付宝公钥内容,在代码中验签使用。
服务器的SDK,签名和验
(https://doc.open.alipay.com/docs/doc.htm?treeId=54&articleId=103419&docType=1);
开发者调用接口前需要先生成RSA密钥,RSA 密钥包含应用私钥(APP_PRIVATE_KEY)、应用公钥(APP_PUBLIC_KEY)。生成密钥后在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥(ALIPAY_PUBLIC_KEY)。
生成RSA密钥
支付宝提供一键生成工具便于开发者生成一对RSA密钥,可通过下方链接下载密钥生成工具:
下载该工具后,解压打开文件夹,运行“RSA签名验签工具.bat”(WINDOWS)或“RSA签名验签工具.command” (MAC_OSX)。
界面示例:
详细步骤:
1、根据开发语言选择密钥格式。
2、选择密钥长度,建议使用2048位。
3、点击“生成密钥”,会自动生成商户应用公钥和应用私钥。
4、点击“打开密钥文件路径”,即可找到生成的公私钥。如图:
生成的私钥需妥善保管,避免遗失,不要泄露。应用私钥需填写到代码中供签名使用。应用公钥需提供给支付宝账户管理者上传到支付宝开放平台。
除了使用支付宝提供的一键生成密钥工具外,也可以使用OpenSSL工具命令生成密钥。步骤如下:
使用OpenSSL工具生成密钥
除了使用支付宝提供的一键生成工具外,也可以使用OpenSSL工具(下载地址https://www.openssl.org/source/?spm=a219a.7629140.0.0.b0VRLV)命令生成密钥。
第一步 生成RSA密钥
首先进入OpenSSL工具,输入以下命令。
OpenSSL> genrsa -out app_private_key.pem
1024
#生成私钥
OpenSSL> pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem #生成公钥
OpenSSL> exit #退出OpenSSL程序
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQC+L0rfjLl3neHleNMOsYTW8r0QXZ5RVb2p/vvY3fJNNugvJ7lo4+fdBz+LN4mDxTz4MTOhi5e 2yeAqx+v3nKpNmPzC5 LmDjhHZURhwbqFtIpZD51mOfno2c3MDwlrsVi6mTypbNu4uaQzw/TOpwufSLWF7k6p2pLo VmmqJzQiD0QIDAQABAoGAakB1risquv9D4zX7hCv9MTFwGyKSfpJOYhkIjwKAik7wrNeeqFEbisqv35FpjGq3Q1o JpGkem4pxaLVEyZOHONefZ9MGVChT/MNH5b0FJYWl392RZy8KCdq376Vt4gKVlABvaV1DkapL+nLh7LMo/bENudA RsxD55IGObMU19lkCQQDwHmzWPMHfc3kdY6AqiLrOss+MVIAhQqZOHhDe0aW2gZtwiWeYK1wB/fRxJ5esk1sScOW gzvCN/oGJLhU3kipHAkEAysNoSdG2oWADxlIt4W9kUiiiqNgimHGMHPwp4JMxupHMTm7D9XtGUIiDijZxunHv3kv ktNfWj3Yji0661zHVJwJBAM8TDf077F4NsVc9AXVs8N0sq3xzqwQD/HPFzfq6hdR8tVY5yRMb4X7+SX4EDPORKKs gnYcur5lk8MUi7r072iUCQQC8xQvUne+fcdpRyrR4StJlQvucogwjTKMbYRBDygXkIlTJOIorgudFlrKP/HwJDoY4u QNl8gQJb/1LdrKwIe7FAkBl0TNtfodGrDXBHwBgtN/t3pyi+sz7OpJdUklKE7zMSBuLd1E3O4JMzvWP9wEE7JDb+br jgK4/cxxUHUTkk592 -----END RSA PRIVATE KEY-----
|
PKCS8处理后的私钥文件示例(java使用)
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAN0yqPkLXlnhM+2H/57aHsYHaHXazr9pFQun907TMvmbR04wHChVsKVgGUF1hC0FN9hfeYT5v2SXg1WJSg2tSgk7F29SpsF0I36oSLCIszxdu7ClO7c22mxEVuCjmYpJdqb6XweAZzv4Is661jXP4PdrCTHRdVTU5zR9xUByiLSVAgMBAAECgYEAhznORRonHylm9oKaygEsqQGkYdBXbnsOS6busLi6xA+iovEUdbAVIrTCG9t854z2HAgaISoRUKyztJoOtJfI1wJaQU+XL+U3JIh4jmNx/k5UzJijfvfpT7Cv3ueMtqyAGBJrkLvXjiS7O5ylaCGuB0Qz711bWGkRrVoosPM3N6ECQQD8hVQUgnHEVHZYtvFqfcoq2g/onPbSqyjdrRu35a7PvgDAZx69Mr/XggGNTgT3jJn7+2XmiGkHM1fd1Ob/3uAdAkEA4D7aE3ZgXG/PQqlm3VbE/+4MvNl8xhjqOkByBOY2ZFfWKhlRziLEPSSAh16xEJ79WgY9iti+guLRAMravGrs2QJBAOmKWYeaWKNNxiIoF7/4VDgrcpkcSf3uRB44UjFSn8kLnWBUPo6WV+x1FQBdjqRviZ4NFGIP+KqrJnFHzNgJhVUCQFzCAukMDV4PLfeQJSmna8PFz2UKva8fvTutTryyEYu+PauaX5laDjyQbc4RIEMU0Q29CRX3BA8WDYg7YPGRdTkCQQCG+pjU2FB17ZLuKRlKEdtXNV6zQFTmFc1TKhlsDTtCkWs/xwkoCfZKstuV3Uc5J4BNJDkQOGm38pDRPcUDUh2/
公钥文件示例
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQWiDVZ7XYxa4CQsZoB3n7bfxLDkeGKjyQPt2FUtm4TWX9OYrd523iw6UUqnQ+Evfw88JgRnhyXadp+vnPKP7unormYQAfsM/CxzrfMoVdtwSiGtIJB4pfyRXjA+KL8nIa2hdQy5nLfgPVGZN4WidfUY/QpkddCVXnZ4bAUaQjXQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQWiDVZ7XYxa4CQsZoB3n7bfxLDkeGKjyQPt2FUtm4TWX9OYrd523iw6UUqnQ+Evfw88JgRnhyXadp+vnPKP7unormYQAfsM/CxzrfMoVdtwSiGtIJB4pfyRXjA+KL8nIa2hdQy5nLfgPVGZN4WidfUY/QpkddCVXnZ4bAUaQjXQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB
-----END PUBLIC KEY-----
AlipayClient alipayClient =
new
DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);
配置参数 | 示例值解释 | 获取方式/示例值 |
---|---|---|
URL | 支付宝网关(固定) | https://openapi.alipay.com/gateway.do |
APP_ID | APPID即创建应用后生成 | 获取见上面创建应用并获取APPID |
APP_PRIVATE_KEY | 开发者应用私钥,由开发者自己生成 | 获取见上面配置密钥 |
FORMAT | 参数返回格式,只支持json | json(固定) |
CHARSET | 请求和签名使用的字符编码格式,支持GBK和UTF-8 | 开发者根据实际工程编码配置 |
ALIPAY_PUBLIC_KEY | 支付宝公钥,由支付宝生成 | 获取详见上面配置密钥 |
SIGN_TYPE | 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 | RSA2 |
接下来,就可以用alipayClient来调用具体的API了。alipayClient只需要初始化一次,后续调用不同的API都可以使用同一个alipayClient对象。
注意:APP支付不支持第三方授权,不能代商家发起请求。
开放平台第三方应用安全开发指南(https://doc.open.alipay.com/docs/doc.htm?treeId=275&articleId=105912&docType=1)。
//支付宝中验签的解释:
说一下简单支付流程:你提交等待支付的订单信息给支付宝,支付宝返回订单支付结果给你(这里暂时先不考虑服务器)。但是这里就有安全问题了,支付宝怎么知道你提交的订单信息商家的真实性?你又怎么知道支付宝返回的结果是支付宝官方操作而不是被篡改过的呢?
所以就有了安全验证一说,也就是私钥和公钥了。商家和支付宝都有一对公钥和私钥,支付宝的公钥提供给了每个商家(现在是统一的),商家的公钥在生成时也是应该提交到支付宝的。私钥都是自己留着,不给别人。私钥用来数字签名,公钥来对私钥签过名的信息作验证。
所以为了安全,你需要在发送订单信息的时候用你的私钥签名,发送给支付宝,支付宝用你的公钥去验证你的订单是否是本人。然后支付宝返回用支付宝私钥签名过的支付结果给你,你这个时候就需要用支付宝公钥去验证到底是不是真正的支付宝返回的信息。
/*
*生成订单信息及签名
*/
//将商品信息赋予AlixPayOrder的成员变量
Order* order = [Ordernew];
// NOTE: app_id设置
order.app_id = appID;
// NOTE: 支付接口名称
order.method =@"alipay.trade.app.pay";
// NOTE: 参数编码格式
order.charset =@"utf-8";
// NOTE: 当前时间点
NSDateFormatter* formatter = [NSDateFormatternew];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
order.timestamp = [formatterstringFromDate:[NSDatedate]];
// NOTE: 支付版本
order.version =@"1.0";
// NOTE: sign_type设置
order.sign_type =@"RSA";
// NOTE: 商品数据
order.biz_content = [BizContentnew];
order.biz_content.body =@"我是测试数据";
order.biz_content.subject =@"1";
order.biz_content.out_trade_no = [selfgenerateTradeNO];//订单ID(由商家自行制定)
order.biz_content.timeout_express =@"30m";//超时时间设置
order.biz_content.total_amount = [NSStringstringWithFormat:@"%.2f",0.01];//商品价格
//将商品信息拼接成字符串
NSString *orderInfo = [orderorderInfoEncoded:NO];
NSString *orderInfoEncoded = [orderorderInfoEncoded:YES];
NSLog(@"orderSpec = %@",orderInfo);
// NOTE: 获取私钥并将商户信息签名,外部商户的加签过程请务必放在服务端,防止公私钥数据泄露;
// 需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
id<DataSigner> signer =CreateRSADataSigner(privateKey);//生成签名器
NSString *signedString = [signersignString:orderInfo];
// NOTE: 如果加签成功,则继续执行支付
if (signedString !=nil) {
//应用注册scheme,在AliSDKDemo-Info.plist定义URL types
NSString *appScheme =@"alisdkdemo";
// NOTE: 将签名成功字符串格式化为订单字符串,请严格按照该格式
NSString *orderString = [NSStringstringWithFormat:@"%@&sign=%@",
orderInfoEncoded, signedString];
// NOTE: 调用支付结果开始支付
[[AlipaySDKdefaultService]payOrder:orderStringfromScheme:appSchemecallback:^(NSDictionary*resultDic) {
//这里的支付结果是在没有安装支付宝客服端的情况下跳转网页支付时,会在这回调
NSLog(@"返回结果resultDic = %@",resultDic);
if (resultDic)
{
/*
9000 订单支付成功
8000 正在处理中
4000 订单支付失败
6001 用户中途取消
6002 网络连接出错
*/
if ([resultDic[@"resultStatus"]integerValue] == 9000)
//网上很多教程到这里就结束了,因为他们没有验证返回订单签名
{
//验签
//去掉返回字典中result值里面的“\\”
NSString *result = [resultDic[@"result"] stringByReplacingOccurrencesOfString:@"\\\\" withString:@""];
//分割字符串获取订单信息和签名
NSArray *array = [result componentsSeparatedByString:@"&sign_type=\\"RSA\\"&sign=\\""];
//返回的订单信息
NSString *orderString = array[0];
//返回的订单签名
NSString *signedString = [array[1] substringToIndex:[array[1]length]-1];
id<DataVerifier> dataVeri = CreateRSADataVerifier(@"public");//用支付宝公钥生成签名器
//支付宝公钥匙验证返回信息与签名 if ([dataVeri verifyString:orderString withSign:signedString]) { //验证签名成功,交易结果无篡改 NSLog(@"------------支付成功---------------"); } else { //验签错误 } } } else { //交易失败 } }]; }
}];
}
}下面是appdelegate中的方法:
//跳转支付宝支付(安装了支付宝客户端的情况)时,支付完成之后重新回到本app会调用此方法,在此可以根据resultDic提示支付结果
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
**验证方式同上面的网页支付**
}];
}
//这个是进程KILL掉之后也会调用,这个只是第一次授权回调,同时也会返回支付信息,
//处理支付宝客户端返回的url(在app被杀模式下,通过这个方法获取支付结果)。
[[AlipaySDK defaultService]processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
NSString * str = resultDic[@"result"];
NSLog(@"result = %@",str);
}];
return YES;
}
不推荐客户端验证,应该由后台来做(后台做更安全)。
由于同步通知和异步通知都可以作为支付完成的凭证,且异步通知支付宝一定会确保发送给商户服务端。为了简化集成流程,商户可以将同步结果仅仅作为一个支付结束的通知(忽略执行校验),实际支付是否成功,完全依赖服务端异步通知。