94 lines
5.7 KiB
Plaintext
94 lines
5.7 KiB
Plaintext
|
|
服务器异步通知页面特性
|
|||
|
|
|
|||
|
|
必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的
|
|||
|
|
异常提示信息等;
|
|||
|
|
支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:
|
|||
|
|
request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
|
|||
|
|
支付宝主动发起通知,该方式才会被启用;
|
|||
|
|
只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务
|
|||
|
|
器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
|
|||
|
|
服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
|
|||
|
|
第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器
|
|||
|
|
异步通知页面也会收到支付宝发来的处理结果通知;
|
|||
|
|
程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这
|
|||
|
|
7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通
|
|||
|
|
知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
|
|||
|
|
程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会
|
|||
|
|
被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
|
|||
|
|
cookies、session等在此页面会失效,即无法获取这些数据;
|
|||
|
|
该方式的调试与运行必须在服务器上,即互联网上能访问;
|
|||
|
|
该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
|
|||
|
|
当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是
|
|||
|
|
说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),
|
|||
|
|
服务器异步通知参数notify_id是不变的。
|
|||
|
|
|
|||
|
|
异步返回结果的验签
|
|||
|
|
|
|||
|
|
某商户设置的通知地址为https://api.xx.com/receive_notify.htm,对应接收到通知的示例如下:
|
|||
|
|
|
|||
|
|
1
|
|||
|
|
https://api.xx.com/receive_notify.htm?
|
|||
|
|
total_amount=2.00&buyer_id=2088102116773037&body=大乐透2.1
|
|||
|
|
&trade_no=2016071921001003030200089909&refund_fee=0.00
|
|||
|
|
¬ify_time=2016-07-19 14:10:49&subject=大乐透2.1
|
|||
|
|
&sign_type=RSA&charset=utf-8¬ify_type=trade_status_sync
|
|||
|
|
&out_trade_no=0719141034-6418&gmt_close=2016-07-19 14:10:46
|
|||
|
|
&gmt_payment=2016-07-19 14:10:47&trade_status=TRADE_SUCCESS
|
|||
|
|
&version=1.0&sign=kPbQIjX+xQc8F0/A6/AocELIjhhZnGbcBN6G4MM/Hmf
|
|||
|
|
WL4ZiHM6fWl5NQhzXJusaklZ1LFuMo+lHQUELAYeugH8LYFvxnNajOvZhu
|
|||
|
|
xNFbN2LhF0l/KL8ANtj8oyPM4NN7Qft2kWJTDJUpQOzCzNnV9hDxh5AaT9
|
|||
|
|
FPqRS6ZKxnzM=
|
|||
|
|
&gmt_create=2016-07-19 14:10:44&app_id=2015102700040153
|
|||
|
|
&seller_id=2088102119685838¬ify_id=4a91b7a78a503640467525113fb7d8bg8e
|
|||
|
|
第一步: 在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的
|
|||
|
|
参数皆是待验签的参数。
|
|||
|
|
|
|||
|
|
第二步: 将剩下参数进行url_decode, 然后进行字典排序,组成字符串,得到待签名字符串:
|
|||
|
|
|
|||
|
|
1
|
|||
|
|
body=大乐透2.1&
|
|||
|
|
buyer_id=2088102116773037&
|
|||
|
|
charset=utf-8&
|
|||
|
|
gmt_close=2016-07-19 14:10:46&
|
|||
|
|
gmt_payment=2016-07-19 14:10:47&
|
|||
|
|
notify_time=2016-07-19 14:10:49&
|
|||
|
|
notify_type=trade_status_sync&
|
|||
|
|
out_trade_no=0719141034-6418&
|
|||
|
|
refund_fee=0.00&
|
|||
|
|
subject=大乐透2.1&
|
|||
|
|
total_amount=2.00&
|
|||
|
|
trade_no=2016071921001003030200089909&
|
|||
|
|
trade_status=TRADE_SUCCESS&
|
|||
|
|
version=1.0
|
|||
|
|
第三步: 将签名参数(sign)使用base64解码为字节码串。
|
|||
|
|
|
|||
|
|
第四步: 使用RSA的验签方法,通过签名字符串、签名参数(经过base64解码)及支付宝公钥验证签名。
|
|||
|
|
|
|||
|
|
第五步:在步骤四验证签名正确后,必须再严格按照如下描述校验通知数据的正确性。
|
|||
|
|
|
|||
|
|
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
|
|||
|
|
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
|
|||
|
|
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应
|
|||
|
|
的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
|
|||
|
|
4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知
|
|||
|
|
是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进
|
|||
|
|
行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态
|
|||
|
|
为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
|
|||
|
|
验签过程代码描述【这里列举java示例,按照服务端SDK中提供的工具类】:
|
|||
|
|
|
|||
|
|
|
|||
|
|
Map<String, String> paramsMap = ... //将异步通知中收到的待验证所有参数都存放到map中
|
|||
|
|
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap,
|
|||
|
|
ALIPAY_PUBLIC_KEY, CHARSET) //调用SDK验证签名
|
|||
|
|
if(signVerfied){
|
|||
|
|
// TODO 验签成功后
|
|||
|
|
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,
|
|||
|
|
校验成功后在response中返回success,校验失败返回failure
|
|||
|
|
}else{
|
|||
|
|
// TODO 验签失败则记录异常日志,并在response中返回failure.
|
|||
|
|
}
|
|||
|
|
注意:
|
|||
|
|
|
|||
|
|
状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功;
|
|||
|
|
交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买
|
|||
|
|
家付款成功;或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限。
|