Почему PayPal периодически не может проверить или аннулировать IPN? - PullRequest
0 голосов
/ 11 июля 2019

При проверке IPN с использованием cmd = _notify-validate я иногда получаю зашифрованный код ответа, который не является ни «VERIFIED», ни «INVALID», но вместо этого кажется двоичным и неразборчивым. Однако такое поведение происходит непредсказуемо, и точно такой же вызов для уведомления-проверки позже даст действительный ответ. Это происходило с перерывами в течение многих лет, и я никогда не мог понять, где проблема. Это будет что-то с 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), но иногда происходит сбой и возвращает то, что выглядит как двоичные символы. Я даже не могу вставить здесь ответ, потому что это все нетекстовые символы и неразборчиво.

Еще одна вещь, на которую следует обратить внимание, это то, что 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 голосов
/ 14 июля 2019

покажите в своем коде, как вы получаете $ 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";
}

Надеюсь, это поможет!

...