title: PayPal IPN Sample code not working
body: <p>I am just having a little trouble with the PayPal IPN. I used their <a href="https://github.com/paypal/ipn-code-samples" rel="nofollow noreferrer">sample code from GitHub</a>. However when I test it in the IPN Simulator, it just says "IPN was not sent, and the handshake was not verified. Review your information."</p>
  
  <p>In the paypalIPN.php, I made <em>$use_local_certs</em> equal <em>false</em> for now as I didn't implement them. </p>
  
  <p>In the routes.php, I commented out the sandbox line for now.</p>
  
  <p>I also did some testing with the server. And everything looks fine. I think I'm just missing something and I just can't put my finger on it.</p>
  
  <blockquote>
    <p>New, TLSv1/SSLv3, Cipher is AES256-SHA256<br>
    Server public key is 2048 bit<br>
    Secure Renegotiation IS supported<br>
    Compression: NONE<br>
    Expansion: NONE<br>
    SSL-Session:<br>
        Protocol  : TLSv1.2<br>
        Cipher    : AES256-SHA256<br>
        Session-ID: 1EE4015F32E83402862D7C1618B15C0AF9826CAB32B642D05C177EAEEA41AC1B<br>
        Session-ID-ctx: <br>
        Master-Key: 7C9C9915C2D933F5E28F566F639BD72E3B591037EE22C6801CBBEB4AFD90784E32BA0DE5989FD9EBA81911589CCE87D3<br>
        Key-Arg   : None<br>
        PSK identity: None<br>
        PSK identity hint: None<br>
        SRP username: None<br>
        Start Time: 1501993978<br>
        Timeout   : 300 (sec)<br>
        Verify return code: 0 (ok)<br></p>
  </blockquote>
  
  <p>Here is the routes.php</p>
  
  <pre><code>&lt;?php namespace Listener;
  require('PaypalIPN.php');
  use PaypalIPN;
  $ipn = new PaypalIPN();
  // Use the sandbox endpoint during testing.
  //$ipn-&gt;useSandbox();
  $verified = $ipn-&gt;verifyIPN();
  if ($verified) {
      /*
       * Process IPN
       * A list of variables is available here:
       * https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/
       */
      require ('../database.php');
      $sql = "INSERT INTO sales (firstName) values (?)";
      $stmt = $mysqli-&gt;prepare($sql);
      $stmt-&gt;bind_param("s", "HELLO HUMANS");
      $stmt-&gt;execute();
      $stmt-&gt;store_results();
      $stmt-&gt;close();
      $mysqli-&gt;close();
  }
  // Reply with an empty 200 response to indicate to paypal the IPN was received correctly.
  header("HTTP/1.1 200 OK");
  </code></pre>
  
  <p>And here is the paypalIPN.php</p>
  
  <pre><code>&lt;?php
  class PaypalIPN
  {
      /**
       * @var bool $use_sandbox     Indicates if the sandbox endpoint is used.
       */
      private $use_sandbox = false;
      /**
       * @var bool $use_local_certs Indicates if the local certificates are used.
       */
      private $use_local_certs = false;
      /** Production Postback URL */
      const VERIFY_URI = 'https://ipnpb.paypal.com/cgi-bin/webscr';
      /** Sandbox Postback URL */
      const SANDBOX_VERIFY_URI = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr';
      /** Response from PayPal indicating validation was successful */
      const VALID = 'VERIFIED';
      /** Response from PayPal indicating validation failed */
      const INVALID = 'INVALID';
      /**
       * Sets the IPN verification to sandbox mode (for use when testing,
       * should not be enabled in production).
       * @return void
       */
      public function useSandbox()
      {
          $this-&gt;use_sandbox = true;
      }
      /**
       * Sets curl to use php curl's built in certs (may be required in some
       * environments).
       * @return void
       */
      public function usePHPCerts()
      {
          $this-&gt;use_local_certs = false;
      }
      /**
       * Determine endpoint to post the verification data to.
       * @return string
       */
      public function getPaypalUri()
      {
          if ($this-&gt;use_sandbox) {
              return self::SANDBOX_VERIFY_URI;
          } else {
              return self::VERIFY_URI;
          }
      }
      /**
       * Verification Function
       * Sends the incoming post data back to PayPal using the cURL library.
       *
       * @return bool
       * @throws Exception
       */
      public function verifyIPN()
      {
          if ( ! count($_POST)) {
              throw new Exception("Missing POST Data");
          }
          $raw_post_data = file_get_contents('php://input');
          $raw_post_array = explode('&amp;', $raw_post_data);
          $myPost = array();
          foreach ($raw_post_array as $keyval) {
              $keyval = explode('=', $keyval);
              if (count($keyval) == 2) {
                  // Since we do not want the plus in the datetime string to be encoded to a space, we manually encode it.
                  if ($keyval[0] === 'payment_date') {
                      if (substr_count($keyval[1], '+') === 1) {
                          $keyval[1] = str_replace('+', '%2B', $keyval[1]);
                      }
                  }
                  $myPost[$keyval[0]] = urldecode($keyval[1]);
              }
          }
          // Build the body of the verification post request, adding the _notify-validate command.
          $req = 'cmd=_notify-validate';
          $get_magic_quotes_exists = false;
          if (function_exists('get_magic_quotes_gpc')) {
              $get_magic_quotes_exists = true;
          }
          foreach ($myPost as $key =&gt; $value) {
              if ($get_magic_quotes_exists == true &amp;&amp; get_magic_quotes_gpc() == 1) {
                  $value = urlencode(stripslashes($value));
              } else {
                  $value = urlencode($value);
              }
              $req .= "&amp;$key=$value";
          }
          // Post the data back to PayPal, using curl. Throw exceptions if errors occur.
          $ch = curl_init($this-&gt;getPaypalUri());
          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_SSLVERSION, 6);
          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
          curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
          // This is often required if the server is missing a global cert bundle, or is using an outdated one.
          if ($this-&gt;use_local_certs) {
              curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/cert/cacert.pem");
          }
          curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
          curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
          curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
          $res = curl_exec($ch);
          if ( ! ($res)) {
              $errno = curl_errno($ch);
              $errstr = curl_error($ch);
              curl_close($ch);
              throw new Exception("cURL error: [$errno] $errstr");
          }
          $info = curl_getinfo($ch);
          $http_code = $info['http_code'];
          if ($http_code != 200) {
              throw new Exception("PayPal responded with http code $http_code");
          }
          curl_close($ch);
          // Check if PayPal verifies the IPN data, and if so, return true.
          if ($res == self::VALID) {
              return true;
          } else {
              return false;
          }
      }
  }
  </code></pre>