基于IOS下的支付宝SDK的学习与使用——实现产品支付(二)
首先本篇为作者原创,仅供学习使用,以后会不断完善,精炼。阅读之前请参考 上一篇
上一篇 中详细说明了结合官方支付宝SDK,对工程环境进行的一些配置,实现了支付,本篇重点说明一下,注意事项和原理,主要作为自己的笔记使用,在这里分享给大家。
实现主要流程:
在支付宝 demo 中给出了签名的lib工具库,因为订单需要签名成一个字符串,然后交给支付接口,签名涉及到商户方的私钥,所以将这个签名的过程交给后台去完成,在调用支付宝支付接口前,我们把必要的参数传给后台服务器,然后服务器那边签名好后返回给我们签名后的字符串,我们传给支付宝SDK支付API就能唤起支付宝支付控件或者打开支付宝钱包进行支付了。参考
下面是一些解说,以及注意事项:
我的上篇笔记中,详细的说明了基于 手机控件支付开发包(IOS版)3.1.3 SDk的使用环境配置,我的Xcode是7.2版本。对于上一篇中有的地方我要做一些补充与说明:
在Appdelegate 中添加一个方法的说明
//添加方法
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
[[AlipaySDK defaultService]
processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"result = %@",resultDic);//返回的支付结果 //【由于在跳转支付宝客户端支付的过程中,商户 app 在后台很可能被系统 kill 了,所以 pay 接 口的 callback 就会失效,请商户对 standbyCallback 返回的回调结果进行处理,就是在这个方法 里面处理跟 callback 一样的逻辑】
}];
return YES;
}
下面是一些请求参数,这里是Order类中的参数说明:
//下面的参数不可空
@property(nonatomic, copy) NSString * partner; //签约的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。
@property(nonatomic, copy) NSString * seller; //卖家支付宝账号(邮箱或手机号码格式)或其对应的支付宝唯一用户号(以2088开头的纯16位数字)。
@property(nonatomic, copy) NSString * tradeNO; //商户网站唯一订单号
@property(nonatomic, copy) NSString * productName; //商品的标题
@property(nonatomic, copy) NSString * productDescription; //商品的描述
@property(nonatomic, copy) NSString * amount; //总金额 该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位。
@property(nonatomic, copy) NSString * notifyURL; //服务器异步通知页面路径 支付宝服务器主动通知商户网站里指定的页面http路径
//以下的信息是支付的基本的配置信息(固定不变)
@property(nonatomic, copy) NSString * service; //借口名称、不可空
@property(nonatomic, copy) NSString * paymentType; //支付类型。默认值为:1(商品购买)。
@property(nonatomic, copy) NSString * inputCharset; //参数编码字符集 商户网站使用的编码格式,固定为utf-8。
@property(nonatomic, copy) NSString * itBPay; //未付款交易的超时时间 设置未付款交易的超时时间,一旦超时,该笔交易就会自动被关闭。当用户输入支付密码、点击确认付款后(即创建支付宝交易后)开始计时。取值范围:1m~15d,或者使用绝对时间(示例格式:2014-06-13 16:00:00)。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。该参数数值不接受小数点,如1.5h,可转换为90m。
@property(nonatomic, copy) NSString * showUrl; //
//下面的参数是可空的
@property(nonatomic, copy) NSString * rsaDate; //
@property(nonatomic, copy) NSString * appID; //客户端号 标识客户端。
@property(nonatomic, readonly) NSMutableDictionary * extraParams;
注意:
(1)对于上面的一些属性是支付宝官网demo中的Order.h类中声明的,我们也可以在我们的订单类中声明一些自己产品相关的其他的属性字段,但是一定不能给支付宝业务逻辑中定义的不可空属性字段重名,参考 业务字段有 { service、partner、_input_charset、sign_type、sign、notify_url、app_id、appenv、out_trade_no、subject、payment_type、seller_id、total_fee、body、goods_type、rn_check、it_b_pay、extern_token、out_context }
(2)此外不要在要拼接的参数中加入其他的key用& 连接,支付宝建议不要在请求参数中附带和支付无关的业务系统自身的key相关数据。
(3)商户的请求参数中,所有的key(支付宝关键key或者商户自己的key),其对应的value中都不应该出现支付宝关键key。
使用支付宝的步骤:
首先商户注册得到Partner 商户ID ,seller 账户ID,商户根据自己的商户ID登陆商网,获取自己的私钥和公钥,然后下载SDK,配置好使用环境,最后由程序生成订单信息,调用支付宝支付。下面是官方文档的说法:
- 构造订单数据并签名
- 商户客户端根据手机支付宝支付开发包的接口规则,通过程序生成得到签名结果及要传输给手机支付宝支付开发包的数据集合。
- 发送请求数据
- 把构造完成的数据集合传递给手机支付宝支付开发包。
- 手机支付宝支付开发包对请求数据进行处理
- 手机支付宝支付开发包将请求数据根据业务规则包装后传递给支付宝服务端,服务端得到这些集合后,会先进行安全校验等验证,一系列验证通过后便会处理完成这次发送过来的数据请求。
- 返回处理的结果数据
- 对于处理完成的交易,支付宝会以两种方式把数据分别反馈给商户应用和商户服务器。
- 在手机客户端上,开发包客户端直接把处理的数据结果反馈给商户客户端;
- 支付宝服务器主动发起通知,调用商户在请求时设定好的页面路径(参数notify_url,如果商户没设定,则不会进行该操作)。
- 对获取的返回结果数据进行处理
- 商户在客户端同步通知接收模块或服务端异步通知接收模块获取支付宝返回的结果数据后,可以结合商户自身业务逻辑进行数据处理(如:订单更新、自动充值到会员账号中等)。同步通知结果仅用于结果展示,入库数据需以异步通知为准。
使用支付宝在完成支付后,采用同步通知客户端处理结果,异步主动通知商品集合参数中早已设定好的回调服务器。
注意* 处理客户端回调的方法必须实现,否则会导致在安装有手机支付宝的情况下,支付完毕后无法正常同步返回支付结果。
支付结果的提取,必须通过CompletionBlock获取,禁止开发者私自解析支付结果返回的URL。获取值的Key对应resultStatus、memo与result(result中的值,开发者可以自行解析);
重要方法:
下面的是一些重要的信息,是在支付宝完成支付后的注意事项:
支付宝对商户的请求数据处理完成后,会将处理的结果数据直接通知给商户。这些处理结果数据就是同步通知参数。
同步返回的数据,对于商户在服务端没有收到异步通知的时候,可以依赖服务端对同步返回的结果来进行判断是否支付成功。同步返回的结果中,sign字段描述了请求的原始数据和服务端支付的状态一起拼接的签名信息。验证这个过程包括两个部分:1、原始数据是否跟商户请求支付的原始数据一致(必须验证这个);2、验证这个签名是否能通过。上述1、2通过后,在sign字段中success=true才是可信的。【特别注意,同步结果校验的逻辑,必须放在服务端处理,切记不要放在客户端】【强烈建议商户直接依赖服务端的异步通知,忽略同步返回】
上面仅仅是针对签约 mobile.securitypay.pay 这个服务业务,适用于上面的规则,如果是网页浏览器接入则建议走异步通知方式。
支付结果的返回码实例:
ResultStatus={9000};memo={};result={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"&success="true"&sign_type="RSA"&sign="hkFZr+zE9499nuqDNLZEF7W75RFFPsly876QuRSeN8WMaUgcdR00IKy5ZyBJ4eldhoJ/2zghqrD4E2G2mNjs3aE+HCLiBXrPDNdLKCZgSOIqmv46TfPTEqopYfhs+o5fZzXxt34fwdrzN4mX6S13cr3UwmEV4L3Ffir/02RBVtU="}
返回码也要注意了解一下
在上一篇中的代码中,在 NSString *orderSpec = [order description]; id<DataSigner> signer = CreateRSADataSigner(privateKey);NSString *signedString = [signer signString:orderSpec];对订单信息加密之前需要对 待签名参数集合 进行排序,并且去除 sign、sign_type 两个关键字、待组装的参数中不能有空的 value .参考订单签名规则。
对商户的请求数据处理完成后,会将处理的结果数据通过服务器主动通知的方式通知给商户网站。这些处理结果数据就是服务器异步通知参数。
关于返回参数请参考
异步通知商户服务器,是服务器之间的交流,不能像同步通知那样可见。支付宝服务器是使用POST方式向商户服务器发送包含处理结果的通知,商户得到这些信息后必须给支付宝服务器反馈一个7个字符的字符串success,如果商户程序中没有打印出,支付宝服务器会不停地发送异步通知,默认时间是24h22m,一般25小时以内完成8次通知(通知的间隔频率一般是:2m,10m,10m,1h,2h,6h,15h)。
支付宝处理完成支付之后,会将处理结果返回给商户,商户获得返回结果,必须验证签名,验证签名是否是支付宝发来的通知,需要调用走支付宝的通知验证接口(notify_verify) 参考 调用后直接回调两种结果,成功(true)和不成功(对应的错误)。商户注意需要验证处理结果中的一些信息:
商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,并判断total_fee是否确实为该订单的实际金额(即商户订单创建时的金额),同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。 如果商户需要对同步返回的数据做验签,必须通过服务端的签名验签代码逻辑来实现。如果商户未正确处理业务通知,存在潜在的风险,商户自行承担因此而产生的所有损失。
注意:
- 交易状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功;
- 交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功;或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限;
- 交易成功之后,商户(高级即时到账或机票平台商)可调用批量退款接口,系统会发送退款通知给商户,具体内容请参见批量退款接口文档;
- 当商户使用站内退款时,系统会发送包含refund_status和gmt_refund字段的通知给商户。
通知交易状态有必要也做一下了解:
退款状态:
关于不可意退款的移动快捷支付:参考
注意事项:对订单信息的签名要放在服务器端,不要放在客户端,通知地址要使用安全的超文本传输协议 https 不要使用 http 协议。要能够保证在客户端没有安装支付宝App 的情况下客户也能够完成支付。
(Over 接上一篇)