无法在 Codeigniter 中接收 PayPal IPN 响应

Posted

技术标签:

【中文标题】无法在 Codeigniter 中接收 PayPal IPN 响应【英文标题】:Unable to Receive PayPal IPN Response in Code Igniter 【发布时间】:2019-02-27 21:16:27 【问题描述】:

我已经在这工作了 3 天了,但仍然无法正常工作。

我想要做的是从 IPN 侦听器获取 PayPal 响应,以便我可以相应地修改我的数据库,但无论我做什么,它都不起作用。我已经在我的 PayPal Sandbox 帐户中完成了以下操作:

    启用自动返回

    设置自动返回 URL('paypal/success')

    启用支付数据传输 (PDT) 启用 IPN 消息接收 设置 IPN URL ('paypal/ipn')

重定向到自动返回 URL 工作正常,我在成功页面中收到付款数据,但 IPN 不会因为我以外的原因处理。快速查看我的 PayPal 个人资料中的 IPN 历史记录显示正在发送消息,但我没有收到它们。

这是我当前的 IPN 监听器:Paypal/ipn

public function ipn()  
        //Build the data to post back to Paypal
        $postback = 'cmd=_notify-validate'; 
        // go through each of the posted vars and add them to the postback variable
        foreach ($_POST as $key => $value) 
            $value = urlencode(stripslashes($value));
            $postback .= "&$key=$value";
        

        // build the header string to post back to PayPal system to validate
        $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
        $header .= "Host: www.sandbox.paypal.com\r\n";//or www.sandbox.paypal.com
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";

        // Send to paypal or the sandbox depending on whether you're live or developing
        // comment out one of the following lines
        $fp = fsockopen ('www.sandbox.paypal.com', 443, $errno, $errstr, 30);//open the connection
        //$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
        // or use port 443 for an SSL connection
        //$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);

        if ( ! $fp ) 
            // HTTP ERROR Failed to connect
            $message = 'HTTP ERROR Failed to connect!'; 
            $this->email_me($message);
         else  // if we've connected OK

            fputs ($fp, $header . $postback); //post the data back
            while ( ! feof($fp) ) 
                $response = fgets ($fp, 1024);

                if (strcmp (trim($response), "VERIFIED") == 0)  //It's verified

                    //read the payment details and the account holder
                    $payment_status = $_POST['payment_status'];
                    $receiver_email = urldecode($_POST['receiver_email']);

                    // further checks
                    if( ($payment_status == 'Completed') && ($receiver_email == $this->business_email) ) 

                        $message = 'IPN verified successfully!';
                        $this->email_me($message);

                        // Insert the transaction data in the database
                        $this->product_model->insert_transaction_details($_POST);

                     else 

                        $message = 'Payment could not be verified!';
                        $this->email_me($message);  

                    

                 else 

                    $message = 'IPN invalid!';
                    $this->email_me($message);  

                
            
        
    

有人能指出我正确的方向吗? 另外,我是否可以在 chrome 调试器或我的 PayPal Sandbox 仪表板中检查 IPN 响应(“已验证”或“无效”)?我可以在我的仪表板中看到交货状态,但它没有在任何地方显示“已验证”或“无效”。

【问题讨论】:

您的 IPN 端点是否可以从 Internet 访问?如果它在本地主机上,PayPal 将无法连接到它。 它在实时服务器上。 是否有防火墙?见paypal.com/us/smarthelp/article/… 我不明白你的意思@Mark_1 如果您的服务器位于防火墙后面,您需要添加规则以允许 PayPal 的 IPN 进程连接到您的服务器。如果每个人都可以访问您的服务器,则不需要这样做。 【参考方案1】:

我找到了解决方案!我在一个控制器中编写了 IPN 处理程序,该控制器允许访问以管理员身份登录的用户。显然,IPN 方法拒绝访问 PayPal 来验证交易。我想通了这一点,并在不同的控制器中编写了 IPN 方法,一切都运行良好。

我还将我的 IPN 处理程序更改为此代码(尽管原来的可能仍然有效......我没有尝试过):

class Paypal_ipn extends MY_Controller 
    public function __construct() 
        parent::__construct();
        $this->load->model('product_model');
        $this->sandbox = $this->config->item('sandbox'); 
        $this->paypal_host = $this->config->item('paypal_host'); 
        $this->paypal_url = $this->config->item('paypal_url'); 
        $this->business_email = $this->config->item('business');
    


    public function ipn() 
        // STEP 1: Read POST data

        // reading posted data from directly from $_POST causes serialization 
        // issues with array data in POST
        // reading raw POST data from input stream instead. 
        $raw_post_data = file_get_contents('php://input');
        $raw_post_array = explode('&', $raw_post_data);
        $myPost = array();
        foreach ($raw_post_array as $keyval) 
          $keyval = explode ('=', $keyval);
          if (count($keyval) == 2)
             $myPost[$keyval[0]] = urldecode($keyval[1]);
        
        // read the post from PayPal system and add 'cmd'
        $req = 'cmd=_notify-validate';
        if(function_exists('get_magic_quotes_gpc')) 
           $get_magic_quotes_exists = true;
         
        foreach ($myPost 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";
        

        // STEP 2: Post IPN data back to paypal to validate

        $ch = curl_init($this->paypal_url);

        $headers = array(
            'POST /cgi-bin/webscr HTTP/1.1',
            'Host: ' . $this->paypal_host,
            'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
            'Content-Length: ' . strlen($req),
            'User-Agent: PayPal-IPN-VerificationScript',
            'Connection: Close'
        );

        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_VERIFYPEER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        if( !($res = curl_exec($ch)) ) 
            curl_close($ch);
            exit;
        
        curl_close($ch);

        // STEP 3: Inspect IPN validation result and act accordingly

        if (strcmp ($res, "VERIFIED") == 0) 
            // check whether 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

            // 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 = urldecode($_POST['receiver_email']); 
            $payer_email = $_POST['payer_email'];
            $school_id = $_POST['custom'];

            // further checks
            if($payment_status == 'Completed') 

                $message = 'IPN verified successfully!';
                $this->email_developer($message);

                // Insert the transaction data in the database
                $this->product_model->insert_transaction_details($_POST);

             else 

                $message = 'Payment could not be verified!';
                $this->email_developer($message);  

            

         else if (strcmp ($res, "INVALID") == 0) 
            // log for manual investigation
            $message = 'IPN Invalid!';
            $this->email_developer($message);

        
    


对于那些可能遇到我的困境的人,请确保您还执行以下操作:

    如果您启用了跨站请求伪造 (CSRF),请确保将 IPN 侦听器/处理程序列入白名单,否则 IPN 消息将失败(PayPal IPN 历史记录中的错误 403)。 为确保您的 IPN 侦听器运行良好,请将其作为 URL 运行并查看响应。如果有任何错误,它将无法正常工作。如需回复,请尝试回显“已验证”或“无效”。 使用PayPal IPN Simulator 测试进程。包括一个在成功后将信息提交到数据库的过程。

我希望它对某人有所帮助。

【讨论】:

【参考方案2】:

使用 php://input 代替 $_POST

reson 在这里详细描述:PHP "php://input" vs $_POST

paypal 也有实现 IPN 监听器及其在 php 中的文档 Paypal tutorial

【讨论】:

【参考方案3】:

没有IPN的原因之一是连接paypal时出现问题,解决这个问题你要做的就是改变端口。

所以对于这段代码来自

$fp = fsockopen($url_parsed['host'],'80',$err_num,$err_str,30);  

$fp = fsockopen($url_parsed['host'],'443',$err_num,$err_str,30);

您也可以参考here

【讨论】:

以上是关于无法在 Codeigniter 中接收 PayPal IPN 响应的主要内容,如果未能解决你的问题,请参考以下文章

在 ci-merchant 库 codeigniter 中接收更多响应数据

Codeigniter + Angular Js:如何接收 JSON 数据

将 JSON 发布到 Codeigniter 控制器

如何从codeigniter控制器接收ajax响应?

Angelleye 的 Codeigniter,Paypal api - 向单个接收者发送付款

无法在 codeigniter 中提交数据