Introduction to IPN

PayPal’s Instant Payment Notification (IPN) system is an extremely valuable tool that will automatically send transaction data to a “listener” application on a web server. This application can process the data in any way necessary, such as updating back-end databases, sending out custom email notifications, activating or suspending subscription profiles, and much more.   It can be utilized with all of PayPal’s payment methods whether you’re processing payments from a web site, a mobile device, or native software applications, and allows you to process data in real-time. 

If you’d like a more in-depth review of Instant Payment Notification please refer to PayPal’s documentation as it explains everything you need to know about configuring an IPN listener and validating the transaction with PayPal to ensure its legitimacy.

One Small Caveat

PayPal provides two simple ways to enable IPN and set the URL to which you would like transaction data to be sent.  First, merchants can enable IPN via the PayPal account profile under Instant Payment Notifications Preferences. 

PayPal IPN Settings

Also, one may utilize the NOTIFYURL parameter in standard checkout code or API calls to override any PayPal profile setting. 

The latter provides the ability to specify various IPN URL’s for any given checkout or API call; however, it does not provide the ability to specify more than one URL for IPN to send data to.  There are many instances, indeed, where such functionality would be appreciated, and I’d like to discuss some of the reasons this might be necessary and throw another option on the table during planning and development.

Technical Details

I’m going to provide some samples of use-cases where this technique comes in handy, but beforehand I’d like to dive into the technical details of what makes it all work.  I typically work with PHP, so that’s what I’ll use here.

As you should know at this point, IPN sends POST data to a listener script sitting on your server.  If you’d like to forward this data on to an additional IPN listener all you need to do is send your POST of that same data onto the URL of your choice.  It’s exactly the same thing as sending HTTP POST requests to make API calls except that you don’t really need to worry about processing a response of any kind. 

Here is a very simple sample of an IPN listener script that forwards data to a secondary listener and proceeds accordingly.

// Curl function to send POST data to a server.
function curl_request($url, $req, $ssl)
{
	$curl_result=$curl_err='';

	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL,$url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
	curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
	curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded", "Content-Length: ".strlen($req)));
	curl_setopt($ch, CURLOPT_HEADER , 0);
	curl_setopt($ch, CURLOPT_VERBOSE, 1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $ssl);
	curl_setopt($ch, CURLOPT_TIMEOUT, 90);

	$curl_result = curl_exec($ch);
	$curl_err = curl_error($ch);
	curl_close($ch);

	return $curl_result;
}
/////////////////////////////////////////////////////

// Set config fields
$sandbox = isset($_POST['test_ipn']) ? true : false;
$ppHost = $sandbox ? 'www.sandbox.paypal.com' : 'www.paypal.com';
$ssl = $_SERVER['SERVER_PORT'] == '443' ? true : false;

// Loop through POST data and generate NVP string to return back to PayPal (or forward on to secondary listeners)
$req = '';
foreach ($_POST as $key => $value)
$req .= "&$key.=".urlencode(stripslashes($value));

// Forward IPN data to secondary IPN listener
curl_request('http://www.domain.com/paypal/ipn/ipn-listener.php', $req, $ssl);
curl_request('http://www.otherdomain.com/paypal/ipn/ipn-listener.php', $req, $ssl);
curl_request('http://www.yetanotherdomain.com/paypal/ipn/ipn-listener.php', $req, $ssl);

// Validate with PayPal using CURL is much the same way we forwarded data to our secondary listener.
$req = 'cmd=_notify-validate'.$req;
$curl_result = curl_request($ppHost.'/cgi-bin/webscr', $req, $ssl);
$valid = strpos($curl_result, "VERIFIED")!==false ? true : false;

// Process accordingly
if($valid)
{
	// Process valid IPN
}
else
{
	// Process invalid IPN
}

The functionality mainly resolves in the curl_request function.  By simply passing in the URL you’d like to pass data to, the request string of data itself, and an option for whether or not SSL should be enabled you can forward the data on to any other URL you’d like.  You’ll notice in the sample I’ve provided 3 additional IPN listeners so the load will essentially be split across 4 different servers and each will be dedicated to its own set of tasks.

One thing to consider is that I’ve handled the validation with PayPal after I’ve already forwarded the data on to the next IPN listener.  This works out fine because the secondary listener also validates the data with PayPal directly. 

Another option would be to validate once in your original listener and then include a validation flag with the POST data to your secondary listeners.  This would allow you to only validate a single time across all servers, however, it would leave the possibility open that somebody could POST bogus data to your listener and it would still be validated.

Sample Scenarios for Daisy-Chaining IPNs

Over the years I’ve run into quite a few situations where daisy-chaining IPN’s was necessary to accomplish the overall goal or potentially solve bottlenecks in data processing tasks.  I’d like to provide a basic overview of some of these cases so you have an idea of the different ways this technique can be applied.

IPN Already in Use

I have had many clients call me up with the goal of automating back-end tasks like updating their own database or sending out custom email notifications.  After discussing their options and deciding on IPN as a solution to their problem we find out that they’re already using IPN with a third party shopping cart system of some sort.  In most cases the shopping cart system is using NOTIFYURL so setting the PayPal profile doesn’t help.  If the merchant disables IPN within their shopping cart, though, the actions that were taking place within that shopping cart solution via IPN will no longer occur.  This, of course, is not typically a welcomed change of events.  This problem could be solved one of two ways. 

  • If you have access to the shopping cart code you can customize the integrated IPN solution so that it forwards all of the IPN data on to your own additional listener.  In this case you would leave IPN enabled within the cart software but add the custom code to handle the forward.
  • If you’re using a hosted shopping cart service you may not have access to the code.  In such cases a simple work-around can solve your problem.
    • Disable the IPN feature in your shopping cart control panel.  This will stop NOTIFYURL from being used in most cases and allow your PayPal profile settings to take effect.
      NOTE
      Keep in mind that not ALL shopping carts are developed this way.  Consult your cart documentation for more information.
    • Add the IPN data forwarding code into your own IPN listener solution so that it forwards the data on to your shopping cart’s solution.  This way your IPN code will run on its own and handle its processing as expected.  It will also forward the same data to your shopping cart’s IPN URL which will process the data accordingly even though it was technically disabled within its control panel.

Splitting up IPN Tasks to Avoid Bottlenecks

There are times when merchants would like to accomplish a large number of tasks within an IPN solution.  Depending on the types of databases and web services you’re working with this can become a tough thing to develop, test, troubleshoot, and maintain.  I’ve found that splitting up IPN tasks between different listeners can help.
For example, one particular client of mine uses FileMaker to manage all of their customers, eBay auctions, inventory, invoices, and much more.  They came to me with the following goals.

  • Automatically add new records in FileMaker when payments are made.  This includes customer data, eBay auction data (where applicable), invoices and inventory updates.
  • Automatically send customized email notifications to buyers.
  • Automatically post a message on their Facebook/Twitter wall each time an item sells.
  • Automatically end eBay item if it sells on the web site.

Each of these tasks could be handled within one, long IPN listener script, and at first, that’s exactly what I did.  Everything was working great except for one problem.

FileMaker provides a number of different ways to connect directly to it from web applications; however, they are all notoriously slow and difficult to work with.  What wound up happening was I was getting lots of timeout errors back from FileMaker which were causing my IPN script to fail and not complete all of its tasks.  As such, PayPal’s system received a bad response from my IPN listener and continued posting the same data to the listener.  As you can imagine, this caused all sorts of unexpected results.

If I tried to handle all of the FileMaker connections first and then do everything else the timeouts would occur and the events that followed the FileMaker interaction would not occur.  If I tried to handle the FileMaker connections at the end of the IPN listener and a timeout occurred PayPal would send duplicate IPN’s and the tasks within the solution would occur multiple times.  Neither of these scenarios is good. 

I was able to solve the dilemma by separating the FileMaker tasks from everything else.  Within PayPal, I set the IPN URL to point to a listener that handled the custom emails, Facebook/Twitter interaction, and eBay interaction.  I then forwarded the IPN data to a separate listener that handled all of the FileMaker interaction.  A few basic “check-points” within the first IPN listener allowed me to avoid duplicate actions while making it easy to test and separate code processes at the same time.

Conclusion

PayPal may only provide a single IPN URL for any given transaction, but don’t let that stop you from forwarding that data on to a number of additional IPN URL’s to suit your needs.  Daisy-chaining IPN’s can help lessen the load on your server and also help with initial development and maintenance.  It can also be fun to see just how many data POSTs you can send across your chain!

Need Additional Help?

Schedule a live meeting with Drew Angell, PayPal Certified Developer, and get all of your questions or concerns answered.