PHP Paypal IPN侦听器仅多个项目失败,而不是单个项目

PHP Paypal IPN侦听器仅多个项目失败,而不是单个项目,php,paypal,paypal-ipn,Php,Paypal,Paypal Ipn,在沙盒模式下处理多个项目时,我的IPN侦听器遇到了一个非常奇怪的问题 我已经使用IPN模拟器进行了测试,它工作正常,而且使用购物车,它将用于添加一个产品,工作正常,但只要我将两个或更多不同的产品添加到购物车和流程中,IPN侦听器就会失败,日志中没有任何错误 Paypal正在接收并发回所有正确的数据,因为我可以在测试沙箱帐户中看到事务细节,也可以在脚本中看到调试数据 它基于官方的IPN Paypal脚本,但添加了发送的电子邮件通知 <?php // CONFIG: Enabl

在沙盒模式下处理多个项目时,我的IPN侦听器遇到了一个非常奇怪的问题

我已经使用IPN模拟器进行了测试,它工作正常,而且使用购物车,它将用于添加一个产品,工作正常,但只要我将两个或更多不同的产品添加到购物车和流程中,IPN侦听器就会失败,日志中没有任何错误

Paypal正在接收并发回所有正确的数据,因为我可以在测试沙箱帐户中看到事务细节,也可以在脚本中看到调试数据

它基于官方的IPN Paypal脚本,但添加了发送的电子邮件通知

    <?php
    // CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
    // Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
    // Set this to 0 once you go live or don't require logging.
    define("DEBUG", 1);

    // Set to 0 once you're ready to go live
    define("USE_SANDBOX", 1);


    define("LOG_FILE", "./ipn.log");

    $mail = new PHPMailer;

    $mail->isSMTP();                                      // Set mailer to use SMTP
    $mail->Host = 'localhost';  // Specify main and backup server
    $mail->SMTPAuth = true;                               // Enable SMTP authentication
    $mail->Username = 'XXXXX';                            // SMTP username
    $mail->Password = 'XXXXX';                           // SMTP password

    $mail->From = 'XXXXX';
    $mail->FromName = 'XXXXX';
    $mail->addAddress('XXXXX', 'XXXXX');  // Add a recipient

    $mail->WordWrap = 50;                                 // Set word wrap to 50 characters
    $mail->isHTML(true);                                  // Set email format to HTML


    // PayPal IPN posts transaction info to this script
    // Read POST data
    // reading posted data 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";
    }


    // Post IPN data back to PayPal to validate the IPN data is genuine
    // Without this step anyone can fake IPN data

    if(USE_SANDBOX == true) {
            $paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
    } else {
            $paypal_url = "https://www.paypal.com/cgi-bin/webscr";
    }

    $ch = curl_init($paypal_url);
    if ($ch == FALSE) {
            return FALSE;
    }
    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);

    if(DEBUG == true) {
            curl_setopt($ch, CURLOPT_HEADER, 1);
            curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
    }
    // CONFIG: Optional proxy configuration
    //curl_setopt($ch, CURLOPT_PROXY, $proxy);
    //curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);

    // Set TCP timeout to 30 seconds
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

    // CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
    // of the certificate as shown below. Ensure the file is readable by the webserver.
    // This is mandatory for some environments.

    //$cert = __DIR__ . "./cacert.pem";
    //curl_setopt($ch, CURLOPT_CAINFO, $cert);

    $res = curl_exec($ch);
    if (curl_errno($ch) != 0) // cURL error
            {
            if(DEBUG == true) {        
                    error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
            }
            curl_close($ch);
            exit;

    } else {
                    // Log the entire HTTP response if debug is switched on.
                    if(DEBUG == true) {
                            error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
                            error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);

                            // Split response headers and payload
                            list($headers, $res) = explode("\r\n\r\n", $res, 2);
                    }
                    curl_close($ch);
    }

    // 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 PayPal email
            // check that payment_amount/payment_currency are correct
            // process payment and mark item as paid.

            // assign posted variables to local variables        
            $first_name = $_POST['first_name']; 
            $last_name = $_POST['last_name']; 
            $address_street = $_POST['address_street']; 
            $address_city = $_POST['address_city'];
            $address_zip = $_POST['address_zip'];
            $payment_status = $_POST['payment_status'];
            $payment_amount = $_POST['mc_gross'];
            $payment_currency = $_POST['mc_currency'];
            $payer_email = urldecode ( $_POST['payer_email'] );
            $shipping = $_POST['mc_shipping'];
            $payment_date = $_POST['payment_date'];
            $num_cart_items = $_POST['num_cart_items'];

            //Redundant variables
            //$item_name = $_POST['item_name1'];
            //$item_number = $_POST['item_number'];
            //$item_name1 = $_POST['item_name1'];
            //$item_number1 = $_POST['item_number1'];
            //$item_cost1 = $_POST['mc_gross1'];
            //$quantity1 = $_POST['quantity1'];        

            /*
     * Deduct VAT from a gross price
     * Using 20 VAT
     */
    $gross = $payment_amount;
    $nett = $gross - (($gross*1)/6);
    $nett = number_format($nett,2);
    // gives 0.XX due to rounding


    $table = "<table>
      <tr >
        <th colspan='2' scope='row' style='padding:0 30px'>
        <h1 style='color:#333366'>INVOICE </h1>
        <p style='color:#000'>Date added: $payment_date</p>
        <p style='color:#000'>&nbsp;</p>
        <h3 style='color:#333366'>Payment Details</h3>
        <p style='color:#000'>Name: $first_name $last_name</p>
       <p style='color:#000'>Address: $address_street, $address_city, $address_zip</p> 
       <p style='color:#000'>Payer email: $payer_email</p> 
       <p style='color:#000'>&nbsp;</p>
        <h3 style='color:#333366'>Product Details</h3>";
    for ($i = 1; $i <= $_POST['num_cart_items']; $i++) {
     $table .= "<p>".$_POST['item_name' + $i]."</p>";
     $table .= "<p>".$_POST['item_number' + $i]."</p>";
     $table .= "<p>".$_POST['quantity' + $i]."</p>";
     $table .= "<p>".$_POST['mc_gross_' + $i]."</p>";
    }       
    $table .= "<p style='color:#000'>Delivery cost: £$shipping</p>
        <p style='color:#000'>Net price: £$nett</p>
        <p style='color:#000'>Gross price: £$payment_amount</p>
        <p style='color:#000;font-size:11px'>VAT calculated at 20%  </p>
    </th>
      </tr>
    </table>";

    $mail->Subject = 'Your Order';
    $mail->Body   = $table;
    $mail->addAddress($payer_email, $first_name.' '.$last_name); 
    if(!$mail->send()) {
       echo 'Message could not be sent.';
       echo 'Mailer Error: ' . $mail->ErrorInfo;
       exit;
    }


            if(DEBUG == true) {
                    error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
            }
    } else if (strcmp ($res, "INVALID") == 0) {
            // log for manual investigation
            // Add business logic here which deals with invalid IPN messages


            $mail->Subject = 'Invalid';
    $mail->Body   = $table;
    if(!$mail->send()) {
       echo 'Message could not be sent.';
       echo 'Mailer Error: ' . $mail->ErrorInfo;
       exit;
    }


            if(DEBUG == true) {
                    error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
            }
    }
    ?>

我终于解决了这个问题。问题在于curl设置

多产品订单比单产品订单长得多

curl设置为在数据超过一定长度时发送额外的头(HTTP/1.1 100 Continue)

有一个简单的解决方法

将“Expect:”作为值添加到CURLOPT_HTTPHEADER数组中

因此:

变成

curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Expect:','Connection: Close'));

我终于解决了这个问题。问题在于curl设置

多产品订单比单产品订单长得多

curl设置为在数据超过一定长度时发送额外的头(HTTP/1.1 100 Continue)

有一个简单的解决方法

将“Expect:”作为值添加到CURLOPT_HTTPHEADER数组中

因此:

变成

curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Expect:','Connection: Close'));

IPN返回的多个项目仍然有效吗?是仍然有效。IPN返回的多个项目仍然有效吗?是仍然有效。感谢共享解决方案!感谢分享解决方案!