PHP: Testing IPN in PayPal Sandbox

I just completed a website where the client uses PayPal to handle the payment transaction. I have used PayPal quite a bit in the past, but I have always had problems using IPN (Instant Payment Notification). Because of the problems, I usually opted not to use IPN, but I was determined to use it for this project. The primary problem with testing IPN is, because the transaction occurs in the background, it is difficult to figure out where the point of failure is. But that is all behind me now.

First sign up for a developer account in PayPal’s Sandbox. The Sandbox is a test environment that can be used to test PayPal transactions without having to use a real credit card on a live site.

Once you log into PayPal’s Sandbox, you should create two test accounts (1 buyer and 1 seller).

PayPal Script

Next, you have to make changes to your PayPal script. In this case, I am using a CodeIgniter PayPal Library. It is based upon Micah Carrick’s PayPal IPN Integration class, so if you don’t use CodeIgniter, you can use Micah’s script instead. You can also get a PayPal script from here.

If you are using CodeIgniter PayPal Library, open the PayPal Controller. If you are using Micah Carrick’s Class or any other script, look for the code that adds the business. You want to add the seller’s test account that you added in the sandbox:

$this->paypal_lib->add_field('business', '');

You also want to make sure that the notify_url field is set. The IPN will not work without it. The notify_url field tells PayPal where to send the data back to. You can also set this in your PayPal profile.

Next you want to change the PayPal post URL. In the CI Library, the url is located in the Paypal_Lib library. So you want to change any occurence of this:

to this:

You also have to change the Port from 80 to 443. With the Paypal_Lib file, I changed:
$fp = fsockopen($url_parsed['host'],"80",$err_num,$err_str,30);

$fp = fsockopen('ssl://',443,$err_num,$err_str,30);


Now comes the fun part. I struggled to figure out where my script was failing. I had created additional code in the PayPal controller that updates the database once data was received back from PayPal. The script wasn’t being called.

There is an IPN testing tool located in the PayPal Sandbox (IPN Simulator). First you enter your IPN handler URL (notify_url field). Then you select the transaction type (Express Checkout, Cart Checkout, Web Accept, etc). Make sure that you select the correct transaction type. I assumed the the CI PayPal Library was using Express Checkout, but it is using “Web Accept”. The amount that the person pays is a field name ‘mc_gross1’ for Express Checkout and ‘mc_gross’ for Web Accept.

Note: PayPal’s custom field has proved invaluable to me. You can add anything you want to this field and PayPal will return it with the other data. For instance, the last project that I worked on, members can sign up for tours. So I added the member_id and tour_id to the custom field separated by an ‘&’ (ie, 498&3). So when the data comes back from PayPal, I know which member signed up for the tour and which tour they signed up for:

list($memberId, $tourId) = explode('&', $_POST['custom']);

By using the IPN simulator, I was able to test the IPN process in a fraction of the time because I did not have to deal with the other portions of the website application (logging in, ordering, etc). Just click the “Send IPN” button and test the results (if you get any results).

Most of the PayPal IPN scripts that I have seen, including the CI Paypal Library, writes the data received back from PayPal to a log file. This log file was the start of my troubles. No matter what I changed, the data was never being written to the log file. I changed the permissions to the log file, change the location, nothing worked. So I removed the log file from the equation. I changed the code so that it would send me an email upon receipt of the data.

I did not receive an email during my first run. The email code was initially placed within:

if ($this->paypal_lib->validate_ipn()) {
//email code

I moved the email code to the beginning of the ipn function. I received an email so I knew that the error was occurring in the validate_ipn method. I continued to move the email code to different positions within the valide_ipn method until I stopped receiving an email. If I did not receive an email, then I knew that the code was blowing up in that area. I continued to move the position of the email code until I had corrected all errors.

While this may be an unpleasant way to debug code, it works. In may case, the code was failing when it attempted to write to the log file, so I commented out that portion of the code. I didn’t need to write to a file because I was posting the data directly to a database.

Be Sociable, Share!

Checkout My New Site - T-shirts For Geeks