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
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 110

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 110

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 110

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

Secure Your WordPress Site: 5 Essential Approaches

WordPress is the web's most popular CMS, but its dominance also makes it a target for attackers. Our eBook will show you how to:

Prevent costly exploits of core and plugin vulnerabilities
Repel automated attacks
Lock down your dashboard, secure your code, and protect your users

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Author Note: Since this E-E article was originally written, years ago, formal testing has come into common use in the world of PHP.  PHPUnit ( and similar technologies have enjoyed wide adoption, making it possib…
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

729 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