If you’re running a web shop or some kind of online business, accepting payments via paypal would be a regular task for you. Paypal features a nice tool named “IPN” or “Instant Payment Notification”. Using this tool, we can easily accept payment and validate simple one time transactions. In today’s post, we shall see how we can integrate Paypal IPN APIs with our PHP based web sites.

Before we start, let’s have a look at how Paypal IPN works:

  • Your website generates a payment request and forwards the user to paypal
  • Paypal collects your data and presents the payment option to the user
  • The user makes the payment, paypal redirects the user back to your site
  • Paypal makes a POST call to your defined URL with all the necessary data about the transaction
  • You make another call to Paypal to verify the notification and the details

This might or might not look complex at the first look. But please bear with me, it’s going to be rather simple. So, let’s get started with some required setup. First, you need to have a Paypal account, go to your Paypal account settings and enable IPN. Go to ‘Selling Preferences’ >> ‘Instant Payment Notification Preferences’ to do that. Then go to ‘Selling Preferences’ >> ‘payment receiving preferences’ and block payment via echeck. This type of payment will take time and turn out to be very complex for a simple payment solution.

Now, we shall setup a simple html form that will generate the payment request and send the user to Paypal.

<form id="paypal_form" class="paypal" action="payments.php" method="post">
    <input name="cmd" type="hidden" value="_xclick" />
    <input name="no_note" type="hidden" value="1" />
    <input name="lc" type="hidden" value="UK" />
    <input name="currency_code" type="hidden" value="GBP" />
    <input name="bn" type="hidden" value="PP-BuyNowBF:btn_buynow_LG.gif:NonHostedGuest" />
    <input name="first_name" type="hidden" value="Customer's First Name" />
    <input name="last_name" type="hidden" value="Customer's Last Name" />
    <input name="payer_email" type="hidden" value="[email protected]" />
    <input name="item_number" type="hidden" value="123456" />
    <input type="submit" value="Submit Payment" />
</form>

When the user submits this form, he will be taken to Paypal. When he makes the payment, Paypal shall POST all the data to a url defined by you in the IPN section. Let’s assume that payment.php shall accept these data. Here is the contents of that file:

// Database variables
$host = "localhost"; //database location
$user = ""; //database username
$pass = ""; //database password
$db_name = ""; //database name

// PayPal settings
$paypal_email = '[email protected]';
$return_url = 'http://example.com/payment-successful.htm';
$cancel_url = 'http://example.com/payment-cancelled.htm';
$notify_url = 'http://example.com/paypal/payments.php';

$item_name = 'Test Item';
$item_amount = 5.00;

// Include Functions
include("functions.php");

//Database Connection
$link = mysql_connect($host, $user, $pass);
mysql_select_db($db_name);

// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){

	// Firstly Append paypal account to querystring
	$querystring .= "?business=".urlencode($paypal_email)."&";

	// Append amount& currency (£) to quersytring so it cannot be edited in html

	//The item name and amount can be brought in dynamically by querying the $_POST['item_number'] variable.
	$querystring .= "item_name=".urlencode($item_name)."&";
	$querystring .= "amount=".urlencode($item_amount)."&";

	//loop for posted values and append to querystring
	foreach($_POST as $key => $value){
		$value = urlencode(stripslashes($value));
		$querystring .= "$key=$value&";
	}

	// Append paypal return addresses
	$querystring .= "return=".urlencode(stripslashes($return_url))."&";
	$querystring .= "cancel_return=".urlencode(stripslashes($cancel_url))."&";
	$querystring .= "notify_url=".urlencode($notify_url);

	// Append querystring with custom field
	//$querystring .= "&custom=".USERID;

	// Redirect to paypal IPN
	header('location:https://www.sandbox.paypal.com/cgi-bin/webscr'.$querystring);
	exit();

}else{
	// Response from PayPal
}

Now, let’s add some verification and check if the data were correct:

// Database variables
$host = "localhost"; //database location
$user = ""; //database username
$pass = ""; //database password
$db_name = ""; //database name

// PayPal settings
$paypal_email = '[email protected]';
$return_url = 'http://example.com/payment-successful.htm';
$cancel_url = 'http://example.com/payment-cancelled.htm';
$notify_url = 'http://example.com/paypal/payments.php';

$item_name = 'Test Item';
$item_amount = 5.00;

// Include Functions
include("functions.php");

//Database Connection
$link = mysql_connect($host, $user, $pass);
mysql_select_db($db_name);

// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){
	// Request from step 3
}else{

	// Response from Paypal

	// read the post from PayPal system and add 'cmd'
	$req = 'cmd=_notify-validate';
	foreach ($_POST as $key => $value) {
		$value = urlencode(stripslashes($value));
		$value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value);// IPN fix
		$req .= "&$key=$value";
	}

	// assign posted variables to local variables
	$data['item_name']			= $_POST['item_name'];
	$data['item_number'] 		= $_POST['item_number'];
	$data['payment_status'] 	= $_POST['payment_status'];
	$data['payment_amount'] 	= $_POST['mc_gross'];
	$data['payment_currency']	= $_POST['mc_currency'];
	$data['txn_id']				= $_POST['txn_id'];
	$data['receiver_email'] 	= $_POST['receiver_email'];
	$data['payer_email'] 		= $_POST['payer_email'];
	$data['custom'] 			= $_POST['custom'];

	// 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.sandbox.paypal.com', 443, $errno, $errstr, 30);

	if (!$fp) {
		// HTTP ERROR
	} else {
				mail('[email protected]', '0', '0');
		fputs ($fp, $header . $req);
		while (!feof($fp)) {
			$res = fgets ($fp, 1024);
			if (strcmp ($res, "VERIFIED") == 0) {

				// Validate payment (Check unique txnid & correct price)
				$valid_txnid = check_txnid($data['txn_id']);
				$valid_price = check_price($data['payment_amount'], $data['item_number']);
				// PAYMENT VALIDATED & VERIFIED!
				if($valid_txnid && $valid_price){
					$orderid = updatePayments($data);
					if($orderid){
						// Payment has been made & successfully inserted into the Database
					}else{
						// Error inserting into DB
						// E-mail admin or alert user
					}
				}else{
					// Payment made but data has been changed
					// E-mail admin or alert user
				}

			}else if (strcmp ($res, "INVALID") == 0) {

				// PAYMENT INVALID & INVESTIGATE MANUALY!
				// E-mail admin or alert user
			}
		}
	fclose ($fp);
	}
}

In the above codes, some functions have been used. Here is the functions.php:

// functions.php
function check_txnid($tnxid){
	global $link;
	return true;
	$valid_txnid = true;
    //get result set
    $sql = mysql_query("SELECT * FROM `payments` WHERE txnid = '$tnxid'", $link);
	if($row = mysql_fetch_array($sql)) {
        $valid_txnid = false;
	}
    return $valid_txnid;
}

function check_price($price, $id){
    $valid_price = false;
 	/*
	you could use the below to check whether the correct price has been paid for the product
	if so uncomment the below code

	$sql = mysql_query("SELECT amount FROM `products` WHERE id = '$id'");
    if (mysql_numrows($sql) != 0) {
		while ($row = mysql_fetch_array($sql)) {
			$num = (float)$row['amount'];
			if($num == $price){
				$valid_price = true;
			}
		}
    }
	return $valid_price;
	*/
	return true;
}

function updatePayments($data){
    global $link;
	if(is_array($data)){
        $sql = mysql_query("INSERT INTO `payments` (txnid, payment_amount, payment_status, itemid, createdtime) VALUES (
                '".$data['txn_id']."' ,
                '".$data['payment_amount']."' ,
                '".$data['payment_status']."' ,
                '".$data['item_number']."' ,
                '".date("Y-m-d H:i:s")."'
                )", $link);
    return mysql_insert_id($link);
    }
}

To try out these codes, you shall need to setup a database and a table. Here is the SQL required for that:

CREATE TABLE IF NOT EXISTS `payments` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `txnid` varchar(20) NOT NULL,
  `payment_amount` decimal(7,2) NOT NULL,
  `payment_status` varchar(25) NOT NULL,
  `itemid` varchar(25) NOT NULL,
  `createdtime` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

When we’re done with that, we have to upload the files to a web host, configure the URL with settings and enjoy!