为什么我的PayPal IPN验证总是返回INVALID?

6
我正在使用一种基于Paypal网站示例代码的PHP类,同时与CodeIgniter一起使用。我使用IPN模拟器测试IPN监听器。我的电子邮件已发送,所以我知道正在访问它。问题在于,我总是收到INVALID的响应,但我不知道为什么。这是我第一次将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。 - DFriend
请在您的Paypal日志中查看响应信息- 我会进一步调试您的cUrl以查看是否有任何阻止请求的内容。 - David Nguyen
我进行了一些调试,发现$raw_post_data始终为空。这可能是导致问题的原因吗?我该如何解决这个问题? - ShoeLace1291
1
您需要告知PayPal将IPN结果发送到何处。如果启用了CSRF,则此操作将失败。结果可以在$_POST数组中找到,而不是$this->input->post()。您没有向PayPal发送任何合法的标头以使其通过。请重新阅读文档并查找需要发送哪些标头。 - Philip
$this->subscription->create_payment($data); 运行正常吗?由于 if($this->paypal->verified){} 中的 verified 是 false,所以很可能所有代码都会失败。但是电子邮件已发送。您能发布一下 curl_exec($ch) 的输出吗? - user2560539
你为什么不使用PayPal的PHP SDK呢?我发现它用起来更容易。另外,我曾经在使用PayPal的原始数据时遇到了一些麻烦,所以我写了这个正则表达式来解析它:https://gist.github.com/hypeJunction/913f07c5fc4f2542a0aa - hypeJunction
1个回答

0

如果您在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!");
}

您将不会收到任何电子邮件。问题出现在您的PayPal类中的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(),以及基于用户在strcmp()文档中的评论

  • strcmp()在失败时将返回NULL。
  • 这会导致在使用等于比较(==)时等同于匹配。
  • 相反,您可能希望使用完全相同的比较(===)来测试匹配,这不应捕获NULL返回。

以上内容是什么意思?请看下面的小例子。

if(null==0) {
echo "1";
}
if(null===0) {
echo "2";
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接