支付宝集成时,有同步返回地址return_url和异步通知地址notify_url,这两个地址中的参数与签名验证有非常大的关系,在支付宝的说明中,强调return_url不可以有自定义参数,否则会导致sign和mysign不相等,但有时我们需要一些自定义参数来做判断,怎么办呢?
签名验证机制
支付宝签名有两次,第一次是发送的时候,第二次是返回的时候,返回又分为同步返回和异步通知,这两个的签名方式是相同的。
向支付宝发送参数时,调用如下函数产生签名,并构建生成表单的数组
1
2
3
4
5
6
7
8
9
10
11
12
|
function
buildRequestPara(
$para_temp
,
$aliapy_config
) {
//除去待签名参数数组中的空值和签名参数
$para_filter
= paraFilter(
$para_temp
);
//对待签名参数数组排序
$para_sort
= argSort(
$para_filter
);
//生成签名结果
$mysign
= buildMysign(
$para_sort
, trim(
$aliapy_config
[
'key'
]),
strtoupper
(trim(
$aliapy_config
[
'sign_type'
])));
//签名结果与签名方式加入请求提交参数组中
$para_sort
[
'sign'
] =
$mysign
;
$para_sort
[
'sign_type'
] =
strtoupper
(trim(
$aliapy_config
[
'sign_type'
]));
return
$para_sort
;
}
|
数据返回时,调用下面的函数生成签名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
function
verifyReturn(){
if
(
empty
(
$_GET
)) {
//判断POST来的数组是否为空
return
false;
}
else
{
//生成签名结果
$mysign
=
$this
->getMysign(
$_GET
);
//获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
$responseTxt
=
'true'
;
if
(!
empty
(
$_GET
[
"notify_id"
])) {
$responseTxt
=
$this
->getResponse(
$_GET
[
"notify_id"
]);}
//写日志记录
$log_text
=
"responseTxt="
.
$responseTxt
.
"\n notify_url_log:sign="
.
$_GET
[
"sign"
].
"&mysign="
.
$mysign
.
","
;
$log_text
=
$log_text
.createLinkString(
$_GET
);
logResult(
$log_text
);
//验证
//$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//mysign与sign不等,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
if
(preg_match(
"/true$/i"
,
$responseTxt
) &&
$mysign
==
$_GET
[
"sign"
]) {
return
true;
}
else
{
return
false;
}
}
}
function
getMysign(
$para_temp
) {
//除去待签名参数数组中的空值和签名参数
$para_filter
= paraFilter(
$para_temp
);
//对待签名参数数组排序
$para_sort
= argSort(
$para_filter
);
//生成签名结果
$mysign
= buildMysign(
$para_sort
, trim(
$this
->aliapy_config[
'key'
]),
strtoupper
(trim(
$this
->aliapy_config[
'sign_type'
])));
return
$mysign
;
}
|
两者最终都要调用buileMysign函数,如下
1
2
3
4
5
6
7
8
|
function
buildMysign(
$sort_para
,
$key
,
$sign_type
=
"MD5"
) {
//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
$prestr
= createLinkstring(
$sort_para
);
//把拼接后的字符串再与安全校验码直接连接起来
$prestr
=
$prestr
.
$key
;
//把最终的字符串签名,获得签名结果
$mysgin
= sign(
$prestr
,
$sign_type
);
return
$mysgin
;
|
从这些过程可以得出以下结论
- 支付宝的签名是根据程序构建的参数进行的,发送时参数由本地程序构建,所以受自己控制,只要参数正确,基本可以成功,去除空参数等操作是支付宝的库函数来完成的。
- 接收回传的参数时,对于同步返回的参数,$_GET数组的内容非常重要,因为验证回传参数时(调用verfyReturn()函数),首先将$_GET的内容全部传给getMysign()方法,支付宝会get给我们一个sign,只有根据回传参数产生出相同的sign,才可以验证通过。这就是我们不能使用自定义参数的原因,如果增加了自定义参数,getMysign()就会带有杂质,跟支付宝生成的sign过程不同了,自然结果就要不对。
如何让自定义参数跟支付宝兼容
这个自定义参数问题只存在于return_url中,因为同步回传使用GET方式,异步通知使用POST方式,POST不受url中的自定义参数影响。
假设你的自定义参数是custom_val
在调用支付宝的验证参数前,将这个自定义参数从$_GET数组中去掉,支付宝验证结束后再恢复就可以了。
1
2
3
4
5
6
7
8
|
$get_temp = $_GET;
unset($_GET[
'custom_val'
]);
//调用支付宝验证签名接口
$alipayNotify =
new
AlipayNotify($aliapy_config);
$verify_result = $alipayNotify->verifyNotify();
//恢复$_GET数组
$_GET = $get_temp;
unset($get_temp);
|
为什么notify_url地址不能带有自定义参数
在指定notify_url时,合法的方式如
http://www.solagirl.net/notify_url.php
不合法的方式如
http://www.solagirl.net/notify_url.php?order_id=10
与其说第二种格式不合法,不如说它是无效的。为什么这样说?因为不论你在这个地址后面带上多少自定义的参数,支付宝通过异步方式向你POST数据时,都会把?后面的参数去掉,所以带了也是白带,起不了任何作用,更不可能影响签名判断。这点似乎不如paypal方便,要区分执行过程只能靠POST过来的数据。
因此支付宝的notify_url不是不可以带有自定义参数,而是带了也没用,自作多情而已。