PHP - need helo with PayPal IPN listener.

Posted on 2014-03-19
Last Modified: 2016-03-24
I have the following PayPal IPN listener script but need help understanding the foreach statements below and assigning posted variables to local variables.

Should I use

$address_street = $_POST['address_street'];


$address_street = $myPost['address_street'];


// CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
// Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
// Set this to 0 once you go live or don't require logging.
define("DEBUG", 1);

// Set to 0 once you're ready to go live
define("USE_SANDBOX", 0);

define("LOG_FILE", "./ipn.log");

// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
	$keyval = explode ('=', $keyval);
	if (count($keyval) == 2)
		$myPost[$keyval[0]] = urldecode($keyval[1]);
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
	$get_magic_quotes_exists = true;
foreach ($myPost as $key => $value) {
	if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
		$value = urlencode(stripslashes($value));
	} else {
		$value = urlencode($value);
	$req .= "&$key=$value";

// Post IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data

if(USE_SANDBOX == true) {
	$paypal_url = "";
} else {
	$paypal_url = "";

$ch = curl_init($paypal_url);
if ($ch == FALSE) {
	return FALSE;

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);

if(DEBUG == true) {
	curl_setopt($ch, CURLOPT_HEADER, 1);
	curl_setopt($ch, CURLINFO_HEADER_OUT, 1);

// CONFIG: Optional proxy configuration
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);

// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

// CONFIG: Please download 'cacert.pem' from "" and set the directory path
// of the certificate as shown below. Ensure the file is readable by the webserver.
// This is mandatory for some environments.

//$cert = __DIR__ . "./cacert.pem";
//curl_setopt($ch, CURLOPT_CAINFO, $cert);

$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
	if(DEBUG == true) {	
		error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);

} else {
		// Log the entire HTTP response if debug is switched on.
		if(DEBUG == true) {
			error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
			error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);

			// Split response headers and payload
			list($headers, $res) = explode("\r\n\r\n", $res, 2);

// Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
	// check whether the payment_status is Completed
	// check that txn_id has not been previously processed
	// check that receiver_email is your PayPal email
	// check that payment_amount/payment_currency are correct
	// process payment and mark item as paid.
	// assign posted variables to local variables

	$mc_gross = $_POST['mc_gross'];
	$invoice = $_POST['invoice'];
	$protection_eligibility = $_POST['protection_eligibility'];
	$address_status = $_POST['address_status'];
	$payer_id = $_POST['payer_id'];
	$tax = $_POST['tax'];
	$address_street = $_POST['address_street'];



Open in new window

Question by:sabecs
  • 3
LVL 11

Accepted Solution

Andrew Angell earned 250 total points
ID: 39941465
Technically, you should get basically the same thing.  It looks like they're just using $myPost to help generate the response back to PayPal for verification.  They loop through it later to generate $req, which is what is then sent to PayPal.

I've always stuck with $_POST['varname'] and it works (assuming your txn_type includes the parameter you're looking for) so that's what I'd go with.

I like to use shorthand if/else syntax on those to avoid warnings in situations where the variable isn't included.

$address_street = isset($_POST['address_street']) ? $_POST['address_street'] : '';

Open in new window

LVL 108

Expert Comment

by:Ray Paseur
ID: 39942060
Where in the world did you find this thing?  It's surely not the PayPal example, right?!

There are a lot of moving parts in a PayPal IPN script!

A good foundation in PHP would be helpful here.

If you only have time to study PHP from one book, I would go with Welling/Thompson.  Get the latest edition and when a new edition comes out, get the new edition and give the obsolete edition to one of your enemies.

I have some time later today, and I'll try to give you an annotated (comments) version of the script, but in between now and then go through the script line-by-line looking at the functions.  If you have any doubt about exactly how the function works, look it up on and learn exactly how it works!  Example: urlencode()
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 250 total points
ID: 39942691
assigning posted variables to local variables
This misunderstands the concept of variable scope.  "Posted" variables are found in $_POST which is a PHP superglobal array.  It is present in every scope and namespace, thus is it always a local variable.  It's also mutable and I think it should not be, but that's a philosophical argument for another time.

Reading the raw post data from stdin presents a security risk.  It takes too much time to explain it all, but if you want a reason, the reason is that php://input does not necessarily contain only the POST request variables.  The preferred way to handle posted data is to use $_POST.  That's why it's there.

I was going to annotate the script, but after reading it more carefully I'm going to recommend that you get rid of it and start over.  Make this exact Google search:

In the results you will find these references and code examples with very good explanations.  They cover the "Classic API" which seems to be what you're using.

Introducing IPN
IPN and PDT Variables
Getting Started
IPN Simulator
the IPN Guide PDF (70 pages)

The overview is this...

1. PayPal makes a POST-method request to your IPN script whenever a payment-related event occurs.  Usually, hopefully, this is a received payment, but it can also be things like a declined check or a reversal.  Your programming will have to account for these things, as shown in the IPN Guide.

2. Your IPN script makes a verification post back to PayPal with the exact information that PayPal sent in the POST request.

3. PayPal responds with VERIFIED and you thereby know you can trust the information.

Anything else is up to your business application needs.

Author Comment

ID: 39943942
Thanks for your comments, very much appreciated.
I will need to have a closer look at all the documentation.

I can't remember exactly where I found the script I am using, but is looks very similar to the one PayPal suggest:
LVL 108

Expert Comment

by:Ray Paseur
ID: 39944135
The PayPal suggestion look like it came from PHP4.  Maybe that is an artifact of the "classic" designation - meaning "from the 1950's" and it may be time to upgrade to the newer APIs.  Technology is always advancing.

In 2004 I would have copied the PayPal IPN example without understanding it.  Today I would try to understand it, forgive it where it was deficient, and improve it where I could.  It's been a few years since I went under the hood to look at PayPal inner workings.  If you can find a JSON or XML interface, that will be easier to use than the classic.

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Generating table dynamically is the most common issue faced by php developers.... So it seems there is a need of an article that explains the basic concept of generating tables dynamically. It just requires a basic knowledge of html and little maths…
Part of the Global Positioning System A geocode ( is the major subset of a GPS coordinate (, the other parts being the altitude and t…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now