为什么 tlstest.paypal.com 可以在浏览器中使用,但不能在我的 PHP 代码中使用(对 Paypal IPN 有用)?
Why does tlstest.paypal.com work from browser but not from my PHP code (useful for Paypal IPN)?
2018 年 6 月 30 日之后,Paypal won't accept non-TLS 1.2 + HTTP 1.1 requests 不再。
他们创建了 URL https://tlstest.paypal.com/ 来测试连接是否正常。如果我们在浏览器中打开这个URL,我们会得到一个成功的:
PayPal_Connection_OK
问题:为什么从PHP使用以下代码连接时会失败?(我一点反应都没有,浏览器还在等待"state" like this, 所以它甚至没有到达 echo $errno; echo $errstr;
)
<?php
$req = ''; // usually I use $req = 'cmd=_notify-validate'; for IPN
$header .= "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 1024);
echo $res;
}
fclose($fp);
}
?>
注:
这不是 的副本,后者已过时。
我有 PHP 5.6.33-0+deb8u1 (cli)(内置:2018 年 1 月 5 日 15:46:26)和 openssl version text: OpenSSL 1.0.1t 3 May 2016
它通过将 tls://
更改为 ssl://
对我有效,这对我来说完全没有意义,但这也是为什么使用 fsockopen
是一个太低级别的库与它进行 HTTP 交换(你应该使用适当的 HTTP 客户端库),同时在 TLS 方面配置不够。
有
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
我得到:
HTTP/1.1 426 Unknown
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 267
Expires: Fri, 22 Jun 2018 19:49:46 GMT
Date: Fri, 22 Jun 2018 19:49:46 GMT
Connection: keep-alive
Upgrade: TLS/1.2
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://tlstest.paypal.com/" on this server.<P>
Reference #18.8024a17.1529696986.1fc51318
</BODY>
</HTML>
但 $fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 30);
我得到:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:05:35 GMT
Connection: keep-alive
然后就挂了,可能是因为是keep-alive连接,buffer小于1024导致你获取不到下面的body内容。
这可能是 "PayPal_Connection_OK",因为它与 Content-Length
中显示的长度完全匹配。
这再次表明您应该使用 HTTP 客户端库,而不是尝试(糟糕地)在 fsockopen
之上重新实现 HTTP。
为了完整起见,这里有一个工作代码(完全归功于 PatrickMevzek 的回答):
<?php
$req = '';
$header = "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 3);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 21); // 21 because length of "PayPal_Connection_OK"
echo $res;
}
fclose($fp);
}
?>
服务器回复如下:
# php -f test.php
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:19:56 GMT
Connection: keep-alive
PayPal_Connection_OK
2018 年 6 月 30 日之后,Paypal won't accept non-TLS 1.2 + HTTP 1.1 requests 不再。
他们创建了 URL https://tlstest.paypal.com/ 来测试连接是否正常。如果我们在浏览器中打开这个URL,我们会得到一个成功的:
PayPal_Connection_OK
问题:为什么从PHP使用以下代码连接时会失败?(我一点反应都没有,浏览器还在等待"state" like this, 所以它甚至没有到达 echo $errno; echo $errstr;
)
<?php
$req = ''; // usually I use $req = 'cmd=_notify-validate'; for IPN
$header .= "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 1024);
echo $res;
}
fclose($fp);
}
?>
注:
这不是 的副本,后者已过时。
我有 PHP 5.6.33-0+deb8u1 (cli)(内置:2018 年 1 月 5 日 15:46:26)和
openssl version text: OpenSSL 1.0.1t 3 May 2016
它通过将 tls://
更改为 ssl://
对我有效,这对我来说完全没有意义,但这也是为什么使用 fsockopen
是一个太低级别的库与它进行 HTTP 交换(你应该使用适当的 HTTP 客户端库),同时在 TLS 方面配置不够。
有
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
我得到:
HTTP/1.1 426 Unknown
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 267
Expires: Fri, 22 Jun 2018 19:49:46 GMT
Date: Fri, 22 Jun 2018 19:49:46 GMT
Connection: keep-alive
Upgrade: TLS/1.2
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://tlstest.paypal.com/" on this server.<P>
Reference #18.8024a17.1529696986.1fc51318
</BODY>
</HTML>
但 $fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 30);
我得到:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:05:35 GMT
Connection: keep-alive
然后就挂了,可能是因为是keep-alive连接,buffer小于1024导致你获取不到下面的body内容。
这可能是 "PayPal_Connection_OK",因为它与 Content-Length
中显示的长度完全匹配。
这再次表明您应该使用 HTTP 客户端库,而不是尝试(糟糕地)在 fsockopen
之上重新实现 HTTP。
为了完整起见,这里有一个工作代码(完全归功于 PatrickMevzek 的回答):
<?php
$req = '';
$header = "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 3);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 21); // 21 because length of "PayPal_Connection_OK"
echo $res;
}
fclose($fp);
}
?>
服务器回复如下:
# php -f test.php
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:19:56 GMT
Connection: keep-alive
PayPal_Connection_OK