为什么PayPal会定期使IPN无效或无效?

时间:2019-07-11 16:45:02

标签: php paypal paypal-ipn verification

当使用cmd = _notify-validate验证IPN时,我偶尔会收到一个既不是'VERIFIED'也不是'INVALID'的隐式响应代码,而是二进制的并且难以理解。但是,此行为无法预测地发生,并且稍后完全相同的对notify-validate的调用将产生有效的响应。这种情况已经间歇性地发生了多年,但我始终无法弄清楚问题出在哪里。是PayPal的问题,代码中的错误还是我没有考虑的其他问题?

由于这种情况是间歇性发生的,并且相同的请求稍后会成功执行,因此,我通过排队并重新验证所有未返回PayPal明确响应的交易来解决此问题。这样做可以解决,但并不是最佳的工作流程,尤其是随着事务数量的增加。

public static function Verify() {
    $start = "url=members/ipn&";
    $errno = $errstr = null;

    $req = 'cmd=_notify-validate';
    foreach($_REQUEST as $key => $value) {
        if(!is_array($value)) {
            $value = urlencode(stripslashes($value));
            $req .= "&$key=$value";
        }
    }
    $verified = false;
    $fp = fsockopen("ssl://www.paypal.com", 443, $errno, $errstr, 30);
    if(!$fp) {
        Paypal::Log("ERROR", "Failed connecting to PayPal:$errstr ($errno)\n");
    }
    else {                                                                        
        if($errno || $errstr) {
            Paypal::Log("ERROR", $errno.":".$errstr);
        }
        $header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
        $header .= "Host:".$host."\r\n";
        $header .= "Connection: clost\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: ".strlen($req)."\r\n\r\n";
        fputs($fp, $header.$req);

        $header = true;
        while(!feof($fp)) {
            $res = trim(fgets($fp, 4096));
            if(strlen($res) == 0) {
                $header = false;
                continue;
            }
            if($header) {
                continue;
            }
            if(strcmp($res, "VERIFIED") == 0) {
                $verified = true;
                break;
            }
            else {
                $verified = false;
                break;
            }
        }
    }
    fclose($fp);

    return $verified;
}

此代码的大多数响应都以纯文本格式(在$ res var中)返回“ VERIFIED”,但有时会失败并返回看起来是二进制字符的内容。我什至不能将响应粘贴到这里,因为它们都是非文本字符并且难以理解。

要注意的另一件事是fsockopen正在运行,并且不会产生任何错误。只是$ res有时其中包含未知数据。

-更新1-

根据建议,我将验证过程切换为使用cURL,并遵循了PayPal的建议准则。

$ch = curl_init('https://ipnpb.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/cert/cacert.pem");

curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'User-Agent: PHP-IPN-Verification-Script',
    'Connection: Close',
));             

if ( !($res = curl_exec($ch)) ) {
  Paypal::Log("cURL Error:", curl_error($ch));
  curl_close($ch);
  exit;
}
curl_close($ch);

$info = curl_getinfo($ch);
$http_code = $info['http_code'];
if ($http_code != 200) {
    Paypal::Log("HTTP Code:", $http_code);
}

Paypal::Log("Response", $res);
if(strcmp($res, "VERIFIED") == 0) {
    $verified = true;
}
else {
    $verified = false;
}

大多数情况下都可以正常工作,但是间歇性问题仍然存在,偶尔验证尝试会返回不同的响应。

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body></html>

在进行完全相同的调用以验证交易时,它可以工作。所以我只是不明白为什么有时会出现此400 Bad Request错误。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

在您的代码中显示如何获得$ req-400错误听起来像是您在PayPal不希望(或可能不发送)的某个地方抓了一些垃圾。

几年前(几年),我选择了这个,但它从未失败过。...-也许提到的“问题”就是您所看到的。

// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$post = array();
foreach ($raw_post_array as $keyval) {
    $keyval = explode('=', $keyval);
    if (count($keyval) == 2)
        $post[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';

foreach ($post as $key => $value) {
    $value = urlencode(addslashes($value));
    $req .= "&$key=$value";
}

希望这会有所帮助!