代码之家  ›  专栏  ›  技术社区  ›  Community wiki

Paypal SandBox IPN总是返回INVALID

  •  11
  • Community wiki  · 技术社区  · 2 年前

    正如下面回答中的一条评论中所提到的,我试着如下 this tutorial 。所以现在我有以下内容:


    ipn.php文件:

    <?php
    
        $ipn_post_data = $_POST;
    
        $url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
    
        // Set up request to PayPal
        $request = curl_init();
        curl_setopt_array($request, array
        (
            CURLOPT_URL => $url,
            CURLOPT_POST => TRUE,
            CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
            CURLOPT_RETURNTRANSFER => TRUE,
            CURLOPT_HEADER => FALSE,
            CURLOPT_SSL_VERIFYPEER => TRUE,
            CURLOPT_CAINFO => 'cacert.pem',
        ));
    
        // Execute request and get response and status code
        $response = curl_exec($request);
        $status   = curl_getinfo($request, CURLINFO_HTTP_CODE);
    
        // Close connection
        curl_close($request);
    
        if($status == 200 && $response == 'VERIFIED')
        {
            $subject = "valid";
            $message = "good";
        }
        else
        {
            $subject = "invalid";
            $message = "bad";
        }
    
        $to = "oshirowanen@mail.com";
        $from = "me@desktop.com";
    
        $header  = 'MIME-Version: 1.0' . "\r\n";
        $header .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
        $header .= 'To: Oshirowanen <oshirowanen@mail.com>' . "\r\n";
        $header .= 'From: Me <me@desktop.com>' . "\r\n";
    
        mail($to,$subject,$message,$header);
    
    ?>
    

    收到的电子邮件:

    Subject "invalid"
    Message "bad"
    
    9 回复  |  直到 12 年前
        1
  •  14
  •   Doug    9 年前

    编辑:

    现在我可以看到您输出的数组了,请尝试替换它以消除PHP数组错误:

    foreach ($_POST as $key => $value) {
        if (!is_array($value)) {
            $value = urlencode(stripslashes($value));
            $req .= "&$key=$value";
        }
        else if (is_array($value)) {
            $paymentArray = explode(' ', $value[0]);
            $paymentCurrency = urlencode(stripslashes($paymentArray[0]));
            $paymentGross = urlencode(stripslashes($paymentArray[1]));
            $req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
        }
    }
    

    以下是经过编辑的完整代码:

    // read the post from PayPal system and add 'cmd'
    $req = 'cmd=' . urlencode('_notify-validate');
    
    foreach ($_POST as $key => $value) {
        if (!is_array($value)) {
            $value = urlencode(stripslashes($value));
            $req .= "&$key=$value";
        }
        else if (is_array($value)) {
            $paymentArray = explode(' ', $value[0]);
            $paymentCurrency = urlencode(stripslashes($paymentArray[0]);
            $paymentGross = urlencode(stripslashes($paymentArray[1]);
            $req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
        }
    }
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Host: www.paypal.com'));
    $res = curl_exec($ch);
    curl_close($ch);
    
    
    // assign posted variables to local variables
    $item_name = $_POST['item_name'];
    $item_number = $_POST['item_number'];
    $payment_status = $_POST['payment_status'];
    $payment_amount = $_POST['mc_gross'];
    $payment_currency = $_POST['mc_currency'];
    $txn_id = $_POST['txn_id'];
    $receiver_email = $_POST['receiver_email'];
    $payer_email = $_POST['payer_email'];
    
    
    if (strcmp ($res, "VERIFIED") == 0) {
        // check the payment_status is Completed
        // check that txn_id has not been previously processed
        // 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) {
        // log for manual investigation
    }
    

    Check this out !

    编辑:查看PayPal疑难解答提示:

    https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNTesting

        2
  •  6
  •   Community CDub    8 年前

    问题是你没有检查HTTP响应代码,所以你将“无效主机头”定义为PayPal响应,而它是web服务器响应(状态代码为400)。
    如果你看看 PayPal documentation ,有一个PHP示例与您的代码非常相似,因为它使用“fsockopen”、“fputs”和“fgets”函数与PayPal服务器通信。
    但如果你仔细看看“fsockopen”通话后的备注,你可以读到:

    // Process validation from PayPal 
    // TODO: This sample does not test the HTTP response code. All 
    // HTTP response codes must be handled or you should use an HTTP 
    // library, such as cUrl
    

    这正是您的问题:在解析响应主体之前,您没有检查HTTP响应代码是否为200(OK)。
    此外,使用“strtolower”函数是不正确的,因为来自PayPal服务器的实际响应总是大写的,如上面引用的示例所示。
    即使PayPal示例使用“fsockopen”方法,我认为使用 PHP cURL 库来实现您的IPN侦听器。
    还可以看看以下答案:

    但是,如果您真的想使用“fsockopen”函数,您应该始终指定 "Host" header field 在POST请求中,如以下代码片段所示(取自 PHP manual )以下为:

    <?php
    $fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
    if (!$fp) {
        echo "$errstr ($errno)<br />\n";
    } else {
        $out = "GET / HTTP/1.1\r\n";
        $out .= "Host: www.example.com\r\n";
        $out .= "Connection: Close\r\n\r\n";
        fwrite($fp, $out);
        while (!feof($fp)) {
            echo fgets($fp, 128);
        }
        fclose($fp);
    }
    ?>
    

    更新

    这里有一个简单的递归条带斜杠/urlencoding函数:

    <html>
    <body>
    <pre>
    <?
    
    $post = Array (
      "transaction" => Array("USD 20.00"),
      "payment_request_date" => "Sun Aug '05 08:49:20 PDT 2012",
      "return_url" => "http://000.000.000.000/success.php"
    );
    
    echo "before myUrlencode...\n";
    print_r($post);
    
    function myUrlencode($post) {
      foreach ($post as $key => $val) {
        if (is_array($val)) {
          $post[$key] = myUrlencode($val);
        } else {
          $post[$key] = urlencode(stripslashes($val));
        }
      }
      return($post);
    }
    
    echo "\nafter myUrlencode...\n";
    print_r(myUrlencode($post));
    
    ?>
    </pre>
    </body>
    </html>
    
        3
  •  2
  •   oshirowanen    12 年前
    1. 使用 basic sample code 4b中,

    2. 已清除 $ipnNotificationUrl = ""; 从基本的示例代码中,因为我在其中添加了一个值,

    3. 在沙盒中创建了一个卖家帐户,而不是商业专业帐户,

    4. 设置卖家帐户以启用ipn url,

    5. 使用了以下PHP 5.2 sample code 用于ipn侦听器

    6. 将2行添加到侦听器中,如所述 here ,这两条线如下所示:

    7. 已下载 cacert.pem 到我的服务器的证书来自 here 并将其放在与ipn侦听器相同的目录中:

    第6点中提到的两条线:

    CURLOPT_SSL_VERIFYPEER => TRUE,
    CURLOPT_CAINFO => 'cacert.pem',
    

    我不知道为什么沙盒商业专业账户不允许我设置ipn url,但卖家账户允许。

        5
  •  1
  •   Rajcho    12 年前

    我不确定你的代码现在到底出了什么问题,但不久前我还在努力,我的修复方法是在标题中添加HOST,主机必须是www.paypal.com。我使用了fsockopen方法,现在工作得很好。

    在Curl中,我以前遇到过ssl问题。解决方案是把这些线:

    curl_setopt($curl, CURLOPT_COOKIEJAR, dirname(__FILE__) . "/cookies.txt");
    curl_setopt($curl, CURLOPT_COOKIEFILE, dirname(__FILE__) . "/cookies.txt");
    

    其中当然必须存在文件cookies.txt。 更重要的是,我必须运行一个到页面的连接来获取会话数据,然后再发送帖子数据。

    下面是一个使用fsockopen方法对我来说很好的标题

    $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
    $header .= "Host: www.paypal.com\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    
        6
  •  1
  •   Izudin Avdibasic    9 年前

    这是+字符的一个问题,它经常被错误地提取,所以我做了这个变通方法,它对我很有效。

    payment_data=2016年6月4日星期六15:11:16 GMT+0200(CEST)

    foreach ($_POST as $key => $value) {
    if($key !== "payment_date"){
        $req .= '&' . $key . '=' . rawurlencode(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
    }else{
        $req .= '&' . $key . '=' . rawurlencode(str_replace(array('GMT '),array('GMT+'),$value));
    }}
    
        7
  •  0
  •   Martin    12 年前

    以下是如何避免这些错误。。。

    foreach ($_POST as $key => $value) {
         if ($key=='transaction')
              foreach ($value as $key2=>$value2) {
                   $value['transaction'][$key2] = urlencode(stripslashes($value2));
         }
         else {
              $value = urlencode(stripslashes($value));
         }
         $req .= "&$key=$value";
     }
    
        8
  •  0
  •   ross.bendigo    9 年前

    几个小时的拉扯直到我看到伊祖丁的回答。他是对的。。日期中的+未被转移。只是为了测试,我把它从模拟器中预先填充的字段中删除,得到了一个 Verified 最后

        9
  •  0
  •   Draeko    8 年前

    我终于找到了这个问题的最新答案(2016年8月5日)。您可以使用此代码作为Sandbox或Live的最终IPN。考虑如下:

    1. 请确保将您的IPN侦听器置于->我的销售工具->即时付款通知科。
    2. 不要在沙盒中使用IPN模拟器,它将始终返回INVALID。
    3. 创建并使用一个实际的沙盒按钮,但不要把你的IPN监听器放在RETURN PAGE上,上面写着“当客户完成结账时,把他们带到这个URL”。

    仅此而已。我希望这会有所帮助。

    下面是工作代码:

    <?php
    $post_data = file_get_contents('php://input');
    $post_array = explode('&', $post_data);
    $dataFromPayPal = array();
    foreach ($post_array as $keyval) {
        $keyval = explode ('=', $keyval);
        if (count($keyval) == 2)
            $dataFromPayPal[$keyval[0]] = urldecode($keyval[1]);
    }
    
    $req = 'cmd=_notify-validate';
    if(function_exists('get_magic_quotes_gpc')) {
        $get_magic_quotes_exists = true;
    }
    foreach ($dataFromPayPal as $key => $value) {
        if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
            $value = urlencode(stripslashes($value));
        } else {
            $value = urlencode($value);
        }
        $req .= "&$key=$value";
    }
    
    $ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
    //use https://www.sandbox.paypal.com/cgi-bin/webscr in case you are testing this on a PayPal Sanbox environment
    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_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
    
    if( !($res = curl_exec($ch)) ) {
        curl_close($ch);
        exit;
    }
    curl_close($ch);
    
    
    
    if (strcmp ($res, "INVALID") == 0) {
            echo "INVALID";
    }
    else if (strcmp ($res, "VERIFIED") == 0) {
            echo "VALID";
    }
    
    ?>