PayPal IPN (Instant Payment Notification) is a service provided by PayPal that allows you to accept payments on your e-commerce website. Although the basic integration is faster and works fine in many cases, the main reason to avoid it is its unreliability, which can lead to incorrect e-commerce data. Imagine the following situation: you are creating orders in your e-commerce admin upon PayPal payment has been made. The customer comes to your website, gets redirected to PayPal, makes a payment and closes the window. The payment is actually made, but you will not receive any return data from PayPal and, as a consequence, you will not see a new order in your e-commerce admin panel. This is one of the problems that is solved by using PayPal IPN. It is important to know that IPN can send notifications for these transactions:

•Instant payments, including Express Checkout and direct credit card payments
•eCheck payments and pending, completed, or denied status payments
•Pending payments •Recurring payments and subscriptions
•Authorizations
•Disputes, chargebacks, reversals, and refunds

How to Enable PayPal IPN

Before using this service, you must enable it in your PayPal account settings. Log in, go to profile, and click on Instant Payment Notification in the right column. On that page, turn it on and enter the full URL to the page that will handle IPN notifications (e.g. http://www.example.com/ipn.php). Handling the data in PHP will be explained later. After saving the settings, you will need a PayPal buy button. If you don’t have one, you can create it through the PayPal website.

PayPal IPN Data in PHP

After enabling the instant payment notification service in your PayPal account settings, you need to create a PHP file that will handle the data. The PHP file must be accessible via the same URL that you have entered in account settings. The PHP file that will handle the IPN notifications should have the following code:

<?php
 
mysql_connect("localhost", "dbuser", "dbpass") or die(mysql_error());
mysql_select_db("DB") or die(mysql_error());
 
// read the data send by PayPal
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
	$value = urlencode(stripslashes($value));
	$req .= "&$key=$value";
}
 
// post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
 
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
// HTTP ERROR
} else {
		fputs ($fp, $header . $req);
		while (!feof($fp)) {
		$res = fgets ($fp, 1024);
		if (strcmp ($res, "VERIFIED") == 0) {
			// PAYMENT VALID

		}
 
		else if (strcmp ($res, "INVALID") == 0) {
 
			// PAYMENT INVALID
 
		}
}
fclose ($fp);
}
?>
 

Let’s see how the IPN works in PHP:

1. After a PayPal event is triggered, the data is sent to the IPN PHP file.

2. The data is encoded in PHP and posted back to PayPal for validation.

3. PayPal responds with “VERIFIED” if the payment is valid, and “INVALID” if the payment is invalid.

4. Runs additional checks in PHP to ensure data validity (will be explained below).

5. Transaction or order processing is done according to your needs.

Validate IPN Data

Although the IPN will work without running the data checks, it is highly recommended to apply them in order to avoid fraudulent transactions. Security checks are run in PHP after the PayPal IPN has confirmed that a transaction is valid and the following criteria is met:

1. Check if payment status is “Completed”

2. Seller e-mail must match your primary account e-mail

3. The amount paid should match the price of your product or service

4. Check if transaction currency code matches the currency you use on your website

5. Check if transaction is already processed

Let’s see the following points in PHP:

 
<?php
	$product_price = “your_product_price”; // The price of your product or service e.g. 15.99
	$currency_code = “your_currency”; // The currency you use on your website e.g. USD
 
	if (strcmp ($res, "VERIFIED") == 0) {
		$errors = array();
		// PAYMENT VALID
                
		// Check payment status 
		if ($_POST['payment_status'] != 'Completed') { 
			$errors[] .= "Payment not completed";

		}

		// Check seller e-mail
		if ($_POST['receiver_email'] != ‘youremail@example.com')  {
			$errors[] = “Incorrect seller e-mail”;
		}

		// Compare the amount received on PayPal with the price you charged for the product or service
		if ($_POST['mc_gross'] != '$product_price') {
			$errors[] .= "Incorrect product price";
		}

		// Check the currency code
		if ($_POST['mc_currency'] != '$currency_code')  {
			$errors[] = “Incorrect currency code”;
		}

		// Check transaction id
		$txn_id = mysql_real_escape_string($_POST['txn_id']);

		$sql = "SELECT COUNT(*) AS count FROM transactions WHERE txn_id = '$txn_id'";
		$q = mysql_query($sql);
		$f = mysql_fetch_array($q);

		if($f[‘count’] > 0) {
			$errors[] = “Transaction already processed”;

		} else {
			// Transaction not processed, store it in the database
			$payer_email  = mysql_real_escape_string($_POST[‘payer_email’]);
			$gross = mysql_real_escape_string($_POST[‘mc_gross’]);

			$insert = mysql_query(“INSERT INTO transactions (txt_id, payer_email, mc_gross) VALUES 
		(‘$txt_id’,’$payer_email’,’$mc_gross’)”);
		}

		if (count($errors) > 0)  {

			// IPN data is incorrect - possible fraud
			// It is a good practice to send the transaction details to your e-mail and investigate manually

			$message = "IPN failed fraud checks";
			mail(‘youremail@example.com’, 'IPN Fraud Warning', $message, $headers);
		} else {

			// PayPal payment is valid
			// Process order here

		}
	}
?>

There are two things to note in this code. First, fetching the product price and currency code depends on the e-commerce solution you are using, so you will have to do that part yourself. Also, checking for a duplicate transaction id is done using a sample database, so please change that according to your website as well.