In part 1 of this series we built a basic IPN listener script using PHP.  If you’re following along, you should now have a working IPN script that receives the data and prepares it for use accordingly.  If you’re struggling with this you might find my previous article on testing and troubleshooting IPN helpful.

Assuming your IPN script is ready to go at this point, we can move forward with automating tasks like sending out email notifications, delivering e-goods, updating databases, interacting with 3rd party web services, etc.

Here, we’re going to go through the steps involved to automatically email an eBook to the buyer, and we’ll also update eBay data to show the item has been delivered if that’s where the purchase originated.  At the same time, we’ll leave some basic feedback for our buyer.

So let’s get started!

Step 1

The first thing we’ll do here is add some basic logic to handle verified and unverified data separately.  In part 1 of this series, we set a variable for $valid to true or false so we can easily use it here.

	// IPN data verified 
	// IPN data unverified.
	// We may want to send out email notifications here,
	// but we don’t want to deliver the product.

We’ve used basic PHP if/then logic to separate our code into two portions.  The first section of code will run if our IPN data was successfully verified with PayPal.  The second section will run should our IPN listener receive data that fails verification with PayPal.

First, we’ll review our code used for verified IPN’s, and then we’ll add some code for handling unverified IPN’s.

Step 2

Because we only want to deliver the product upon an actual completed payment (as opposed to a pending e-check, for example) we’ll add an additional piece of code logic within our $valid block.

// Only deliver the product if the payment is complete.

if(strtoupper($payment_status) == ‘COMPLETED’)
     // Code to run for completed payments goes here.

We can make use of the $payment_status variable that we have available from our parse-data.php file we created in part 1.  We’re simply checking to ensure this value shows that the payment has completed prior to processing the order.

The value comes back as “Completed” and this should never change.  That said, there’s no reason not to make sure if the case syntax does change sometime in the future that our code won’t break.  To do that, we use the PHP function strtoupper().  When we pass our $payment_status into strtoupper() we get back the value “COMPLETED”.  This way our if statement will always work as expected no matter how the value is returned from PayPal.

Step 3

Now we’re ready to deliver our eBook via email to the buyer.  I like to use PHPMailer for email within PHP scripts.  As such, I’ll add that to my project under /includes/phpmailer.  I’ve also added a standard /images directory and a /downloads directory that contains our eBook, so our current directory structure is as follows:

Directory Structure 3

We can now include the PHPMailer library in our script and use it to deliver the eBook to our buyer as an attachment.

// Email eBook to buyer using PHPMailer


$mail = new PHPMailer();
$mail->Host = ‘localhost’;
$mail->From = ‘’;
$mail->FromName = ‘Testers, LLC’;
$mail->Subject = ‘Testers, LLC eBook Delivery’;
$mail->Body = ‘Thank you for your purchase! Your eBook is attached to this email.
                           <br /><br />-Testers, LLC’;
$mail->AddAddress($payer_email, $first_name.’ ‘.$last_name);

Here, we’ve created a PHP variabled, $mail, and we load the PHPMailer object into this using new PHPMailer(). This will now give us access to all of the functions in the PHPMailer class through our $mail variable.

PHPMailer makers things pretty simple for us, and the code here is pretty straight forward.  You’ll notice we’re simply calling class functions to set values like the host, email addresses, the email subject and body, and we’ve also added our eBook attachment stored in our local /downloads directory.

Step 4

Now that our eBook is delivered, we need to update the eBay system to show the auction was “shipped” if the purchase originated on eBay.  We’ll also be adding some basic feedback for our buyer during this same process.  PayPal provides an IPN variable called for_auction that is used for all eBay IPN’s.  Therefore, we can add some more basic logic to handle this using the $for_auction variable:

// If the item was an eBay auction, mark it shipped on eBay.

    // Code for handling eBay auctions goes here.

Now we can add all of our eBay integration code into this code block so that it will only run if the purchase originated on eBay.

Step 5

Now we’re ready to update the eBay system to show that the product has been delivered to the buyer.  I’ve covered the details of integrating eBay with PHP in a previous article, so I won’t be going into depth on that here.    I will be using a simple class library I developed for eBay that follows the procedures in that article, so we’ll go ahead and add that to our project under /includes/eBay.class.php.  We’ll also create a file at /includes/eBay-config.php to store our API credentials:

// /includes/eBay-config.php

$eBay_sandbox = true;
$dev_id = ‘K8CU7387FJ375WF5N1D1QXDR37K1K8’;
$app_id = ‘AngellEY-5555-5555-994c-1bdcb33b093d’;
$cert_id = ‘0138ffba-9069-3g21-aa3f-4511125133a4’;
$auth_token = ‘ASDF;KJ1234;LKJASDF1234;LKJASDF’;

Then we’ll include both of these files in our IPN script and proceed to call the CompleteSale API to mark the item shipped on eBay.


// Setup eBay object.
$eBayConfig = array(
	'Sandbox' => $eBay_sandbox, 
	'DevID' => $dev_id, 
	'AppID' => $app_id, 
	'CertID' => $cert_id, 
	'AuthToken' => $auth_token

$eBay = new eBay($eBayConfig);

$TrackingNumbers = array();

$TrackingNumber = array(
	'TrackingNumber' => $ipn_track_id,
	'ShippingCarrierUsed' => 'Other'

$DaysTimestamp = strtotime('now');
$Mo = date('m', $DaysTimestamp);
$Day = date('d', $DaysTimestamp);
$Year = date('Y', $DaysTimestamp);
$Hour = date('H', $DaysTimestamp);
$Minutes = date('i', $DaysTimestamp);
$Seconds = date('s', $DaysTimestamp);
$ShippedTime = $Year . '-' . $Mo . '-' . $Day . 'T'. $Hour . ':' . $Minutes . ':' . $Seconds . '\Z';

$eBayRequestData = array(
	'FeedbackCommentText' => 'Thank you for your purchase!',
	'FeedbackCommentType' => 'Positive',
	'FeedbackTargetUser' => $auction_buyer_id,
	'ItemID' => $item_number,
	'Paid' => 'TRUE',
	'Shipped' => 'TRUE',
	'TransactionID' => $txn_id,
	'ShipmentNotes' => 'Your eBook has been delivered to '.$payer_email,
	'ShippedTime' => $ShippedTime,
	'TrackingNumbers' => $TrackingNumbers

$eBayResult = $eBay->CompleteSale($eBayRequestData);

Similar to what we did with the PHPMailer class, we’re creating a new PHP variable for our eBay class object called $eBay.  We setting this variable to our eBay object and we’re passing in an array of configuration data that includes our eBay credentials accordingly.

After that, we’re creating an array called $TrackingNumbers to store all of the tracking information we’d like to send to eBay.  Here, we’re sending a single tracking number and we’ve included the $ipn_track_id made available by our parse-data.php file.  Since this was a digital deliver we’ll set the carrier to “other”.

Then, you’ll find that we’re generating a basic timestamp that we’ll be sending in the eBay request to log when the delivery took place.

Finally, we gather everything into a single data array that we’ve called $eBayRequestData.  We’ve included fields for feedback to leave the buyer, the item ID that we’re working with, and we’ve set both Paid and Shipped to TRUE so the eBay platform will reflect this status.  We’ve also included some basic notes, our timestamp, and our tracking information.  All of this is passed into the CompleteSale() function of the class and the result is stored in our $eBayResult variable.

NOTE: We didn’t cover it here, but it’s a good idea to log all of your IPN data in your own database so you can easily refer back to it and confirm the tracking number if somebody claims they didn’t receive their product.

Step 6

Now we’ll add a simple email notification to our own email account in a situation where an IPN hits our script that does not validate correctly with PayPal.  This will go in the else{} block of our if($valid) statement.

Again, we’ll be using PHPMailer for this.  We’ll simply generate a basic dump of the IPN data to send in our email body and send it to ourselves accordingly.

// Email a notification to myself if the IPN data is unverified by PayPal.

$ipn_email = '';
foreach ($_POST as $key => $value)
	$ipn_email .= $key . '' = '' . urldecode($value) . '<br />';


$mail = new PHPMailer();
$mail->Host = 'localhost';
$mail->From = '';
$mail->FromName = 'Testers, LLC';
$mail->Subject = 'Invalid IPN Received';
$mail->Body = 'The following IPN data was received, but was not verified by PayPal.<br /><br />' . $ipn_email;
$mail->AddAddress('', 'My Name');

Here, we’ve created another loop through the $_POST data the same way we did to generate our validation string with PayPal in part 1.  This time, though, we’re generating some basic HTML that provides the IPN in an easy to read format for our email.

Then, we utilize PHPMailer just like before to send ourselves an email with this IPN data so we can handle the information however we choose.


Now that we’ve completed part 2 of this series we have a complete IPN solution that delivers our eBook to our buyer if the payment is complete, and also updates eBay with delivery information and feedback for the buyer.

We also have a basic email notification sent to ourselves if the IPN data is not verified by PayPal.

As I mentioned at the beginning of part 1, you may wish to take the steps here and build them into your own class libraries.  You can download the final project files for this article for your own use in any way you’d like.

One thing you’ll want to consider when implementing this code is that it’s all setup to work with the PayPal and eBay sandbox servers right now.  As you build this code in to your applications it will be a good idea to develop it in a way that’s easy to switch back and forth between the sandbox servers and the live servers.

I hope this has given you a good idea of how you can get up-and-running pretty easily with PHP and PayPal IPN, and then beyond that, the types of more advanced things you can do with it once it’s running successfully.

Good luck and happy coding!