为啥我的贝宝 ipn 验证总是返回无效?

Posted

技术标签:

【中文标题】为啥我的贝宝 ipn 验证总是返回无效?【英文标题】:Why do my paypal ipn validations always return INVALID?为什么我的贝宝 ipn 验证总是返回无效? 【发布时间】:2016-08-10 22:33:40 【问题描述】:

我正在使用基于 paypal 网站上的示例代码创建的 php 类。我也将它与 CodeIgniter 一起使用。我用 IPN 模拟器测试 IPN 监听器。我的电子邮件已发送,所以我知道它正在被访问。问题是我总是得到无效的响应,我不知道为什么。这是我第一次在我的一个网站中集成 PayPal。什么可能导致此问题?

这是我的课:

<?php

class Paypal 

    public $sandbox = false;
    private $_url;
    public $verified = false;
    public $fields = array();
    public $post_fields = array();
    public $result;

    public function __construct($params = array())

        $this->sandbox = (isset($params['sandbox'])) ? $params['sandbox'] : false;

        $this->_url = ($this->sandbox) ? 'https://www.sandbox.paypal.com/cgi-bin/webscr' : 'https://www.paypal.com/cgi-bin/webscr';

    

    public function run()

        $this->verified = false;

        // STEP 1: read POST data
        // Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
        // Instead, read raw POST data from the input stream.
        $raw_post_data = file_get_contents('php://input');
        $raw_post_array = explode('&', $raw_post_data);

        foreach ($raw_post_array as $keyval) 

          $keyval = explode ('=', $keyval);

          if (count($keyval) == 2)

            $this->post_fields[$keyval[0]] = urldecode($keyval[1]);

        

        // read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
        $req = 'cmd=_notify-validate';

        if (function_exists('get_magic_quotes_gpc')) 

          $get_magic_quotes_exists = true;

        

        foreach ($this->post_fields 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->_url);
        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, array('Connection: Close'));
        // In wamp-like environments that do not come bundled with root authority certificates,
        // please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set
        // the directory path of the certificate as shown below:
        curl_setopt($ch, CURLOPT_CAINFO, FCPATH.'cacert.pem');
        if ( !($res = curl_exec($ch)) ) 
          // error_log("Got " . curl_error($ch) . " when processing IPN data");
          curl_close($ch);
          die('curl did not work<br>'.FCPATH.'cacert.pem');
        
        curl_close($ch);

        if (strcmp ($res, "VERIFIED") == 0) 

            $this->verified = true;            

        

        $this->result = $res;

    

这是我的 CI 控制器:

function paypalipn()

        $this->load->model('subscription');
        $this->load->library('paypal', array('sandbox' => true));;

        $this->paypal->run();

        $fields = $this->paypal->post_fields;

        if($this->paypal->verified)

            $data = array(
                'user_id' => $fields['buyer_id'],
                'txn_id' => $fields['txn_id'],
                'payment_gross' => $fields['mc_gross'],
                'currency_code' => $fields['mc_currency'],
                'payer_email' => $fields['payer_email'],
                'plan_id' => $fields['item_number'],
                'payment_status' => $fields['payment_status']
            );

            $this->subscription->create_payment($data);

        

        $this->load->library('email');

        $this->email->to('*******@gmail.com');
        $this->email->from('**************');
        $this->email->subject('PayPal IPN');
        $this->email->message($this->paypal->result."\nAmount: ".$fields['mc_gross']."\nCurrency: ".$fields['mc_currency']."\nUser ID: ".$fields['buyer_id']);

        $this->email->send();

    

电子邮件中的所有字段均为空白,$this->paypal->result 始终返回“INVALID”。有人有什么想法吗?感谢您的宝贵时间。

【问题讨论】:

您的 Paypal 课程对我来说看起来很可靠,并且似乎与我使用的工作代码几乎相同。一定是其他问题,SSL 是第一个要查看的地方。 SSL 是否真的在您的站点上工作,并且“cacert.pem”是您用于该站点的证书吗?如果没有经过验证的 https 连接,PayPal 将始终返回 INVALID。 查看您在 Paypal 上的日志以查看您收到的响应 - 我会进一步调试您的 cUrl 以查看是否有任何内容阻止您的请求。 我做了一些调试,发现 $raw_post_data 总是空的。这会导致问题吗?我该如何解决这个问题? 您需要通知paypal将IPN结果发送到哪里。如果启用 CSRF,这将失败。结果在 $_POST 数组中找到,而不是在 $this->input->post() 中。您没有向贝宝发送任何合法的标头以使其通过。重新阅读文档并找出需要发送哪些标头。 $this-&gt;subscription-&gt;create_payment($data); 工作了吗?很可能if($this-&gt;paypal-&gt;verified) 中的所有代码都应该失败,因为验证是错误的。但是会发送电子邮件。你能先发布curl_exec($ch) 的输出是什么吗? 【参考方案1】:

如果您编写了这样的代码,请在 CI 控制器中:

function paypalipn()

        $this->load->model('subscription');
        $this->load->library('paypal', array('sandbox' => true));;

        $this->paypal->run();

        $fields = $this->paypal->post_fields;

        if($this->paypal->verified)

            $data = array(
                'user_id' => $fields['buyer_id'],
                'txn_id' => $fields['txn_id'],
                'payment_gross' => $fields['mc_gross'],
                'currency_code' => $fields['mc_currency'],
                'payer_email' => $fields['payer_email'],
                'plan_id' => $fields['item_number'],
                'payment_status' => $fields['payment_status']
            );

            $this->subscription->create_payment($data);
        $this->load->library('email');
        $this->email->to('*******@gmail.com');
        $this->email->from('**************');
        $this->email->subject('PayPal IPN');
        $this->email->message($this->paypal->result."\nAmount: ".$fields['mc_gross']."\nCurrency: ".$fields['mc_currency']."\nUser ID: ".$fields['buyer_id']);
        $this->email->send();
        
     

或者如果你在 if 语句失败的情况下停止了脚本

if($this->paypal->verified)

            $data = array(
                'user_id' => $fields['buyer_id'],
                'txn_id' => $fields['txn_id'],
                'payment_gross' => $fields['mc_gross'],
                'currency_code' => $fields['mc_currency'],
                'payer_email' => $fields['payer_email'],
                'plan_id' => $fields['item_number'],
                'payment_status' => $fields['payment_status']
            );

            $this->subscription->create_payment($data);
 else 
exit("We are sad to inform you that verification FAILED. Bye!");

您不会收到任何电子邮件。问题出在您的贝宝课程中的public function run() 中,这很可能是失败的部分。

       if ( !($res = curl_exec($ch)) ) 
          curl_close($ch);
          die('curl did not work<br>'.FCPATH.'cacert.pem');
        
        curl_close($ch);
       if (strcmp ($res, "VERIFIED") == 0)    
            $this->verified = true;                
        

尝试像这样重写它

       $res=curl_exec($ch); // define $res
       if($res==null)  // check if $res is null
            exit('curl did not work<br>'.FCPATH.'cacert.pem');
        
       curl_close($ch);
       if (strcmp ($res, "VERIFIED") === 0)    
            $this->verified = true;                
       

关于使用strcmp() 的最后3 行,并基于strcmp() 文档中的用户comment。

strcmp() 失败时返回 NULL。 当使用等号比较 (==) 时,这会产生等同于匹配的副作用。 相反,您可能希望使用相同比较 (===) 来测试匹配项,这不应捕获 NULL 返回。

以上是什么意思?举个小例子。

if(null==0) 
echo "1";

if(null===0) 
echo "2";

上面的示例只会输出=> 1,在您的情况下,这意味着如果strcmp() 失败,您会将verified 设置为true,这是不正确的。

【讨论】:

以上是关于为啥我的贝宝 ipn 验证总是返回无效?的主要内容,如果未能解决你的问题,请参考以下文章

贝宝 IPN 验证

IPN 验证 ipnSimulator INVALID

Paypal IPN 沙盒 - IPN 侦听器 - 未验证或无效

贝宝 IPN 模拟器在直播时总是给出无效响应,

IPN 模拟器:“IPN 未发送,握手未验证。请查看您的信息”

为暂停的订阅正确的贝宝 IPN 变量