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 "<pre>".print_r($_POST,true)."</pre>\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?