How to Test and Troubleshoot PayPal IPN

One of the questions I most often see asked on forums like StackOverflow and Experts Exchange is regarding PayPal Instant Payment Notification (IPN) and how to test it thoroughly. It can be a little bit tricky because the result of the script you are running does not happen on screen. Therefore, troubleshooting and debugging can be difficult.

I’ve been following this tried and true method for developing IPN solutions for years now, and it’s allowed me to launch IPN for hundreds of clients confidently and successfully.

Step 1 - Local Testing

With an IPN script ready to go (or so I think), the first thing I like to do is run some local tests. This is done by building a very simple HTML form with hidden fields that match what you might expect to get from a particular IPN.

Use Google to find a sample of PayPal IPN data for a particular transaction type that you’d like to test. Then, build your HTML form to produce the same thing. For example, here is a basic form that would simulate a “web_accept” IPN from PayPal.

<form action="" method="POST">
	<input name="mc_gross" type="hidden" value="500.00" />
	<input name="custom" type="hidden" value="some custom data" />
	<input name="address_status" type="hidden" value="confirmed" />
	<input name="item_number1" type="hidden" value="6" />
	<input name="item_number2" type="hidden" value="4" />
	<input name="payer_id" type="hidden" value="FW5W7ZUC3T4KL" />
	<input name="tax" type="hidden" value="0.00" />
	<input name="address_street" type="hidden" value="1234 Rock Road" />
	<input name="payment_date" type="hidden" value="14:55 15 Jan 07 2005 PST" />
	<input name="payment_status" type="hidden" value="Completed" />
	<input name="address_zip" type="hidden" value="12345" />
	<input name="mc_shipping" type="hidden" value="0.00" />
	<input name="mc_handling" type="hidden" value="0.00" />
	<input name="first_name" type="hidden" value="Jason" />
	<input name="last_name" type="hidden" value="Anderson" />
	<input name="mc_fee" type="hidden" value="0.02" />
	<input name="address_name" type="hidden" value="Jason Anderson" />
	<input name="notify_version" type="hidden" value="1.6" />
	<input name="payer_status" type="hidden" value="verified" />
	<input name="business" type="hidden" value="" />
	<input name="address_country" type="hidden" value="United States" />
	<input name="num_cart_items" type="hidden" value="2" />
	<input name="mc_handling1" type="hidden" value="0.00" />
	<input name="mc_handling2" type="hidden" value="0.00" />
	<input name="address_city" type="hidden" value="Los Angeles" />
	<input name="verify_sign" type="hidden" value="AlUbUcinRR5pIo2KwP4xjo9OxxHMAi6.s6AES.4Z6C65yv1Ob2eNqrHm" />
	<input name="mc_shipping1" type="hidden" value="0.00" />
	<input name="mc_shipping2" type="hidden" value="0.00" />
	<input name="tax1" type="hidden" value="0.00" />
	<input name="tax2" type="hidden" value="0.00" />
	<input name="txn_id" type="hidden" value="TESTER" />
	<input name="payment_type" type="hidden" value="instant" />
	<input name="last_name=Borduin" type="hidden" />
	<input name="payer_email" type="hidden" value="" />
	<input name="item_name1" type="hidden" value="Rubber+clog" />
	<input name="address_state" type="hidden" value="CA" />
	<input name="payment_fee" type="hidden" value="0.02" />
	<input name="item_name2" type="hidden" value="Roman sandal" />
	<input name="invoice" type="hidden" value="123456" />
	<input name="quantity" type="hidden" value="1" />
	<input name="quantity1" type="hidden" value="1" />
	<input name="receiver_id" type="hidden" value="5HRS8SCK9NSJ2" />
	<input name="quantity2" type="hidden" value="1" />
	<input name="txn_type" type="hidden" value="web_accept" />
	<input name="mc_gross_1" type="hidden" value="0.01" />
	<input name="mc_currency" type="hidden" value="USD" />
	<input name="mc_gross_2" type="hidden" value="0.01" />
	<input name="payment_gross" type="hidden" value="0.02" />
	<input name="subscr_id" type="hidden" value="PP-1234" />
	<input name="test" type="submit" value="test" />


You might use completely bogus data, or you could use data that resides in your database if you’re testing things like updating your database or sending out email notifications based on specific invoice numbers.

Notice that the action of the form is set to the URL you would configure for PayPal to hit for IPN. This way, you can load the form in a browser and submit it directly, which will display the result on screen including any errors that may occur. This can help to find any problems with the script and eliminate them early in the process.

Keep in mind that when testing this way the data is not coming from PayPal’s server. As such, the IPN verification process will come back as invalid. You may need to adjust for this in your IPN solution depending on the logic you have in place for handling verified or invalid IPN’s.

I would recommend setting up local form simulators for various types of transaction types and also test with different data that has special characters or other unique aspects to ensure you’ve covered all your bases. Once you’re able to hit your IPN script from a local simulator without failure, you’re ready for step 2.

Step 2 - PayPal IPN Simulator

PayPal provides their IPN simulator which is essentially the same thing we put together in step 1 here. The only difference is that this time the data is coming from PayPal’s sandbox server. As such, it will validate correctly with PayPal and the IPN would come back as verified if you have your IPN script configured correctly, so that’s a good thing to test for at this point. Just remember that your IPN script needs to validate against PayPal’s sandbox at this point, not their live server, so update your script accordingly.

On that note, PayPal’s sandbox server includes a parameter called “test_ipn” that will be sent with a value of 1 when the IPN comes from PayPal’s sandbox. You can check for this in your IPN script and set the PayPal URL’s for verification based on that so you don’t have to remember to switch it back and forth.

When using the simulator, you should be able to post the test and see everything that your IPN script is supposed to do happen just like an actual transaction had occurred. Once you’re happy with these results, you’re ready to move on to step 3.

Step 3 - Actual Sandbox Transaction Testing

This step could be considered optional, for the sake of thorough testing, I’ll say it’s not.

Create an account at if you haven’t already done so, and if that’s the case, then you’ll need to go ahead and create at least 1 sandbox seller account and 1 sandbox buyer account. These accounts act just like real PayPal accounts except they work with instead of, and of course all the money moved around on the sandbox is fake.

Once you have these accounts created, you can setup the seller account using a Standard Button or API calls just like you would a live seller account. This allows you to fully complete a transaction from beginning to end so you can simulate the entire process as it will happen live.

You’ll use your buyer sandbox account to login and pay for the transaction, and when this happens, the PayPal sandbox will trigger the IPN the same way the live account would. Of course, you’ll need to make sure you’ve configured IPN in your sandbox seller account just like you would a live account, too, so that it functions as expected.

This is the essential part of testing in my opinion. Successful tests here will provide absolute confidence that my IPN solution is ready to rock!

Step 4 - Deployment

At this point, all you need to do is make sure your IPN script is ready for live processing and that you have IPN enabled in your live PayPal account. Then, release the hounds!

Tips for Troubleshooting PayPal IPN Failures
  • Check your IPN History in your PayPal account. This will allow you to confirm whether or not PayPal is actually sending IPN’s to your listener, and if so, what response code it’s getting back from your server. A 200 OK response means it worked as expected. Anything else means it failed, and PayPal’s server would continue to re-try until it gets a 200 OK.
  • Check your web server logs. Apache, IIS, etc. have their own server logs that provide the same information you would see on screen if you ran into the problem in a client/browser. If PayPal IPN history is showing that it’s sending data but a failure is occurring, your web server logs can help you diagnose the problem.

PayPal IPN for WordPress

If you are using WordPress be sure to check out our PayPal IPN WordPress plugin.  It logs all of your IPN data, provides shortcodes for easy display on page/post content, and provides a wide variety of developer hooks so that you can easily build your own plugins around it.

Need Additional Help?

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