3

我们一直在努力根据 seom 最近的变化更新我们的 PayPal IPN 脚本。以下是 PayPal 的部分说明...

您需要在 2013 年 2 月 1 日之前采取的行动 您需要更新您的 IPN 和/或 PDT 脚本以使用 HTTP 1.1,并在 IPN 和 PDT 中包含“主机:www.paypal.com”和“连接:关闭”HTTP 标头脚本。

我们这样做了,但 IPN 失败了。PayPal 商家技术服务要求我们转移到 SLL 连接。以前我们与 PayPal 的连接是基于...

fsockopen ('www.paypal.com', 80, $errno, $errstr, 30)

现在它是 ...

fsockopen ('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30)

我们必须克服主机配置的 SSL 问题才能使其正常工作,但它现在可以建立连接。然而,IPN 要求我们发回 PayPal 以接收“已验证”通知,此时脚本可以根据确认的付款进行本地处理。

这就是失败的地方。

示例 PayPal 脚本的行是:

if (!$fp) {
    // HTTP ERROR

} else {
    fputs ($fp, $header . $req);
    while (!feof($fp)) {
        $res = fgets ($fp, 1024);

        if (strcmp (trim($res), "VERIFIED") == 0) {  ... rest of script

但是回复是空白的。我插入了一个诊断程序,通过电子邮件向我发送每个阶段 $res 的值。如果我使用旧脚本执行此操作,它会返回一个“已验证”值就好了。几个网站上的旧脚本多年来一直很稳定。我现在已经在两个不同服务器上的两个不同站点上尝试了这些新更新,结果是相同的。

在查看 Apache 日志时,我看到了这个错误:

PHP 警告:fgets() [function.fgets]: SSL: Connection reset by peer in /home/[sitename]/public_html/[IPN scriptname].php on line 145

因此,PayPal 似乎在未发送已验证响应的情况下关闭了连接。然而,贝宝支持否认这是一个问题,因为连接首先是打开的。

我花了几天时间试图解决这个问题。我仍然有 2 月 1 日的最后期限,在那之后 PayPal 说我的旧脚本可能无法正常工作,但新脚本也不能。

其他人有这方面的经验吗?

4

4 回答 4

2

如果其他人有同样的问题,CURL 似乎是 PayPal 推荐的 IPN 选择。

在 Github 上查看代码示例:https ://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.php

于 2014-04-11T01:49:15.940 回答
2

After struggle this issue a few hours. I got it right.

first, the header should be correct.

$header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";

second, the buffer should be big enough to receive whole msg from Paypal. the original sample code use 1024, but the result got truncated.

$res = stream_get_contents($fp, 2048);

After the two, the result return good.

于 2015-02-02T11:03:08.153 回答
1

终于找到答案了! Paypal IPN 脚本,feof 和 fgets 的问题

删除while(!feof($fp) ... 并将其更改为$res = stream_get_contents($fp, 1024);也添加$header .= "Conection: Close"以确保贝宝关闭连接。

于 2014-02-21T23:16:13.713 回答
0

更新:2016 年 1 月 7 日 我注意到以下内容对于 tls://www.sandbox.paypal.com 非常有效,但是当您上线 tls://www.paypal.com 时,连接被拒绝!(它不起作用)我发现问题出在标题中,沙箱和生产级别的 live paypal 需要不同的标题!这是一个错误,但要让它在生产级别和沙箱中工作,请分别使用这些标头:

生产级别(实时 PayPal 标头):

  $header  = "POST /cgi-bin/webscr HTTP/1.1\r\n"; 
  $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";

沙盒 PayPal 标头:

$header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";

* 下面的代码是相同的,它工作正常,只需分别替换标题。* 这是其余的代码(和答案):

我在这里和那里检查了所有答案,以使其在 2016 年 1 月 5 日生效。他们都有优点,但不适用于整体情况。

所以首先,这是实际工作的完整代码,然后我将通过它:

<?php

// =========================================================================
// PayPal Official PHP Codes and Tutorial:
// https://developer.paypal.com/webapps/developer/docs/classic/ipn/gs_IPN/
// =========================================================================

// Send an empty HTTP 200 OK response to acknowledge receipt of the notification 
header('HTTP/1.1 200 OK');

// Assign payment notification values 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'];

// Build the required acknowledgement message out of the notification just received
$req = 'cmd=_notify-validate';               // Add 'cmd=_notify-validate' to beginning of the acknowledgement

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


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

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

// Send the HTTP POST request back to PayPal for validation
fputs($fp, $header . $req);

// Log the transaction:
file_put_contents("paypal_post.txt",  date('Y-m-d H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND);

// While not EOF
while (!feof($fp)) {
    // Get the acknowledgement response
    // $res = fgets($fp, 1024);  
    $res = stream_get_contents($fp, 1024);
    $responses = explode("\r\n", $res);
    foreach ($responses as $response) {
        if (strcmp ($response, "VERIFIED") == 0) {
            // Response contains VERIFIED - process notification

            // Authentication protocol is complete - OK to process notification contents

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

            // Check that 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 ($response, "INVALID") == 0) {
            // Response contains INVALID - reject notification
            // Authentication protocol is complete - begin error handling

        }
    }
}
// Close the file
fclose($fp);
?>

好的,现在您有了用于侦听 PayPal IPN的代码,重新编译并将其发送回 PayPal进行验证,接收回标头,逐行处理标头以查找VERIFIED验证。

这应该由 PayPal 一体化软件包在工作状态下提供,但他们的教程缺少非常关键的部分,它对我和这个线程上的许多人都不起作用。

那么现在使它工作的关键部分是什么:首先,paypal 提供的标题不起作用,我发现 @jp_eagle 的标题工作完美。

贝宝错了 $res = fgets($fp, 1024); 还有……

但是你不需要 $res = stream_get_contents($fp, 2048); 正如@jp_eagle 建议的那样, $res = stream_get_contents($fp, 1024); 很好。

while (!feof($fp)) {} 循环应该留在那里以使其工作!是的,即使从 fgets() 切换到 stream_get_contents() 它也应该留在那里!

@richbai90 删除它的建议是错误的。换个方法试试,不行。。。

获得 VALIDATION 的最关键部分是这个循环:

    $res = stream_get_contents($fp, 1024);
    $responses = explode("\r\n", $res);
    foreach ($responses as $response) {}

现在您可以在此处的每一行中查找 VALIDATION :

if (strcmp ($response, "VERIFIED") == 0) {}

在其他任何事情之前,还记录了初始完整的传入 POST 请求表单 paypal:

// 记录交易:file_put_contents("paypal_post.txt", date('Ymd H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND );

而已。现在去这里用你的贝宝登录看看它是否有效: https ://developer.paypal.com/developer/ipnSimulator/

例如,给您的网址选择“购物车结帐”并发送测试 IPN。如果一切正常,请继续从上面的 paypal URL 中删除 .sandbox,从 www.sandbox.paypal.com 到 www.paypal.com,您就可以上线了。

使用您的数据库操作填写 if-else 语句中的 VERIFIED 和 INVALID 部分,然后就完成了。

于 2016-01-06T01:13:22.530 回答