Paypal SandBox IPN always returns INVALID

前端 未结 9 1389
予麋鹿
予麋鹿 2020-12-03 08:33

As mentioned in one of the comments in an answer below, I tried following this tutorial. So now I have the following:


The ipn.php file:

相关标签:
9条回答
  • 2020-12-03 09:01

    Here's how to avoid these errors...

    foreach ($_POST as $key => $value) {
         if ($key=='transaction')
              foreach ($value as $key2=>$value2) {
                   $value['transaction'][$key2] = urlencode(stripslashes($value2));
         }
         else {
              $value = urlencode(stripslashes($value));
         }
         $req .= "&$key=$value";
     }
    
    0 讨论(0)
  • 2020-12-03 09:03

    I am not sure what is exactly wrong right now with your code, but I was strugling wuth the same while ago and my fixes was to add HOST in the header and host have to be www.paypal.com. I used fsockopen method and work fine now.

    In Curl I had a problem before with ssl. And solution was to put those lines:

    curl_setopt($curl, CURLOPT_COOKIEJAR, dirname(__FILE__) . "/cookies.txt");
    curl_setopt($curl, CURLOPT_COOKIEFILE, dirname(__FILE__) . "/cookies.txt");
    

    where of course file cookies.txt have to exists. and more over I had to run one connection to page to get session data and later send post data.

    Below is a header what is working fine for me with fsockopen method

    $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
    $header .= "Host: www.paypal.com\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    
    0 讨论(0)
  • 2020-12-03 09:07

    The problem is that you don't check the HTTP response code, so you are intepreting the "Invalid Host header" as the PayPal response, whilst it's the web server response (for the status code 400).
    If you look at the PayPal documentation, there is a PHP example which is very similar to your code, since it uses the "fsockopen", "fputs" and "fgets" functions to communicate with the PayPal server.
    But if you look carefully at the remark after the "fsockopen" call, you can read:

    // Process validation from PayPal 
    // TODO: This sample does not test the HTTP response code. All 
    // HTTP response codes must be handled or you should use an HTTP 
    // library, such as cUrl
    

    And this is exacty your problem: you don't check that the HTTP response code is 200 (OK), before parsing the response body.
    Also, using the "strtolower" function is not correct, since the real response from the PayPal server is always uppercase, as shown in the above cited example.
    Even if the PayPal example uses the "fsockopen" approach, I think it should be much better to use the PHP cURL library to implement your IPN listener.
    Have also a look at the following answers:

    • PHP cURL PayPal Sandbox
    • cURL or fsockopen for paypal ipn

    However, if you really want to use the "fsockopen" function, you should always specify the "Host" header field in the POST request, as shown in the following snippet of code (taken from the PHP manual):

    <?php
    $fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
    if (!$fp) {
        echo "$errstr ($errno)<br />\n";
    } else {
        $out = "GET / HTTP/1.1\r\n";
        $out .= "Host: www.example.com\r\n";
        $out .= "Connection: Close\r\n\r\n";
        fwrite($fp, $out);
        while (!feof($fp)) {
            echo fgets($fp, 128);
        }
        fclose($fp);
    }
    ?>
    

    UPDATE

    Here is a simple function for recursive stripslashes/urlencoding:

    <html>
    <body>
    <pre>
    <?
    
    $post = Array (
      "transaction" => Array("USD 20.00"),
      "payment_request_date" => "Sun Aug '05 08:49:20 PDT 2012",
      "return_url" => "http://000.000.000.000/success.php"
    );
    
    echo "before myUrlencode...\n";
    print_r($post);
    
    function myUrlencode($post) {
      foreach ($post as $key => $val) {
        if (is_array($val)) {
          $post[$key] = myUrlencode($val);
        } else {
          $post[$key] = urlencode(stripslashes($val));
        }
      }
      return($post);
    }
    
    echo "\nafter myUrlencode...\n";
    print_r(myUrlencode($post));
    
    ?>
    </pre>
    </body>
    </html>
    
    0 讨论(0)
  • 2020-12-03 09:09

    Edit:

    Now that I can see the array you've outputted, try replacing this to get rid of the PHP array error:

    foreach ($_POST as $key => $value) {
        if (!is_array($value)) {
            $value = urlencode(stripslashes($value));
            $req .= "&$key=$value";
        }
        else if (is_array($value)) {
            $paymentArray = explode(' ', $value[0]);
            $paymentCurrency = urlencode(stripslashes($paymentArray[0]));
            $paymentGross = urlencode(stripslashes($paymentArray[1]));
            $req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
        }
    }
    

    Here is the edited code in full:

    // read the post from PayPal system and add 'cmd'
    $req = 'cmd=' . urlencode('_notify-validate');
    
    foreach ($_POST as $key => $value) {
        if (!is_array($value)) {
            $value = urlencode(stripslashes($value));
            $req .= "&$key=$value";
        }
        else if (is_array($value)) {
            $paymentArray = explode(' ', $value[0]);
            $paymentCurrency = urlencode(stripslashes($paymentArray[0]);
            $paymentGross = urlencode(stripslashes($paymentArray[1]);
            $req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
        }
    }
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
    curl_setopt($ch, CURLOPT_HEADER, 0);
    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_HTTPHEADER, array('Host: www.paypal.com'));
    $res = curl_exec($ch);
    curl_close($ch);
    
    
    // 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 = $_POST['receiver_email'];
    $payer_email = $_POST['payer_email'];
    
    
    if (strcmp ($res, "VERIFIED") == 0) {
        // check 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
    }
    else if (strcmp ($res, "INVALID") == 0) {
        // log for manual investigation
    }
    

    Check this out!

    Edit: Check out the PayPal troubleshooting tips:

    https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNTesting

    0 讨论(0)
  • 2020-12-03 09:12
    1. Got it working using the basic sample code 4b,

    2. Cleared $ipnNotificationUrl = ""; from the basic sample code as I had a value in there which I added myself,

    3. Created a seller account instead of a business pro account in sandbox,

    4. Set the seller account to enable the ipn url,

    5. Used the following PHP 5.2 sample code for the ipn listener

    6. Added the 2 lines into the listener, as described here, the 2 lines can be seen below:

    7. Downloaded the cacert.pem certificate to my server from here and put it in the same directory as the ipn listener:

    The 2 lines mentioned in point 6:

    CURLOPT_SSL_VERIFYPEER => TRUE,
    CURLOPT_CAINFO => 'cacert.pem',
    

    I have no idea why the sandbox business pro account does not let me set an ipn url, but the seller account does.

    0 讨论(0)
  • 2020-12-03 09:18

    It's a problem with the + character, it often get wrongly fetched so I made that workaround, and it worked for me.

    payment_data = Sat Jun 04 2016 15:11:16 GMT+0200 (CEST)

    foreach ($_POST as $key => $value) {
    if($key !== "payment_date"){
        $req .= '&' . $key . '=' . rawurlencode(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
    }else{
        $req .= '&' . $key . '=' . rawurlencode(str_replace(array('GMT '),array('GMT+'),$value));
    }}
    
    0 讨论(0)
提交回复
热议问题