Paypal SandBox IPN 总是返回 INVALID

Posted

技术标签:

【中文标题】Paypal SandBox IPN 总是返回 INVALID【英文标题】:Paypal SandBox IPN always returns INVALID 【发布时间】:2012-07-29 14:31:32 【问题描述】:

正如以下答案中的一个 cmets 所述,我尝试关注 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"

【问题讨论】:

更新的问题显示通过 $_POST 接收的值,其中包含@Enzino 所期望的数组中的数组。 我仍然无法让它工作。因此,我会将赏金奖励给包含 ipn.php 文件的工作示例的答案,该文件通过电子邮件给我一个valid 和沙盒帐户。 根据教程geekality.net/2011/05/28/… 更新了问题,该教程在下面的评论中提到了答案。 【参考方案1】:

我终于找到了对此查询的更新(2016 年 8 月 5 日)有效答案。您可以将此代码用作 Sandbox 或 Live 的最终 IPN。考虑到以下几点:

    请务必将您的 IPN 监听器放置到 ->我的销售工具 -> 即时付款通知部分。 不要在沙盒中使用 IPN 模拟器,它总是会返回 INVALID。 创建并使用一个实际的沙盒按钮,但不要将您的 IPN 侦听器置于显示“在客户完成结帐时将其带到此 URL”的 RETURN PAGE。

就是这样。我希望这会有所帮助。

这是工作代码:

<?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";


?>

【讨论】:

【参考方案2】:

在我看到伊祖丁的回答之前,我拉了几个小时的头发。他是对的..日期中的+没有被转移。只是为了测试,我把它从模拟器的预填充字段中删除,最后得到了Verified

【讨论】:

【参考方案3】:

这是 + 字符的问题,它经常被错误地提取,所以我做了这个解决方法,它对我有用。

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));

【讨论】:

【参考方案4】:

编辑:

现在我可以看到您输出的数组,尝试替换它以消除 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

【讨论】:

完成并更新了原始问题中的代码。知道为什么我一直无效,如原始问题中所述,您可以在其中看到我收到的电子邮件详细信息,即使付款清楚地从买方转移到卖方? @oshirowanen 似乎 PayPal 沙盒非常脆弱,我不断看到有同样问题的人。要尝试的一件事是打印 $req 值并查看是否已添加所有 POST。阅读:(cms.paypal.com/us/cgi-bin/… 我再次尝试了您的代码并仔细查看了。 strtolower($res); 包含值 invalid host header。这解释了为什么 if (strcmp ($res, "VERIFIED") == 0) 对我来说永远不正确。但为什么我会收到invalid host header 消息? 我在原始问题中添加了更多详细信息。 @oshirowanen Enzino 在我回复您之前添加了一些解释。在下面查看他们的答案。我同意 cURL 是更好的解决方案,并且 Enzino 将主机放入标头以确保没有错误是正确的。我正在发布 PayPal 代码示例中的 cURL 解决方案供您查看。【参考方案5】:

我不确定你的代码现在到底有什么问题,但我之前也遇到过同样的问题,我的修复是在标题中添加 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】:

    使用basic sample code 4b 让它工作,

    从基本示例代码中清除了$ipnNotificationUrl = "";,因为我在其中添加了自己的值,

    在沙盒中创建了卖家账户而不是商业专业账户,

    设置卖家账号启用ipn url,

    使用以下 PHP 5.2 sample code 作为 ipn 监听器

    将2行添加到监听器中,如here所述,2行如下:

    cacert.pem证书从here下载到我的服务器,并放在与ipn监听器相同的目录下:

第 6 点中提到的 2 行:

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

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

【讨论】:

我可以得到你的完整代码吗?我也一直在创建 paypal ipn @IvorySantos,我想我已经通过链接在我的答案中包含了所有代码。如果我遗漏了什么,请告诉我。 使用 curl 的 PHP 示例代码(上面的第 5 步)效果很好!谢谢你的提示!点赞!【参考方案7】:

以下是避免这些错误的方法...

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";
 

【讨论】:

好的,从数组中获取数组,但我仍然从$res 得到无效。 我仍然无法让它工作。因此,我会将赏金奖励给包含 ipn.php 文件的工作示例的答案,该文件通过电子邮件与沙盒帐户为我提供valid【参考方案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 侦听器应该会更好。 还请查看以下答案:

PHP cURL PayPal Sandbox cURL or fsockopen for paypal ipn

但是,如果你真的想使用“fsockopen”功能,你应该总是在POST请求中指定"Host" header field,如下面的sn-p代码所示(取自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);

?>

更新

这是一个简单的递归stripslashes/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>

【讨论】:

我最近一次尝试基于 cURL 方法再次更新了问题。 您应该在foreach 语句之前添加行echo "&lt;pre&gt;".print_r($_POST,true)."&lt;/pre&gt;\n";,以便您可以看到$_POST 数组的内容。我怀疑 $_POST 数组的值之一是数组本身,因此 stripslashes() 函数失败。 正如我在之前的评论中所说,您还应该检查 http 状态代码,因为如果 PayPal 服务器不会使用 200(OK)代码回复您的请求, $res 变量可以为空(或 FALSE),并且您不会收到任何邮件,因为这两个 if (strcmp($res, ... 都不会返回零值。 更新的问题显示通过 $_POST 接收的值,其中包含您预期的数组中的数组。 stripslashes() 不是递归的。如果要将此函数应用于多维数组,则需要使用递归函数。在stripslashes() 函数的PHP manual 上,您可以找到如何执行此操作的示例(搜索“stripslashes_deep”函数)。请注意,您将在“urlencode”功能上遇到同样的问题。另请参阅 this link 以获取数组的 urlencoding 示例(搜索“urlencode_array”)。【参考方案9】:

这些链接可能会解决您的问题,

Paypal: Invalid IPN problem

http://www.webmasterworld.com/ecommerce/4292847.htm

Paypal sandbox IPN return INVALID

【讨论】:

以上是关于Paypal SandBox IPN 总是返回 INVALID的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 sandbox.paypal 获取 IPN 进行支付

即使我按照 PayPal 示例代码操作,PayPal IPN 总是返回 INVALID?

Paypal Sandbox 突然停止发送 IPN

Paypal IPN 总是返回 INVALID

Rails 监听器 Paypal IPN 总是返回 INVALID

PayPal IPN _notify-validate 在沙盒中引发错误 302