PayPal IPN - Sandbox and IPN Simulator - Never successfully verifies

Evil_Chippy
Contributor
Contributor

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

Login to Me Too
1 REPLY 1

MTS_Justin
Moderator
Moderator
Hello,

The IPN simulator provides a method to test your IPN listeners ability to receive IPN data, however it will not allow you to check if the transactional data is valid, as the IPN simulator uses fake transaction data, which is why you're receiving an "INVALID" response.

You would need to setup a sandbox account and implement your IPN listener within the account or within the transaction requests and then postback the exact data as it's received to the sandbox IPN postback URL (https://ipnpb.sandbox.paypal.com) to confirm validation of the transactional data.

https://developer.paypal.com/docs/classic/ipn/ht_ipn/

Was my post helpful? If so, please give me a kudos!
Login to Me Too

Haven't Found your Answer?

It happens. Hit the "Login to Ask the community" button to create a question for the PayPal community.