title: Paypal PHP not working. No response received from Sandbox; only Connection: close
body: <p>I've been at it for 24 hours now and am about to pull my hair out. I've checked all over the Web and within Stackoverflow and can find nothing that helps. I've even checked previous posts such as <a href="https://stackoverflow.com/questions/8302028/not-receiving-a-response-from-paypal-ipn-sandbox">Not receiving a response from Paypal IPN Sandbox</a> but the responses aren't working for me. Perhaps I've missed <em>the</em> post that'll help me; if I have, please excuse me for repeating a same question, but this matter is rather urgent and I'm at the end of my rope.</p>
  
  <p><strong>Anyway, the issue is this:</strong></p>
  
  <p>I have a PHP cart going and it sends clients to Paypal. Everything is working perfectly. I've built it according to Paypal's instructions when it comes to sending all codes to them. Everything fine except when it comes to the final step, the return <code>POST</code> from Paypal back to my server to check everything's OK. Even there I've copied the entire Paypal code as is and just added the functions that'll check everything matches on my part. However, I can't get it to work. I try sending the results to a DB and nothing happens. So I placed a PHP <code>Mail</code> command right where the last action should be if everything's OK and I don't receive the E-Mail. I've placed this same <code>Mail</code> command almost everywhere else to try and find out what's going on and have received different clues from different places.</p>
  
  <p><strong>First,</strong> when I call to print into the E-Mail the vars Paypal's supposed to send back to my server, I get the E-Mail with all vars except for <code>item_name</code> and <code>item_number</code>, which I get blank; this is strange, because up to that point Paypal Sandbox is showing me every single product I'm supposedly purchasing. I tried giving these vars on the PHP code a fixed value in light of the fact that Paypal is not posting a value to them, at least at that point. I wanted to see if the lack of these two values was the reason for the error; the error persisted, however, even with the set values.</p>
  
  <p><strong>Second,</strong> there's a <code>while</code> loop in the Paypal code (the loop that a response in the link given above says one should replace with <code>$res=stream_get_contents($fp, 1024);</code>, this, however, didn't work for me either), so I placed the <code>Mail</code> command in there to see what I got back. I asked to print the <code>$res</code> var and the <code>payment_status</code> and got, as expected, several E-Mails, the collected results of which are as follows:</p>
  
  <pre><code>    payment_status = Pending
      $res = HTTP/1.0 302 Found
             Location: https://www.sandbox.paypal.com
             Server: BigIP
             Connection: close
             Content-Length: 0
  </code></pre>
  
  <p>So I'm guessing the error is occurring at this point. Something is happening (or not happening) which is not only not returning the <code>Completed</code> status for <code>payment_status</code> that the code looks for in order to continue checking and etc., but it's not even returning an <code>INVALID</code> response. So I'm getting no response and nothing to show me whether everything's OK or not. And, what's strange, and even worse, is the fact that Sandbox is still fake-charging me for every single one of these payment attempts, even though it's clear that there's an error somewhere and the process is not being fulfilled. (This, of course, brings out another dilemma: once all this is solved, if there is a server error at some point for X reason, will Paypal still charge my client and I won't even know somebody has bought something on my Website?)</p>
  
  <p>Well, I suppose that's as much as I can explain on this matter. At this point I can't see where I could have gone wrong. The code should make this a pretty straightforward thing. So really I don't see how I could have messed up the copy &amp; paste. I suppose that at least should have no errors, unless they come with the original code. Moreover, because everything else to that point is working 100% fine, the only place where this error could be is within this PHP file Paypal has to call once the payment process is completed by the user. </p>
  
  <p><strong>ANY idea on the matter will be EXTREMELY appreciated!</strong></p>
  
  <p>Here goes the code. </p>
  
  <p><strong>PHP CODE:</strong></p>
  
  <pre><code>    include('../inc/db_fns.inc'); //&lt;--All my DB work is in here.
      include('../inc/shipping.inc');
  
      $paypal_email = "seller_1360198925_biz@hotmail.com";
      $paypal_currency = 'USD';
  
      //Here begin the functions I'm using to check everything and pass data to DB.
      function no_paypal_trans_id($trans_id) {
        $connection = db_connect();
        $query = sprintf("SELECT id FROM orders WHERE paypal_trans_id = '%s'", mysql_real_escape_string($trans_id));
        $result = mysql_query($query);
        $num_results = mysql_num_rows($result);
        if($num_results == 0) {
          return true; 
        }
        return false;
      }
  
      function payment_amount_correct($shipping, $params) {
        $amount = 0.00;   
        for ($i=1;  $i &lt;= $params['num_cart_items']; $i++) {
          $query = sprintf("SELECT precio FROM products WHERE id='%s'", mysql_real_escape_string($params["item_number{$i}"]));
          $result = mysql_query($query);
          if($result) {
            $item_price = mysql_result($result, 0, 'precio');
            $amount += $item_price * $params["quantity{$i}"];
          }
        }
        if(($amount+$shipping) == $params['mc_gross']) {
          return true;    
        } else {
          return false;   
        }
      }
  
      function create_order($params) {
        db_connect();
        $query =  sprintf("INSERT INTO orders set orders.firstname = '%s', orders.lastname = '%s', orders.email = '%s', orders.country = '%s', orders.address = '%s', orders.city = '%s', orders.zip_code = '%s', orders.state = '%s', orders.status = '%s', orders.amount = '%s', orders.paypal_trans_id = '%s', created_at = NOW()", mysql_real_escape_string($params['first_name']), mysql_real_escape_string($params['last_name']), mysql_real_escape_string($params['payer_email']), mysql_real_escape_string($params['address_country']), mysql_real_escape_string($params['address_street']), mysql_real_escape_string($params['address_city']), mysql_real_escape_string($params['address_zip']), mysql_real_escape_string($params['address_state']), mysql_real_escape_string($params['payment_status']), mysql_real_escape_string($params['mc_gross']), mysql_real_escape_string($params['txn_id']));
        $result = mysql_query($query);
        if(!$result) {
          return false;
        }
        $order_id = mysql_insert_id();
        for ($i=1;  $i &lt;= $params['num_cart_items'] ; $i++) {
          $product = find_product($params["item_number{$i}"]);
          $query = sprintf("INSERT INTO items set order_id = '%s', product_id = '%s', title = '%s', price = '%s', qty = '%s'", mysql_real_escape_string($order_id), mysql_real_escape_string($product['id']), mysql_real_escape_string($product['title']), mysql_real_escape_string($product['price']), mysql_real_escape_string($params["quantity{$i}"]));
          $result = mysql_query($query);
          if(!$result) {
            return false; 
          }
        }
        return true;
      }   
  
      //Here begins the Paypal code as is
      // read the post from PayPal system and add 'cmd'
      $req = 'cmd=_notify-validate';
  
      foreach ($_POST as $key =&gt; $value) {
        $value = urlencode(stripslashes($value));
        $req .= "&amp;$key=$value";
      }
  
      // post back to PayPal system to validate
      $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
      $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
      $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
      $fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30); //&lt;--Here I also tried out the other response to the question linked above and it actually returned a 'Invalid host' into he $res var-
  
      // assign posted variables to local variables
      $item_name = $_POST['item_name'];    //&lt;--These are the two vars for which
      $item_number = $_POST['item_number'];//   Paypal Sandbox posts no value.
      $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 (!$fp) {
        // HTTP ERROR
      } else {
        fputs ($fp, $header . $req);
        while (!feof($fp)) {
          $res = fgets ($fp, 1024);
          if (strcmp ($res, "VERIFIED") == 0) {
          if ($_POST['payment_status'] == 'Completed' &amp;&amp; no_paypal_trans_id($_POST['txn_id']) &amp;&amp; $paypal_email == $_POST['receiver_email'] &amp;&amp; $paypal_currency == $_POST['mc_currency'] &amp;&amp; payment_amount_correct($shipping, $_POST)) {
              // process payment
              create_order($_POST);
            }
          } else if (strcmp ($res, "INVALID") == 0) {
            // log for manual investigation
          }
        }
        fclose ($fp);
      }
  </code></pre>
  
  <p><strong>EDIT 1: Gave it another go: No luck!</strong></p>
  
  <p>I've just tried the original, untouched Paypal code. I've just copied and pasted it into this PHP and then run the Sandbox buy. Same outcome. No response, no data inserted into tables, no way of me knowing somebody has actually bought something off my cart, but Paypal still fake-charges the buy. This supports my suspicion that the error comes in the original code and has nothing to do with the functions I've added. Hope somebody can make sense of this soon. Thousand thanks in advance to that person!</p>
  
  <p><strong>EDIT 2: Following 1st response.</strong></p>
  
  <p>A kind user who has now deleted his/her response was kind enough to give it a first try. He/she recommended that the Paypal documentation mentions <code>https://www.sandbox.paypal.com/cgi-bin/webscr</code> where I have <code>http://www.sandbox.paypal.com</code>. Perhaps I have an older version of the code, so I tried out his/her recommendation. Now I comment what happened:</p>
  
  <p>First of all, thanks a lot for your response. Unfortunately, no luck with it. Having substituted one address with the other, now the process doesn't get to the <code>while</code> loop, it stops at the <code>fsockopen</code>. I placed a <code>Mail</code> command to print out the <code>$errno</code> and <code>$errstr</code> contained in that command. With <code>https://www.sandbox.paypal.com/cgi-bin/webscr</code> substituting <code>http://www.sandbox.paypal.com</code> I get <strong>Unable to find the socket transport "https" - did you forget to enable it when you configured PHP?</strong> So I stripped the "https://" and tried again. Now I get <strong>php_network_getaddresses: getaddrinfo failed: Name or service not known</strong>. Any ideas?</p>
  
  <p><strong>EDIT 3: Following my conversation with @hexacyanide in the comments section:</strong></p>
  
  <pre><code>    $req = cmd=_notify-validate&amp;test_ipn=1&amp;payment_type=instant&amp;payment_date=20%3A55%3A22+Feb+08%2C+2013+PST&amp;payment_status=Completed&amp;address_status=confirmed&amp;payer_status=verified&amp;first_name=John&amp;last_name=Smith&amp;payer_email=buyer%40paypalsandbox.com&amp;payer_id=TESTBUYERID01&amp;address_name=John+Smith&amp;address_country=United+States&amp;address_country_code=US&amp;address_zip=95131&amp;address_state=CA&amp;address_city=San+Jose&amp;address_street=123%2C+any+street&amp;receiver_email=seller%40paypalsandbox.com&amp;receiver_id=TESTSELLERID1&amp;residence_country=US&amp;item_name1=something&amp;item_number1=AK-1234&amp;quantity1=1&amp;tax=2.02&amp;mc_currency=USD&amp;mc_fee=0.44&amp;mc_gross_1=9.34&amp;mc_handling=2.06&amp;mc_handling1=1.67&amp;mc_shipping=3.02&amp;mc_shipping1=1.02&amp;txn_type=cart&amp;txn_id=2229455&amp;notify_version=2.4&amp;custom=xyz123&amp;invoice=abc1234&amp;charset=windows-1252&amp;verify_sign=AsdNkKD2ktCz.aUB.9WYWy-g8MHoAa-TsvSjUgGstseJVdUhQTq3aCwW
  </code></pre>
  
  <p>Sorry, that was <code>$req</code>, not <code>$res</code> var. The <code>$res</code> var is returned by the <code>while</code> loop. I placed a <code>mail</code> command there and I get several E-Mails with the <code>$res</code> values. These are: </p>
  
  <pre><code>    X-Frame-Options: SAMEORIGIN
      HTTP/1.0 200 OK
      Strict-Transport-Security: max-age=14400
      INVALID 
      Content-Type: text/html; charset=UTF-8
      Date: Sat, 09 Feb 2013 05:44:33 GMT
      Connection: close
      Content-Length: 7
  </code></pre>
  
  <p><strong>EDIT 4: Absolutely dumbfounded: Not even simple Paypal IPN code straight from the Paypal Developer website and tested with the Paypal Developer website IPN Tester works...</strong></p>
  
  <ol>
  <li>I've used the Paypal IPN script generator found <a href="https://www.paypaltech.com/SG2/" rel="nofollow noreferrer">here</a>.</li>
  <li>I chose to generate a script that would mail back to me a VALID or INVALID response.</li>
  <li>I copied it in its entirety and pasted it into a PHP file I saved in my server.</li>
  <li>I checked and re-checked that no part of the code was left behind.</li>
  <li>I targeted this PHP file on my server from the IPN Simulator found <a href="https://developer.paypal.com/devscr?cmd=_ipn-link-session" rel="nofollow noreferrer">here</a>.</li>
  <li>Simulator shows a Check and says "<em>IPN successfully sent.</em>"</li>
  <li>I go to check my mail to see what response I got, whether it was VALID or INVALID... I got no response at all. So the IPN reaches the PHP buy at some point the process is broken so that it doesn't even get to the VALID/INVALID functions. However, this is the very code Paypal gives you and is tested with the very sim it has designed for it. So what's going on?</li>
  </ol>
  
  <hr>
  
  <h2>DAY 2 OF THIS ODYSSEY</h2>
  
  <p>O.K. Needless to say: no luck yet. But I've now decided to exhaustively track everything that's going into the PHP code and out to Paypal. The idea is that a response of <code>VALID</code> occurs when this code sends back to Paypal those initial values Paypal inserted into it and in the very same order (this according to Paypal documentation).</p>
  
  <p>The first issue is that I was receiving no response at all from the code. So the supposition was that the code was failing at the very start. It happened that the first issue was that the Sandbox links do no work. (I got to this point with the aid of @hexacyanide, who was very kind to follow along yesterday with my progress or lack thereof. Whenever I get enough reputation to positively mark his/her response, I will.) So I stripped off the <code>sandbox.</code> and, voilà, it was returning a response, but now the response was <code>INVALID</code>.</p>
  
  <p>From that time to now I've understood that IPN is just for seller-side transaction checking and data logging. Paypal doesn't care about the outcome of the IPN. So my cart IS working 100%. A user can buy something and Paypal will charge the user for it. The IPN will only check for me if the buy info is in fact coming from Paypal and all is well there and will then (in my script) send all this info into a DB so I can fulfill my side of the service. So, even though the transaction happens successfully without IPN, IPN would be an invaluable mechanism to facilitate the overall selling process. However, all this last info settles my suspicion that all my cart's code is O.K., and that the issue is ONLY here, with this code, or the way Paypal is handling it. (The fact that Paypal's "virgin" code passed through Paypal's own IPN Simulator returns no response, but the same error at the initial state of not being able to connect to the the Paypal server makes me inclined to think that the issue has to do with something wrong on Paypal's side.)</p>
  
  <p>So, the log of everything going into and out of this PHP. I located <code>Mail</code> commands at every single significant point in the code in order to create checkpoints. Inside the <code>foreach()</code> command I included an incremental variable in order to have and ordered list of the POSTED data coming in. And this is what I got:</p>
  
  <p><strong>This is the data being POSTED into the code by Paypal (in the order it's being POSTED)</strong></p>
  
  <pre><code>    test_ipn=1
      payment_type=instant
      payment_date=08%3A42%3A23+Feb+09%2C+2013+PST
      payment_status=Completed
      payer_status=verified
      first_name=John
      last_name=Smith
      payer_email=buyer%40paypalsandbox.com
      payer_id=TESTBUYERID01
      business=seller%40paypalsandbox.com
      receiver_email=seller%40paypalsandbox.com
      receiver_id=TESTSELLERID1
      residence_country=US
      item_name1=something
      item_number1=AK-1234
      quantity1=1
      tax=2.02
      mc_currency=USD
      mc_fee=0.44
      mc_gross=15.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=23291642
      notify_version=2.4
      custom=xyz123
      invoice=abc1234
      charset=windows-1252
  </code></pre>
  
  <p>At a glance, I seem to be getting all the data I should from Paypal. And this is the order in which PP sends it. So if I were to send this data in this order back and PP actually does check it for this same order, then I should, in theory, get a <code>VALID</code> response.</p>
  
  <p>So what the code does is add "cmd=_notify-validate", which is the only addition (the PP documentation makes it clear that this is necessary addition is necessary), and passes all this data into a var called <code>$req</code> in this way: <code>A=X&amp;B=Y&amp;C=Z...</code>.</p>
  
  <p>Here is the message from the next checkpoint, added just after the <code>foreach()</code> command closes.</p>
  
  <pre><code>    $req = cmd=_notify-validate&amp;test_ipn=1&amp;payment_type=instant&amp;payment_date=08%3A42%3A23+Feb+09%2C+2013+PST&amp;payment_status=Completed&amp;payer_status=verified&amp;first_name=John&amp;last_name=Smith&amp;payer_email=buyer%40paypalsandbox.com&amp;payer_id=TESTBUYERID01&amp;business=seller%40paypalsandbox.com&amp;receiver_email=seller%40paypalsandbox.com&amp;receiver_id=TESTSELLERID1&amp;residence_country=US&amp;item_name1=something&amp;item_number1=AK-1234&amp;quantity1=1&amp;tax=2.02&amp;mc_currency=USD&amp;mc_fee=0.44&amp;mc_gross=15.34&amp;mc_gross_1=12.34&amp;mc_handling=2.06&amp;mc_handling1=1.67&amp;mc_shipping=3.02&amp;mc_shipping1=1.02&amp;txn_type=cart&amp;txn_id=23291642&amp;notify_version=2.4&amp;custom=xyz123&amp;invoice=abc1234&amp;charset=windows-1252&amp;verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb
  </code></pre>
  
  <p><strong>Note</strong> the <code>verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb</code> at the end. I can't account for that piece of data because it was not spit out in the form of an E-Mail during the <code>foreach()</code> loop. So where does it come from? Could this be the culprit piece of data that returns the <code>INVALID</code> response? Open question. But at this point, all this data is passed by the code at the "<em>DATA COMING IN</em>" checkpoint; so if this data is what's coming in and my code sends it back out, then everything should be fine.</p>
  
  <p>So the next checkpoint will be "<em>DATA COMING OUT</em>." I located this checkpoint right before the <code>fputs ($fp, $header . $req);</code> code which writes <code>$req</code> back to PP. And this is what is coming out of the code:</p>
  
  <pre><code>    $req = cmd=_notify-validate&amp;test_ipn=1&amp;payment_type=instant&amp;payment_date=08%3A42%3A23+Feb+09%2C+2013+PST&amp;payment_status=Completed&amp;payer_status=verified&amp;first_name=John&amp;last_name=Smith&amp;payer_email=buyer%40paypalsandbox.com&amp;payer_id=TESTBUYERID01&amp;business=seller%40paypalsandbox.com&amp;receiver_email=seller%40paypalsandbox.com&amp;receiver_id=TESTSELLERID1&amp;residence_country=US&amp;item_name1=something&amp;item_number1=AK-1234&amp;quantity1=1&amp;tax=2.02&amp;mc_currency=USD&amp;mc_fee=0.44&amp;mc_gross=15.34&amp;mc_gross_1=12.34&amp;mc_handling=2.06&amp;mc_handling1=1.67&amp;mc_shipping=3.02&amp;mc_shipping1=1.02&amp;txn_type=cart&amp;txn_id=23291642&amp;notify_version=2.4&amp;custom=xyz123&amp;invoice=abc1234&amp;charset=windows-1252&amp;verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb
  </code></pre>
  
  <p>HUM! Am I missing something? Are my eyes skipping over the tiniest hiccough that makes this code trip? The output seems to be exactly the same as the input. Let's assume that PP worked up their code fine, that the POSTED data is turned into a string in a way that PP expects and, therefore, checks for... assuming all this, what could be wrong?</p>
  
  <p>Let's see... again: open questions:</p>
  
  <ul>
  <li>Could it be an issue with the header? According to documentation, this is the header one should use... so unless they're off on this, then it should work fine.</li>
  <li>Could it be an issue with the URL given? O.K., this IS in fact an issue, or rather WAS at some point. There are at least 6 or 7 different URLs running around the Internet which are supposed to work and not work. Some have worked for some people and not for others, some seem never to work, etc. Not even PP themselves are sure what URL is supposed to be used, as in their own documentation it varies. What is clear is that using the <code>sandbox.</code> URLs will return nothing--one is to suppose that these are broken.</li>
  <li>Could it be an issue with the charset? Could there be an incompatibility? This just occurred to me as I was seeing the data come in and out with Hex codes. The thing is that this should make it compatible, right? However, yesterday, as I was checking what was going on in the <code>while()</code> loop, and catching the info sent back from PP into the <code>$res</code> var I wasn't getting Hex codes, as you can see further up in this <em>testament</em>.</li>
  </ul>
  
  <p>The next checkpoint is the classic one at <code>if()</code> VALID and <code>if()</code> INVALID. And, of course, I get the one that tells me it all came out INVALID, once again.</p>
  
  <p>Hopefully this detailed info I'm giving will make someone's light bulb shine brightly! As for me, I'm still dumbfounded.</p>
  
  <p>Thanks in advance!</p>
  
  <hr>
  
  <p><strong>THANKS A LOT TO BOTH OF YOU FOR YOUR RESPONSES!</strong></p>
  
  <p>I haven't tried the second one out, though it sounds pretty sensible. Sounds like that could have been the issue with the original code I got from PayPal. If I have any time to try it out and see if the code works with it, I'll post my feedback here. Hopefully someone will find all this useful.</p>
  
  <p>After a week banging my head against the walls and a little time off to cool the issue in my brain, I have finally, a couple of days ago, found another way to solve the issue somewhere in the Web which is pretty similar to the second part of Hexacyanide's response. I'm posting it below for anyone who's following this string. However, I must say, <strong>the PayPal code is pretty buggy.</strong> At least the one I received. I have since had several other issues I've had to solve via workarounds. An the worst part is that PayPal has no support AT ALL. I even called three times: the first, the person hung up on me; then, they told me it could be a virus issue (typical response when they don't really know what's going on).</p>
  
  <p>Anyway, I wish I could rate both your responses but I need more than +15 points for that, so I'll praise them verbally here. I tried out Hexacynide's response code as is but it didn't work or me, so I can't mark it as checked. If I have time to try out the other in the future, and it works, I'll mark it as correct.</p>
  
  <p>Until then, here is my code update using CURL.</p>
  
  <pre><code>    $ch = curl_init('https://www.paypal.com/pe/cgi-bin/webscr');
      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'));
  
      if( !($res = curl_exec($ch)) ) {
          // error_log("Got " . curl_error($ch) . " when processing IPN data");
          curl_close($ch);
          exit;
      }
      curl_close($ch);
  </code></pre>
  
  <p><strong>NOTE:</strong> Here it's targeted to the real thing because it's now up and running, however, using it with <code>sandbox.</code> works as well.</p>