Paypal IPN始终返回INVALID

时间:2016-06-10 03:41:35

标签: php curl paypal

我正在使用我在这里下载的这个监听器Paypal-IPN-listener。问题是我总是得到一个“无效”的回复。我检查了我的主机提供商,他们确认服务器有支持TLS1.2的OpenSSL v1.0.1。任何帮助将不胜感激。

我得到的回应:

  

HTTP / 1.1 200 OK日期:2016年6月10日星期五03:32:59 GMT服务器:Apache   X-Frame-Options:SAMEORIGIN Set-Cookie:   c9MWDuvPtT9GIMyPc3jwol1VSlO = M5RZRbR9O0p3RyZcCd57VOjVO7vQahSBxti78obb5GWSaRge7FbInlJm9xeF6VIGA8jPMUyenYAZxlLOd4-YqTmucvLu0i7lWMg5RM94Q0SFZOVZ1_ysAo5Y_G3ISqMX_oc5Ts8Xqy5z9q8YKYW-RCBOKKo6ZpkXYUKF2RyjbOQIV3jeUWUSBBtkl4SxeDFi4MUm49opYTa6CvKAqQZ4vr6cWpOQghPpmEsz0s175Z8k6UtTfB99JV14faev1SBZUafmOkyNWCT3XSUF_dRWx-RAQ9L_T8MPetd5beRMm5fXBY1rvehFXw33aUvhTxckwGBJrdrKW2bOQa0Ry6YaRdp1uvdMC6uCuesozTjKXBFgSf0g0cDdHXoxV0rgVPTMlTRoVoV9xNJJ1CNKZzxeCvaFBZNSQdSTT888w0;   域= .paypal.com;路径= /;安全; HttpOnly Set-Cookie:   cookie_check =是; expires =周一,08-Jun-2026 03:32:59 GMT;   域= .paypal.com;路径= /;安全; HttpOnly Set-Cookie:   navcmd = _notify-验证;域= .paypal.com;路径= /;安全;仅Http   Set-Cookie:navlns = 0.0; expires = Sun,10-Jun-2018 03:32:59 GMT;   域= .paypal.com;路径= /;安全; HttpOnly Set-Cookie:   阿帕奇= 10.72.108.11.1465529579669687;路径= /;到期=太阳,03年6月 - 46日   03:32:59 GMT Vary:Accept-Encoding,User-Agent X-Cnection:close   HTTP_X_PP_AZ_LOCATOR:sandbox.slc Paypal-Debug-Id:5d6d91989f423   设置Cookie:   X-PP-SILOVER =名称%3DSANDBOX3.WEB.1%26silo_version%3D1880%26app%3Dappdispatcher%26TIME%3D3946076759%26HTTP_X_PP_AZ_LOCATOR%3Dsandbox.slc;   Expires = Fri,2016年6月10日04:03:00 GMT;域= .paypal.com;路径= /;   安全; HttpOnly Set-Cookie:X-PP-SILOVER =; Expires = Thu,1970年1月1日   00:00:01 GMT Strict-Transport-Security:最大年龄= 14400   Transfer-Encoding:chunked Content-Type:text / html;字符集= UTF-8

     

INVALID   -------------------------------------------------- ------------------------------ payment_type instant payment_date Fri Jun 10   2016 11:02:14 GMT 0800(PHT)payment_status已完成   address_status已确认payer_status已验证   first_name John last_name Smith   payer_email buyer@paypalsandbox.com payer_id
  TESTBUYERID01 address_name John Smith address_country
  美国address_country_code US address_zip
  95131 address_state CA address_city San Jose   address_street 123任何街头商务
  seller@paypalsandbox.com receiver_email
  seller@paypalsandbox.com receiver_id
  seller@paypalsandbox.com residence_country美国item_name1
  item_number1 AK-1234税   2.02 mc_currency USD mc_fee 0.44 mc_gross 12.34 mc_gross_1 12.34   mc_handling 2.06 mc_handling1 1.67   mc_shipping 3.02 mc_shipping1 1.02 txn_type
  cart txn_id 331854069 notify_version 2.1   自定义xyz123发票abc1234   test_ipn 1 verify_sign
  AFcWxV21C7fd0v3bYYYRCpSSRl31AfWDVp14NjdjOS60QpDQiiFYCMSm

ipn.php:

<?php


include('listener/ipnlistener.php');
$listener = new IpnListener();

$listener->use_sandbox = true;

try {
    $listener->requirePostMethod();
    $verified = $listener->processIpn();
} catch (Exception $e) {
    $file = fopen("log.txt","w");
    fwrite($file,$e->getMessage());
    fclose($file);
    exit(0);
}


if ($verified) {
    $file = fopen("log.txt","w");
    fwrite($file,$listener->getTextReport());
    fclose($file);

} else {
    $file = fopen("log.txt","w");
    fwrite($file,$listener->getTextReport());
    fclose($file);
}

?>

ipnlistener.php

<?php
/**
 *  PayPal IPN Listener
 *
 *  A class to listen for and handle Instant Payment Notifications (IPN) from 
 *  the PayPal server.
 *
 *  https://github.com/Quixotix/PHP-PayPal-IPN
 *
 *  @package    PHP-PayPal-IPN
 *  @author     Micah Carrick
 *  @copyright  (c) 2012 - Micah Carrick
 *  @version    2.1.0
 */
class IpnListener {

    /**
     *  If true, the recommended cURL PHP library is used to send the post back 
     *  to PayPal. If flase then fsockopen() is used. Default true.
     *
     *  @var boolean
     */
    public $use_curl = true;     

    /**
     *  If true, explicitly sets cURL to use SSL version 3. Use this if cURL
     *  is compiled with GnuTLS SSL.
     *
     *  @var boolean
     */
    public $force_ssl_v3 = true;     

    /**
     *  If true, cURL will use the CURLOPT_FOLLOWLOCATION to follow any 
     *  "Location: ..." headers in the response.
     *
     *  @var boolean
     */
    public $follow_location = false;     

    /**
     *  If true, an SSL secure connection (port 443) is used for the post back 
     *  as recommended by PayPal. If false, a standard HTTP (port 80) connection
     *  is used. Default true.
     *
     *  @var boolean
     */
    public $use_ssl = true;      

    /**
     *  If true, the paypal sandbox URI www.sandbox.paypal.com is used for the
     *  post back. If false, the live URI www.paypal.com is used. Default false.
     *
     *  @var boolean
     */
    public $use_sandbox = true; 

    /**
     *  The amount of time, in seconds, to wait for the PayPal server to respond
     *  before timing out. Default 30 seconds.
     *
     *  @var int
     */
    public $timeout = 30;       

    private $post_data = array();
    private $post_uri = '';     
    private $response_status = '';
    private $response = '';

    const PAYPAL_HOST = 'www.paypal.com';
    const SANDBOX_HOST = 'www.sandbox.paypal.com';

    /**
     *  Post Back Using cURL
     *
     *  Sends the post back to PayPal using the cURL library. Called by
     *  the processIpn() method if the use_curl property is true. Throws an
     *  exception if the post fails. Populates the response, response_status,
     *  and post_uri properties on success.
     *
     *  @param  string  The post data as a URL encoded string
     */
    protected function curlPost($encoded_data) {

        if ($this->use_ssl) {
            $uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';
            $this->post_uri = $uri;
        } else {
            $uri = 'http://'.$this->getPaypalHost().'/cgi-bin/webscr';
            $this->post_uri = $uri;
        }

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_CAINFO, 
                    dirname(__FILE__)."/cert/cacert.pem");
        curl_setopt($ch, CURLOPT_URL, $uri);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->follow_location);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);

        if ($this->force_ssl_v3) {
            curl_setopt($ch, CURLOPT_SSLVERSION, 6);
        }

        $this->response = curl_exec($ch);
        $this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));

        if ($this->response === false || $this->response_status == '0') {
            $errno = curl_errno($ch);
            $errstr = curl_error($ch);
            throw new Exception("cURL error: [$errno] $errstr");
        }
    }

    /**
     *  Post Back Using fsockopen()
     *
     *  Sends the post back to PayPal using the fsockopen() function. Called by
     *  the processIpn() method if the use_curl property is false. Throws an
     *  exception if the post fails. Populates the response, response_status,
     *  and post_uri properties on success.
     *
     *  @param  string  The post data as a URL encoded string
     */
    protected function fsockPost($encoded_data) {

        if ($this->use_ssl) {
            $uri = 'ssl://'.$this->getPaypalHost();
            $port = '443';
            $this->post_uri = $uri.'/cgi-bin/webscr';
        } else {
            $uri = $this->getPaypalHost(); // no "http://" in call to fsockopen()
            $port = '80';
            $this->post_uri = 'http://'.$uri.'/cgi-bin/webscr';
        }

        $fp = fsockopen($uri, $port, $errno, $errstr, $this->timeout);

        if (!$fp) { 
            // fsockopen error
            throw new Exception("fsockopen error: [$errno] $errstr");
        } 

        $header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
        $header .= "Host: ".$this->getPaypalHost()."\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: ".strlen($encoded_data)."\r\n";
        $header .= "Connection: Close\r\n\r\n";

        fputs($fp, $header.$encoded_data."\r\n\r\n");

        while(!feof($fp)) { 
            if (empty($this->response)) {
                // extract HTTP status from first line
                $this->response .= $status = fgets($fp, 1024); 
                $this->response_status = trim(substr($status, 9, 4));
            } else {
                $this->response .= fgets($fp, 1024); 
            }
        } 

        fclose($fp);
    }

    private function getPaypalHost() {
        if ($this->use_sandbox) return self::SANDBOX_HOST;
        else return self::PAYPAL_HOST;
    }

    /**
     *  Get POST URI
     *
     *  Returns the URI that was used to send the post back to PayPal. This can
     *  be useful for troubleshooting connection problems. The default URI
     *  would be "ssl://www.sandbox.paypal.com:443/cgi-bin/webscr"
     *
     *  @return string
     */
    public function getPostUri() {
        return $this->post_uri;
    }

    /**
     *  Get Response
     *
     *  Returns the entire response from PayPal as a string including all the
     *  HTTP headers.
     *
     *  @return string
     */
    public function getResponse() {
        return $this->response;
    }

    /**
     *  Get Response Status
     *
     *  Returns the HTTP response status code from PayPal. This should be "200"
     *  if the post back was successful. 
     *
     *  @return string
     */
    public function getResponseStatus() {
        return $this->response_status;
    }

    /**
     *  Get Text Report
     *
     *  Returns a report of the IPN transaction in plain text format. This is
     *  useful in emails to order processors and system administrators. Override
     *  this method in your own class to customize the report.
     *
     *  @return string
     */
    public function getTextReport() {

        $r = '';

        // date and POST url
        for ($i=0; $i<80; $i++) { $r .= '-'; }
        $r .= "\n[".date('m/d/Y g:i A').'] - '.$this->getPostUri();
        if ($this->use_curl) $r .= " (curl)\n";
        else $r .= " (fsockopen)\n";

        // HTTP Response
        for ($i=0; $i<80; $i++) { $r .= '-'; }
        $r .= "\n{$this->getResponse()}\n";

        // POST vars
        for ($i=0; $i<80; $i++) { $r .= '-'; }
        $r .= "\n";

        foreach ($this->post_data as $key => $value) {
            $r .= str_pad($key, 25)."$value\n";
        }
        $r .= "\n\n";

        return $r;
    }

    /**
     *  Process IPN
     *
     *  Handles the IPN post back to PayPal and parsing the response. Call this
     *  method from your IPN listener script. Returns true if the response came
     *  back as "VERIFIED", false if the response came back "INVALID", and 
     *  throws an exception if there is an error.
     *
     *  @param array
     *
     *  @return boolean
     */    
    public function processIpn($post_data=null) {

        $encoded_data = 'cmd=_notify-validate';

        if ($post_data === null) { 
            // use raw POST data 
            if (!empty($_POST)) {
                $this->post_data = $_POST;
                $encoded_data .= '&'.file_get_contents('php://input');
            } else {
                throw new Exception("No POST data found.");
            }
        } else { 
            // use provided data array
            $this->post_data = $post_data;

            foreach ($this->post_data as $key => $value) {
                $encoded_data .= "&$key=".urlencode($value);
            }
        }

        if ($this->use_curl) $this->curlPost($encoded_data); 
        else $this->fsockPost($encoded_data);

        if (strpos($this->response_status, '200') === false) {
            throw new Exception("Invalid response status: ".$this->response_status);
        }

        if (strpos($this->response, "VERIFIED") !== false) {
            return true;
        } elseif (strpos($this->response, "INVALID") !== false) {
            return false;
        } else {
            throw new Exception("Unexpected response from PayPal.");
        }
    }

    /**
     *  Require Post Method
     *
     *  Throws an exception and sets a HTTP 405 response header if the request
     *  method was not POST. 
     */    
    public function requirePostMethod() {
        // require POST requests
        if ($_SERVER['REQUEST_METHOD'] && $_SERVER['REQUEST_METHOD'] != 'POST') {
            header('Allow: POST', true, 405);
            throw new Exception("Invalid HTTP request method.");
        }
    }
}

1 个答案:

答案 0 :(得分:1)

调试步骤

  1. 检查脚本是否指向IPN侦听器和输出生成脚本中的正确路径 ssl://www.sandbox.paypal.com:443 / cgi-bin / webscr for Sandbox(包括IPN模拟器) ssl://www.paypal.com:443 / cgi-bin / webscr for live site
  2. 沙盒只允许SSL连接 在编写本文时,HTTP协议仍可用于实际站点。

    1. 检查服务器是否支持TLS 1.2
      PayPal正在更新其服务,要求所有HTTPS / TLS

    2. 的TLS v1.2
    3. 如果您使用fsockopen(),请确保已在PHP配置中启用该功能

    4. 如果使用curl()函数确保它们在PHP配置中启用

    5. 检查curl_setopt($ ch,CURLOPT_SSLVERSION,)是否设置为正确的版本,即在撰写本文时为6

    6. 如果检查了以上所有内容并且您仍然无法使其工作,则可以尝试在IPN模拟器的付款日期字段中输入NULL,以查看它是否返回验证结果。

      付款日期用于时区的加号可能会导致问题 当urlencode()用于解析URL时。