Hello, I am currently developing an automated call/response system for use with the PayPal IPN. I have followed the steps described at https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/ and am currently using the PaypalIPN.php provided on the github linked from that site, but every test sent to the PayPal site returns an invalid response (Verify == false in the PayPal IPN). I am copying the code used in both the send request and the listener. The certification is installed in the appropriate file location "cert/cacert.pem" and I can confirm that all the test payments made in the sandbox are being completed appropriately. I have used both the created script that points to the sandbox and the IPN Simulator. The simulator states it made a successful handshake, but the verify is a false return and I receive the email stating such. I have read across multiple help forums that state that the sandbox and the IPN Simulator provide false returns as they are not actual statements, but prior to making this go live, I'd like to make sure it works. Listener.php: <?php namespace Listener; require('PaypalIPN.php'); require('functions.php'); use PaypalIPN; $ipn = new PaypalIPN(); // Use the sandbox endpoint during testing. //Set to false for live $enable_sandbox = true; $useLocalCerts = TRUE; if($enable_sandbox) { $ipn->useSandbox(); } if(!$useLocalCerts) { $ipn->usePHPCerts(); } $verified = $ipn->verifyIPN(); if ($verified) { mail("MYEMAILADDRESS", "Testing Paypal IPN","Succeeded verification","From: " . "MYDOMAINMAILER"); } else { mail("MYEMAILADDRESS", "Testing Paypal IPN","Failed to verify","From: " . "MYDOMAINMAILER"); } header("HTTP/1.1 200 OK"); The variables being sent to https://www.sandbox.paypal.com/cgi-bin/webscr are: cmd: _xclick no_note: 1 currency_code: USD lc: US bn: PP-BuyNowBF:btn_buynow_LG.gif:NonHostedGuest rm: 2 no_shipping: 1 custom: ########)(####### test_ipn: 1 The PayPalIPN.php file copied from Github: <?php class PaypalIPN { /** @Var bool Indicates if the sandbox endpoint is used. */ private $use_sandbox = false; /** @Var bool Indicates if the local certificates are used. */ private $use_local_certs = true; /** 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->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->use_local_certs = false; } /** * Determine endpoint to post the verification data to. * * @RETURN string */ public function getPaypalUri() { if ($this->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('&', $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 => $value) { if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) { $value = urlencode(stripslashes($value)); } else { $value = urlencode($value); } $req .= "&$key=$value"; } // Post the data back to PayPal, using curl. Throw exceptions if errors occur. $ch = curl_init($this->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->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( 'User-Agent: PHP-IPN-Verification-Script', '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; } } }
... View more