PayPal IPN更新以容纳HTTP1.1 - 由对等方重置连接

时间:2013-01-24 17:05:42

标签: ssl paypal paypal-ipn fsockopen

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

  

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

我们做到了,IPN失败了。 PayPal Merchant Technical Service要求我们转移到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的值。如果我用旧脚本执行此操作,它会返回“VERIFIED”值,就好了。多个站点上的旧脚本已稳定多年。我现在已经在两个不同服务器上的两个不同站点上尝试了这些新更新,结果是相同的。

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

PHP警告:fgets()[function.fgets]:SSL:第145行/ home / [sitename] / public_html / [IPN scriptname] .php中的对等重置连接

所以看来PayPal关闭连接而不发送验证响应。但是PayPal支持否认这是一个问题,因为连接首先打开。

我花了几天时间试图解决这个问题。我仍然有2月1日的截止日期,之后PayPal说我的旧脚本可能不起作用,但新的脚本也不行。

其他人对此有什么经验吗?

4 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

经过几个小时的斗争这个问题。我做对了。

首先,标题应该是正确的。

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

第二,缓冲区应足够大,以便从Paypal接收整个消息。原始示例代码使用1024,但结果被截断。

$res = stream_get_contents($fp, 2048);

在两者之后,结果返回良好。

答案 2 :(得分:1)

终于找到了答案! Paypal IPN Script, issue with feof and fgets

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

答案 3 :(得分:0)

  

更新:2016年1月7日   我注意到以下内容非常适用于tls://www.sandbox.paypal.com 当你上线tls://www.paypal.com连接被拒绝! (它不起作用)我发现问题出在标题,沙箱和生产级别,直播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";
  

Sandbox 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 验证,接收标题,逐行处理标题以查找验证验证。

应该由工作条件中的PayPal一体化软件包提供,但是他们的教程缺少非常关键的部分,并且它对我和许多人都没有在这个线程上工作。

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

$ res = fgets($ fp,1024),PayPal是错误的;还有...

但是你不需要$ 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) {}

现在,您可以在每行中查找验证

  

if(strcmp($ response,&#34; VERIFIED&#34;)== 0){}

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

  

//记录交易:       file_put_contents(&#34; paypal_post.txt&#34;,date(&#39; Ymd H:i:s&#39;)。&#34; \ n&#34; .file_get_contents(&#34; php://输入&#34;)。&#34; \ n \ n&#34;,FILE_APPEND);

那就是它。 现在去这里,用你的paypal登录,看它是否有效: https://developer.paypal.com/developer/ipnSimulator/

让您的网址选择&#34;购物车结帐&#34;例如,发送测试IPN。 如果它运作良好,请继续将上述paypal URL中的.sandbox从www.sandbox.paypal.com移至www.paypal.com,您就可以了。

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