Location: PHPKode > scripts > PayPal Buy Now and IPN Classes > paypal/IPN.php
<?php
/**
 * Paypal IPN class
 * v1.0 (27/05/2008)
 * Copyright 2008 Roberto Gomes
 * http://ptdev.net
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

class IPN {

	// Official url: https://www.paypal.com/cgi-bin/webscr
	// Testing urls: (do test!)
	//  - https://www.sandbox.paypal.com/cgi-bin/webscr
	//  - http://www.eliteweaver.co.uk/testing/ipntest.php
	public $paypal_url = 'https://www.paypal.com/cgi-bin/webscr';

	// your paypal email (the one that receives the payments)
	public $paypal_email = 'hide@address.com';

	// log to file options
	public $log_to_file = true;					// write logs to file
	public $log_filename = '/path/to/ipn.log';  // the log filename (should NOT be web accessible)

	// log to e-mail options
	public $log_to_email = true;				// send logs by e-mail
	public $log_email = 'hide@address.com';		// where you want to receive the logs
	public $log_subject = 'IPN Log: ';			// prefix for the e-mail subject

	// database information
	public $log_to_db = true;					// false not recommended
	public $db_host = 'localhost';
	public $db_user = 'some_user';
	public $db_pass = 'some_password';
	public $db_name = 'ipn';

	// array of currencies accepted or false to disable
	public $currencies = array('USD');

	// date format on log headers (default: dd/mm/YYYY HH:mm:ss)
	// see http://php.net/date
	public $date_format = 'd/m/Y H:i:s';

	// holds the ipn in a "pretty" way for viewing on logs and emails, can set prefix here
	public $pretty_ipn = "IPN Values received:\n\n";

	// the IPN information received by post will be on this array
	public $ipn = array();

	// the database resource
	protected $dbc;


	// this is where the action is
	public function ipn_is_valid() {

		// loop through the IPN received by POST and do 3 things:
		//  - populate the ipn_data array
		//  - generate a "pretty" list of all ipn variables received (for file logs and e-mails)
		//  - generate the IPN verification string to post back to paypal for validation
		foreach ($_POST as $key => $value) {
			$this->ipn["$key"] = $value;
			$this->pretty_ipn .= "$key: $value\n";
			$req .= "&$key=".urlencode(stripslashes($value));
		}

		// post the ipn back to paypal and exit if invalid
		if(!$this->ipn_postback($req)) {
			return false;
		}

		// got verified
		// do the paypal recommended validations

		// check if payment status is completed
		if($this->ipn['payment_status'] != 'Completed') {
			$this->write_log('WARNING: payment status not completed', $this->pretty_ipn);
			return false;
		}

		// check if it's a duplicate transaction id
		if($this->txn_is_duplicate()) {
			$this->write_log('WARNING: duplicate transaction id detected', $this->pretty_ipn);
			return false;
		}

		// check if it was payed to your e-mail
		if($this->ipn['receiver_email'] != $this->paypal_email) {
			$this->write_log('WARNING: payment was made to different e-mail account', $this->pretty_ipn);
			return false;
		}

		if($this->currencies && !in_array($this->ipn['mc_currency'],$this->currencies)) {
			$this->write_log('WARNING: payment in unsupported currency', $this->pretty_ipn);
			return false;
		}


		// if we didn't return false until now, then everything should be correct
		return true;

	}

	// this method must be called after ipn_is_valid() and all your custom validations have passed
	// it finally updates the database (if active) and logs the valid ipn
	// you can also call it anyway if you want to log invalid ipn's to the database
	public function complete() {
		if(is_resource($this->dbc)) {
			$this->write_db();
		}
		$this->write_log('VALID IPN RECEIVED', $this->pretty_ipn);
	}


	// this method sends the ipn back to paypal
	// returns true if Paypal says verified
	protected function ipn_postback($req) {

		// split the paypal url
		$url_parsed=parse_url($this->paypal_url);

		// connect to paypal
		$socket = fsockopen($url_parsed['host'],80,$err_num,$err_str,30);

		if(!$socket) {
			// could not open the connection. Log it and return
			$this->write_log('WARNING: failed connection to Paypal',"Error establishing connection to paypal\n\nfsockopen error no. $err_num: $err_str");
			return false;

		} else {

			// connected, add the ipn validation cmd and post everything back to PayPal
			$req = 'cmd=_notify-validate' . $req;
			$header .= "POST ".$url_parsed['path']." HTTP/1.0\r\n";
			$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
			$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
			fputs($socket, $header . $req);

			// loop through the response from the server and assign it to a variable
			while(!feof($socket)) {
				$res .= fgets($socket, 1024);
			}

			// close connection
			fclose($socket);

			// check Paypals response
			if (eregi("VERIFIED",$res)) {
				return true;
			} else {
				$this->write_log('WARNING: invalid IPN detected', $this->pretty_ipn);
				return false;
			}

		}

	}

	// checks for duplicate transaction id
	// ignored when database is off
	// also only now we need to connect to the db if enabled
	protected function txn_is_duplicate() {

		if($this->log_to_db) {
			if($this->connect_db()) {
				if(mysql_num_rows(mysql_query("SELECT txn_id FROM ipn WHERE txn_id='".mysql_real_escape_string($this->ipn['txn_id'],$this->dbc)."'",$this->dbc)) != 0) {
					return true;
				}
			}
		}
		return false;

	}


	// connects to database
	protected function connect_db() {

		$this->dbc = mysql_connect($this->db_host, $this->db_user, $this->db_pass);
		if(!$this->dbc) {
			$this->write_log('WARNING: failed connection to database', "Error connecting to database\n\nmysql error no. " . mysql_errno() . ': ' . mysql_error());
			return false;
		} else {
			$db = mysql_select_db($this->db_name, $this->dbc);
			if(!$db) {
				$this->write_log('WARNING: error selecting database',"Error selecting database\n\nmysql error no. " . mysql_errno() . ': ' . mysql_error());
				return false;
			} else {
				return true;
			}
		}

	}


	// writes the log to file and/or sends to email according to preferences
	// parameters:
	//	$log_msg -> short descriptive msg (gets appended to e-mail subjects)
	//  $log_descr -> everyting else, generally the pretty ipn (goes in e-mail body and file logs)
	protected function write_log($log_msg,$log_descr) {

		$thelog = "------------------------------------------------\n";
		$thelog .= '----------- [ '.date($this->date_format).' ] ------------' . "\n";
		$thelog .= "------------------------------------------------\n";
		$thelog .= $log_msg . "\n\n";
		$thelog .= $log_descr . "\n";
		$thelog .= "------------------------------------------------\n";
		$thelog .= "------------------------------------------------\n\n\n\n";

		// log to file if enabled
		if($this->log_to_file) {
			$fp = fopen($this->log_filename,'a');
			fwrite($fp, $thelog);
			fclose($fp);  // close file
		}

		// send email if enabled
		if($this->log_to_email) {
			mail($this->log_email, "$this->log_subject $log_msg", $thelog);
		}

	}

	// write the ipn to the database
	// the hard way because different types of payment send different vars and may mess up the order
	protected function write_db() {
		$sql = 'INSERT INTO ipn VALUES(NULL,';
		$sql .= "'".mysql_real_escape_string($this->ipn['mc_gross'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_status'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payer_id'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['tax'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_street'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payment_date'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payment_status'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['charset'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_zip'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['first_name'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['mc_fee'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_country_code'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_name'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['notify_version'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['custom'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payer_status'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['business'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_country'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_city'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['quantity'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['verify_sign'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payer_email'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['txn_id'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payment_type'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['last_name'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['address_state'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['receiver_email'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payment_fee'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['receiver_id'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['txn_type'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['item_name'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['mc_currency'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['item_number'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['residence_country'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['test_ipn'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['payment_gross'],$this->dbc)."',";
		$sql .= "'".mysql_real_escape_string($this->ipn['shipping'],$this->dbc)."',";
		$sql .= "NOW())";
		//echo $sql;
		mysql_query($sql, $this->dbc);
		// check for insert errors, log the query and the ipn for analysis
		if(mysql_affected_rows() != 1) {
			$this->write_log('WARNING: error saving to database',"SQL query:\n$sql\n\n mysql error no " . mysql_errno() . ': ' . mysql_error() . "\n\n" . $this->pretty_ipn);
		}
	}


}

?>
Return current item: PayPal Buy Now and IPN Classes