Paypal ipn not verifying: INVALID

I have set up paypal ipn, the handshake works: I am able to go and pay using paypal. But, weirldy it does not return as verified and comes back as invalid.

Payment has gone through too?

The code for paypal button:

<form action="" name="gateway_form" method="post">
                <input type="hidden" value="2" name="rm">
                <input type="hidden" value="_xclick" name="cmd">

                <input type="hidden" value="<?=base_url()?>stationery/paypal_accepted" name="return">
                <input type="hidden" value="<?=base_url()?>stationery/paypal_cancelled" name="cancel_return">

                <input type="hidden" value="<?=base_url()?>stationery/paypal_ipn" name="notify_url">

                <input type="hidden" value="" name="business">

                <input type="hidden" value="GBP" name="currency_code">

                <input type="hidden" value="<?=$basket_total?>" name="amount">

                <input type="hidden" value="John Browns Stationery Order" name="item_name">

                <input type="hidden" value="<?=$random_invoice_no?>" name="invoice">
                <input type="hidden" value="<?=$user_id?>" name="custom">

                <input class="btn btn-default" type="submit" value="Checkout by Paypal">

The ipn listener code:

 *  PayPal IPN Listener
 *  A class to listen for and handle Instant Payment Notifications (IPN) from 
 *  the PayPal server.
 *  @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 = false;     

     *  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 is used for the
     *  post back. If false, the live URI is used. Default false.
     *  @var boolean
    public $use_sandbox = false; 

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

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

    const PAYPAL_HOST = '';
//    const SANDBOX_HOST = '';
    const SANDBOX_HOST = '';

     *  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, 
        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, 3);

        $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); 


    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://"
     *  @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
            header('Allow: POST', true, 405);
            throw new Exception("Invalid HTTP request method.");

And the ipn handler code:

function paypal_ipn(){

        * PHP-PayPal-IPN Example
        * This shows a basic example of how to use the IpnListener() PHP class to
        * implement a PayPal Instant Payment Notification (IPN) listener script.
        * For a more in depth tutorial, see my blog post:
        * This code is available at github:
        * @package PHP-PayPal-IPN
        * @author Micah Carrick
        * @copyright (c) 2011 - Micah Carrick
        * @license
        Since this script is executed on the back end between the PayPal server and this
        script, you will want to log errors to a file or email. Do not try to use echo
        or print--it will not work!
        Here I am turning on PHP error logging to a file called "ipn_errors.log". Make
        sure your web server has permissions to write to that file. In a production
        environment it is better to have that log file outside of the web root.

        ini_set('log_errors', true);
        ini_set('error_log', base_url() . 'add_ons/ipn/ipn_errors.log');
        // instantiate the IpnListener class
        $listener = new IpnListener();
        When you are testing your IPN script you should be using a PayPal "Sandbox"
        When you are ready to go live change use_sandbox to false.
        $listener->use_sandbox = false;
        By default the IpnListener object is going going to post the data back to PayPal
        using cURL over a secure SSL connection. This is the recommended way to post
        the data back, however, some people may have connections problems using this
        To post over standard HTTP connection, use:
        $listener->use_ssl = false;
        To post using the fsockopen() function rather than cURL, use:
        $listener->use_curl = false;
        The processIpn() method will encode the POST variables sent by PayPal and then
        POST them back to the PayPal server. An exception will be thrown if there is
        a fatal error (cannot connect, your server is not configured properly, etc.).
        Use a try/catch block to catch these fatal errors and log to the ipn_errors.log
        file we setup at the top of this file.
        The processIpn() method will send the raw data on 'php://input' to PayPal. You
        can optionally pass the data to processIpn() yourself:
        $verified = $listener->processIpn($my_post_data);
        try {
        $verified = $listener->processIpn();
        mail('', 'what is the verified value?', $verified);

        } catch (Exception $e) {
        mail('', 'error', $e->getMessage());

        The processIpn() method returned true if the IPN was "VERIFIED" and false if it
        was "INVALID".

        if ($verified) {
        Once you have a verified IPN you need to do a few more checks on the POST
        fields--typically against data you stored in your database during when the
        end user made a purchase (such as in the "success" page on a web payments
        standard button). The fields PayPal recommends checking are:
        1. Check the $_POST['payment_status'] is "Completed"
        2. Check that $_POST['txn_id'] has not been previously processed
        3. Check that $_POST['receiver_email'] is your Primary PayPal email
        4. Check that $_POST['payment_amount'] and $_POST['payment_currency']
        are correct
        Since implementations on this varies, I will leave these checks out of this
        example and just send an email using the getTextReport() method to get all
        of the details about the IPN.

        foreach($this->input->post() as $key => $value){
            $data[$key] = $value;


        mail('', 'Verified IPN', $listener->getTextReport());
        } else {
        An Invalid IPN *may* be caused by a fraudulent transaction attempt. It's
        a good idea to have a developer or sys admin manually investigate any
        invalid IPN.
        mail('', 'IPN is invalid', $listener->getTextReport());



I have created a text report:

HTTP/1.1 200 OK
Server: Apache
X-Frame-Options: SAMEORIGIN
Paypal-Debug-Id: f12b436ba34fc
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=UTF-8
Date: Tue, 15 Mar 2016 11:35:50 GMT
Content-Length: 7
Connection: close
Set-Cookie: cwrClyrK4LoCV1fydGbAxiNL6iG=AJGAy4asx-VKRgNsi6tOcBzPvUpJE9YTQbT7LjGvLd-hcG94uHT60TPzfOxn7psnIrKUTpm7Uhx1EuFm_dN5UmgmCIBhkbYG3UFQLQ0kAyJNq-4F2YHhtPqLlJJ-nAPKKNHp4-9na-9KedRj8tKnSadxd-rkQffpEdJ3a-olWxDgCXbiCS3m8GUxs9whYlM4j9fC44XtdKa6tVAEyxnlXvTZT_tPjsmRusPIwe508NVz8mOiTYDIjWWgYTcLgb6N6__WHncNHihk4vD-VA3PVWhBIH4W0Zku-29-r2vcHZILfLlowv-b3s6jqu4HHdsTV1vkBTXcRFHQKeHl6INVuZ8AUa10N30il814CCVMQrMMf9oHtoQ2HLDls2g24vFuiQa1w5U-R-v_MhPsJj5Sgdtz8t5K2Dl3Wtp_-h8b2MEI8Sn-YzrdtpfYzy8;; path=/; Secure; HttpOnly
Set-Cookie: cookie_check=yes; expires=Fri, 13-Mar-2026 11:35:49 GMT;; path=/; Secure; HttpOnly
Set-Cookie: navcmd=_notify-validate;; path=/; Secure; HttpOnly
Set-Cookie: navlns=0.0; expires=Thu, 15-Mar-2018 11:35:49 GMT;; path=/; Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=name%3DLIVE11.WEB.1%26silo_version%3D880%26app%3Dappdispatcher%26TIME%3D2515789654;; path=/; Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Set-Cookie:; expires=Tue, 15-Mar-2016 12:05:50 GMT; path=/; secure
Strict-Transport-Security: max-age=63072000

mc_gross                 30.60
invoice                  pDz0Nun6H29xdGuI
protection_eligibility   Ineligible
address_status           confirmed
payer_id                 9SU8DB98AZDKU
tax                      0.00
address_street           1 Main Terrace
payment_date             04:35:46 Mar 15, 2016 PDT
payment_status           Pending
charset                  windows-1252
address_zip              W12 4LQ
first_name               Vish
address_country_code     GB
address_name             Vish Patel's Test Store
notify_version           3.8
custom                   501
payer_status             verified
address_country          United Kingdom
address_city             Wolverhampton
quantity                 1
verify_sign              AzFmRN2alSXsVcRQ-bkJDjsW0iFTAt2a-oBGJoFYjl.qfx0QiRc9VT6j
txn_id                   37U96690JH140125B
payment_type             instant
payer_business_name      Vish Patel's Test Store
last_name                Patel
address_state            West Midlands
pending_reason           unilateral
txn_type                 web_accept
item_name                ipn test Order
mc_currency              GBP
residence_country        GB
test_ipn                 1
handling_amount          0.00
shipping                 0.00
ipn_track_id             d243782521d47 

Also, looking at the ipn history log, worryingly no http status code, its blank and status shows disabled.


The merchant account accidentally had receive ipn messages turned off. So, logging into this account and enabling this option go it working.

Need Your Help

Prevent streaming play stop when screen goes off Kitkat

android streaming android-4.4-kitkat playing

We are streaming to play sound in App but due to Kill/Draining battery feature in Android Kitkat 4 currently playing is stopped when screen goes off. How to overcome this or handle this and keep pl...