When validating IPNs using cmd=_notify-validate, I am occasionally receiving a cryptic response code that is neither 'VERIFIED' nor 'INVALID', but instead appears to be binary and unintelligible. However, this behavior occurs unpredictably and the exact same call to notify-validate later will produce a valid response. This has been occurring intermittently for years and I've never been able to figure out where the issue is. Would it be something with PayPal, an error in my code, or something else I'm not considering?
Since this happens intermittently and the exact same request succeeds at a later time, I have worked around this by queuing and re-verifying any transactions that don't return a definitive response from PayPal. This is fine as a workaround but not really the optimal workflow especially as the number of transactions increases.
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;
}
Most of the responses from this code return 'VERIFIED' in plain text (in $res var), but sometimes it fails and returns what appears to be binary characters. I can't even paste the response here because it's all non-text characters and is unintelligible.
Another thing to note is that fsockopen is working and it is not producing any errors. It's just that $res sometimes has unknown data in it.
-- UPDATE 1 --
As suggested, I switched the verification process over to using cURL and followed PayPal's recommended guidelines.
$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;
}
It's mostly working, however the intermittent problem is still occurring where once in a while a verification attempt returns a different response.
<!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>
When the exact same call is made to verify the transaction, it works. So I'm just not understanding why sometimes this 400 Bad Request error occurs. Any ideas?
Bad Request
Your browser sent a request that this server could not understand.
` – Stephen Walker Jul 13 '19 at 21:36