PHP - need helo with PayPal IPN listener.

Posted on 2014-03-19
Medium Priority
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 = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
	$paypal_url = "https://www.paypal.com/cgi-bin/webscr";

$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 "http://curl.haxx.se/docs/caextract.html" 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 1000 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 111

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 PHP.net and learn exactly how it works!  Example: urlencode()
LVL 111

Assisted Solution

by:Ray Paseur
Ray Paseur earned 1000 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: https://developer.paypal.com/docs/classic/ipn/ht_ipn/
LVL 111

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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

These days socially coordinated efforts have turned into a critical requirement for enterprises.
This article discusses four methods for overlaying images in a container on a web page
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
Suggested Courses

765 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