Search code examples
paypal-ipn

Any ideas why my PayPal IPN Listener spontaneously started throwing errors?


As of Saturday, I'm suddenly getting "cURL error: [60] SSL certificate problem: unable to get local issuer certificate" when my PayPal IPN fires. I haven't changed any code on my site in months, and to my knowledge, I'm not running any auto-upgrades that would interfere.

I've read dozens of Stack posts, and nothing in them has worked. I've moved the .crt to other directories and updated cURLOPT_CAINFO. Restarted httpd and cpanel. Listed the cert in php.ini. (It was not there before this started, so I doubt that's it.) Downloaded and installed .pem files, which I think isn't even relevant to this. None of it has worked.

PayPal has verified that the IPN messages are being sent, and the PayPal Sandbox verifies a handshake successfully. There is a regular ol' SSL on the website, and it's operating correctly.

Server is GoDaddy VPS, CentOs Linux 7 with Apache 2.4.41.

I am not willing to turn off VERIFYPEER or operate in an insecure setup.

My code:

protected function curlPost($encoded_data)
    {
        $uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';
        $this->post_uri = $uri;

        $ch = curl_init();

        if ($this->verify_ssl) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
            curl_setopt($ch, CURLOPT_CAINFO, '/home/shopusa/public_html/app/Support/cert/api_cert_chain.crt');
        }

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

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

        return $this->response;
    }

The file is here, according to Terminal, and it is owned by the web user:

[root@ip-###-###-###-## cert]# cd /home/shopusa/public_html/app/Support/cert/
[root@ip-###-###-###-## cert]# ls
api_cert_chain.crt  _notes

All permissions are 755/directory and 644/file.

Does anything jump out at anyone? I've spent about eight hours chasing this down, and I'm out of ideas for how to get the IPN listener firing again.


Solution

  • Replace api_cert_chain.crt with an updated certificate authority bundle that is able to verify the issuer of the PayPal servers' current, cryptographically secure certificates.

    Perhaps from https://curl.haxx.se/docs/caextract.html (convert the pem to crt if necessary, though I don't think it is)