2

我知道有人问过这个问题,但没有一个解决方案对我有用。我正在使用来自 paypal ( https://developer.paypal.com/webapps/developer/applications/ipn_simulator ) 的即时付款通知 (IPN) 模拟器,但我总是收到: 来自 PayPal 的意外响应:HTTP/1.1 400 Bad Request

<?php
    //Open a socket for the acknowledgement request
        $fp = fsockopen (ssl://www.sandbox.paypal.com, 443, $errno, $errstr, 30);

        if (!$fp) { 
            // fsockopen error
            throw new Exception("An error occured while using fsockopen(): [$errno] $errstr");
        } 

        //Set up the acknowledgement request headers
        $header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
        $header .= "Host: ssl://www.sandbox.paypal.com\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: ".strlen($data)."\r\n";
        $header .= "Connection: Close\r\n\r\n";

        // Post request back to PayPal for validation
        fputs ($fp, $header . $data);
        while (!feof($fp)) {                     // While not EOF
            $res = fgets ($fp, 1024);              // Get the acknowledgement response
            if (strcmp ($res, "VERIFIED") == 0)  {  // Response is VERIFIED
                $response = 'verified';
            }
            else if (strcmp ($res, "INVALID") == 0)  { // Response is INVALID
                $response = 'invalid';
            } 
            else {
                throw new Exception("Unexpected response from PayPal: $res");
            }
        }

编辑:更改后

$header .= "Host: ssl://www.sandbox.paypal.com\r\n";

$header .= "Host: www.sandbox.paypal.com\r\n";

我现在收到HTTP/1.1 200 OK响应。另一个问题是,我的 if() 部分不起作用,它总是跳到最后

 else {
                throw new Exception("Unexpected response from PayPal: $res");
  }

有什么问题if (strcmp ($res, "VERIFIED") == 0)吗?

更新 2: 这是我的完整代码。也许有人可以找到错误:

class Paypal {
    protected $sandbox = false;
    protected $data = null;

    const SANDBOX_URL = 'www.sandbox.paypal.com';
    const PAYPAL_URL = 'www.paypal.com';


    public function __construct($sandbox = false) { 
        $this->sandbox = $sandbox;
    }

    public function receiveData() {
        if (empty($_POST)) {
            throw new Exception('No $_POST data found');
        }
        $this->data = $_POST;
        // Read the notification from PayPal and create the acknowledgement response
        $req = 'cmd=_notify-validate';               // add 'cmd' to beginning of the acknowledgement you send back to PayPal

        foreach ($_POST as $key => $value) {         // Loop through the notification NV pairs
            $value = urlencode(stripslashes($value));  // Encode the values
            $req .= "&$key=$value";                    // Add the NV pairs to the acknowledgement
        }

        if ($this->fsock($req))
            return true;
        else
            return false;
    }

    public function getData() {
        return $this->data;
    }

    private function fsock($data) {
        //Open a socket for the acknowledgement request
        $fp = fsockopen ('ssl://'.$this->getURL(), 443, $errno, $errstr, 30);

        if (!$fp) { 
            // fsockopen error
            throw new Exception("An error occured while using fsockopen(): [$errno] $errstr");
        } 

        //Set up the acknowledgement request headers
        $header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
        $header .= "Host: ".$this->getURL()."\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: ".strlen($data)."\r\n";
        $header .= "Connection: Close\r\n\r\n";

        // Post request back to PayPal for validation
        fputs ($fp, $header . $data);
        while (!feof($fp)) {                     // While not EOF
            $res = fgets ($fp, 1024);              // Get the acknowledgement response
            if (strcmp ($res, "VERIFIED") == 0)  {  // Response is VERIFIED
                $response = 'verified';
                // Notification protocol is complete, OK to process notification contents

                // Possible processing steps for a payment might include the following:

                // Check that the payment_status is Completed
                // Check that receiver_email is your Primary PayPal email
                // Check that payment_amount/payment_currency are correct
                // Process payment
            }
            else if (strcmp ($res, "INVALID") == 0)  { // Response is INVALID
                $response = 'invalid';
            } 
            else {
                throw new Exception("Unexpected response from PayPal: $res");
            }
        }
        fclose ($fp);  //close file pointer 
        if ($response == 'verified')
            return true;
        else
            return false;
    }

    private function getURL() {
        if ($this->sandbox) 
            return self::SANDBOX_URL;
        else                
            return self::PAYPAL_URL;
    }   
}

$paypal = new Paypal(true);
try {
    if ($paypal->receiveData()) {
        // success
    }
    else {
        // fail
    }
}
catch (Exception $e) {
    // exception
    echo 'Exception: ',  $e->getMessage(), "\n";
}
4

5 回答 5

4

您的脚本似乎存在许多问题。例如, $data 未初始化,因此您没有发布任何内容。但是您收到 400 错误的原因是因为 $header .= "Host: ssl://www.sandbox.paypal.com\r\n";

应该

$header .= "主机:www.sandbox.paypal.com\r\n";

于 2013-05-05T05:31:50.400 回答
2

我维护了一些当前的 IPN 程序,在我看来,您读取数据的方法不太可能获得 PayPal 的完整响应。TCP 数据以数据包的形式发送,而 PayPal 响应不作为单个缓冲区发送,因此实际上单个读取实际上不太可能包含尽可能多的字节,最多可达请求的 1,024 个字节。

为了在您自己的代码中实现,您必须循环并将您读取的内容附加到变量中,直到到达 EOF。

此外,您可以在此处查看此 PHP 示例代码:

https://www.x.com/developers/PayPal/documentation-tools/code-sample/216623

我想我现在可以看到你的错误了。您的 fgets 语句一次只读取一行响应,因为它在达到长度或达到换行符时停止读取,以先到者为准。您要查找的行位于 Pay Pal 响应的正文中,而不是前几行(标题等)。然而,您的 else 语句包含在循环中。因此,一旦从 PayPal 收到标题的第一行,您的 else 语句就会始终执行。

我建议进行两项更改。在循环之后移动失败代码,因为在到达 EOF 之前,实际上并不能确定失败。

我建议的另一个更改是使用 strpos 而不是 strcmp。尽管 paypal 响应正文仅包含已验证的单个单词,但很有可能 CR 或 LF 或两者都将成为 fgets 读取的那一行的一部分。所以 strcmp 会失败。您可以使用条件strpos ($res, "VERIFIED") !== false代替现有条件(以及检查是否收到 INVALID 的相同更改。

这是一些可能有效的代码(我无法在您的情况下测试此代码,未经测试的代码永远无法确定有效)。请将此代码视为建议,而不是字面答案:

   // Post request back to PayPal for validation
    fputs ($fp, $header . $data);
    while (!feof($fp)) {                     // While not EOF
        $res = fgets ($fp, 1024);              // Get the acknowledgement response
        if (strpos ($res, "VERIFIED") !== false)  {  // Response is VERIFIED
            $response = 'verified';
            // Notification protocol is complete, OK to process notification contents

            // Possible processing steps for a payment might include the following:

            // Check that the payment_status is Completed
            // Check that receiver_email is your Primary PayPal email
            // Check that payment_amount/payment_currency are correct
            // Process payment
            break;
        }
        else if (strpos ($res, "INVALID") !== false)  { // Response is INVALID
            $response = 'invalid';
            break;
        } 
    }
    fclose ($fp);  //close file pointer 
    if ($response == 'verified')
        return true;
    else if ($response == 'invalid') {
            return false;
        }
    else { // no else statement is actually needed here,
           // since the previous conditions both return
        throw new Exception("Unexpected response from PayPal: $res");
        return false;
    }
于 2013-05-14T04:15:10.417 回答
1

您的 If 语句是正确的,您需要做的就是打印您正在检查的值“VERIFIED”以获取标题/尾随字符:将类似这样的内容复制到您的 else 块中:

    var_dump($res) ; die;

所以你可以看到你得到了什么:)

于 2013-05-13T18:26:45.197 回答
0

套接字级别的 HTTP 通信使您的代码非常容易出错。我建议你使用像cURL这样的 HTTP 客户端库,它会为你处理请求/响应周期,跟踪潜在的重定向和读/写 headers/trailers。

于 2013-05-15T09:51:31.480 回答
0

请清理您的浏览器缓存或重新启动浏览器。

这肯定会解决您的错误请求问题。

于 2013-05-15T07:02:29.787 回答