Jump to content

Paypal IPN Script not updating database


Smashden

Recommended Posts

Hey, I'm trying to integrate paypal into my site. When the purchase is complete, I want update my mysql database with the relevant values. However, when I test, the database does not update.I don't get any errors.

This is my paypal.htm and ipn.php

<?php
$acc = $_SESSION["acc"]
?>
<b>PayPal Shop System.</b><br><br>
The shop costs:<ul><li> 5 EUR (for 1000 points)</li><li> 10 EUR (for 2500 points)</li><li> 20 EUR (for 6000 points)</li><br>

<b>Here are the steps you need to make:</b> <br>
1. A PayPal account with a required balance [5, 10 or 20 EUR] or a creditcard. <br>
2. Fill in your account number. <br>
3. Click on the Buy Now button or your creditcard brand. <br>
4. Make a transaction. <br>
5. After the transaction 1000, 2500 or 6000 points will be automatically added to your account. <br>
6. Go to Item shop and use your points <br> <br> <br> </b>

<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="my business email from sandbox mode(for testing)">
<input type="hidden" name="lc" value="US">
<input type="hidden" name="item_name" value="Premium Points">
<b>Account number: </b> <font color="black"><?php echo $acc ?></font>
<select name="amount">
  <option value="5.00">1000 PP - 5 EUR</option>
  <option value="10.00">2500 PP - 10 EUR</option>
  <option value="20.00">6000 PP - 20 EUR</option>
</select>
<input type="hidden" name="button_subtype" value="products">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="no_note" value="1" />
<input type="hidden" name="currency_code" value="EUR">
<input type="hidden" name="rm" value="0">
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynowCC_LG.gif:NonHostedGuest">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>

<?
		baza(); // -- connecting with database
		ob_start();		
		$account = $_SESSION["acc"];
	if ($_SERVER['REQUEST_METHOD'] == "POST")
	{
		if ($account == "111111" || $account == "1") 
			{ $error='Dla bezpieczeństwa ten numer jest zablokowany!';	}
		elseif (empty($account)) 
			{ $error='Podaj Numer Konta!'; } 
		elseif (!is_numeric($account)) 
			{ $error='Numer konta może składać się wyłącznie z cyfr!'; } 			
		if (empty($error)) 
			{$query=mysql_query('SELECT * FROM accounts WHERE (id = '.$account.')');
				if (mysql_num_rows($query) == 0)
					{$error= "Numer nie istnieje";}	
			}

		$query2 = mysql_query("SELECT `bonus` FROM `accounts` WHERE (`id` = '$account') ") or die(mysql_error());
		
		
		if($_REQUEST['debug'])
		{
			ini_set('display_errors', true);
			error_reporting(E_ALL);
		}
		
		// Variables, don't touch!
		$receiverEmail = $_REQUEST['receiver_email'];
		$paymentStatus = $_REQUEST['payment_status'];
		$mcGross = $_REQUEST['mc_gross'];
		$mcCurrency = $_REQUEST['mc_currency'];
		// Prices
		$prices = array('5.00' => 1000, '10.00' => 2500, '20.00' => 6000);
		// Setup
		$receiver = 'my business email from sandbox mode(for testing)';
		$currency = 'EUR';
		
		if($paymentStatus == 'Completed' && $receiverEmail == $receiver && isset($prices[$mcGross]) && $mcCurrency == $currency)
		{		
			$data = date("U");
			mysql_query("INSERT INTO archiwum(user, kod, data, typ) values('$account', 'Paypal', '$data', '1')");	
			
			// Process payment
			$data = mysql_select_single("SELECT `bonus` AS `old_bonus` FROM `accounts` WHERE (`id` = '$account');");

			// Give points to user
			$new_points = $data['old_bonus'] + $prices[$mcGross];
			mysql_query("UPDATE `accounts` SET `bonus`='$new_points' WHERE (`id` = '$account'));	
		}
		else
		{	
			exit();
		}
	
	}
?>
Link to post
Share on other sites

when paypal makes a http request to your ipn php page, the only input will be the $_POST data. there will be no $_SESSION data, because the request isn't coming from the visitor. your ipn script must also go through a data verification process with the paypal server to insure that the ipn request actually came from paypal.

 

i recommend that you research the ipn documentation and search the web for existing ipn php scripts to see how they are coded.

Link to post
Share on other sites

Hmm, I rewrite some piece of code and nothing changed :/

<?php
$acc = $_SESSION["acc"]
?>
<b>PayPal Shop System.</b><br><br>
The shop costs:<ul><li> 5 EUR (for 1000 points)</li><li> 10 EUR (for 2500 points)</li><li> 20 EUR (for 6000 points)</li><br>

<b>Here are the steps you need to make:</b> <br>
1. A PayPal account with a required balance [5, 10 or 20 EUR] or a creditcard. <br>
2. Fill in your account number. <br>
3. Click on the Buy Now button or your creditcard brand. <br>
4. Make a transaction. <br>
5. After the transaction 1000, 2500 or 6000 points will be automatically added to your account. <br>
6. Go to Item shop and use your points <br> <br> <br> </b>

<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="business email from sandbox mode">
<input type="hidden" name="lc" value="US">
<input type="hidden" name="item_name" value="Premium Points">
<b>Account number: </b> <font color="black"><?php echo $acc ?></font>
<select name="amount">
  <option value="5.00">1000 PP - 5 EUR</option>
  <option value="10.00">2500 PP - 10 EUR</option>
  <option value="20.00">6000 PP - 20 EUR</option>
</select>
<input type="hidden" name="button_subtype" value="products">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="no_note" value="1" />
<input type="hidden" name="currency_code" value="EUR">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynowCC_LG.gif:NonHostedGuest">
<input type="hidden" name="return" value="http://127.0.0.1/sms_shop/">
<input type="hidden" name="cancel_return" value="http://127.0.0.1/sms_shop/">
<input type="hidden" name="notify_url" value="http://127.0.0.1/sms_shop/shop/ipn/ipn.php" />
<input type="hidden" name="custom" value="<?php echo $acc; ?>">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
<?php
	baza();
	ob_start();		
	$account = $_SESSION["acc"];
	if ($_SERVER['REQUEST_METHOD'] == "POST")
	{
		if ($account == "111111" || $account == "1") 
			{ $error='Dla bezpieczeństwa ten numer jest zablokowany!';	}
		elseif (empty($account)) 
			{ $error='Podaj Numer Konta!'; } 
		elseif (!is_numeric($account)) 
			{ $error='Numer konta może składać się wyłącznie z cyfr!'; } 			
		if (empty($error)) 
			{$query=mysql_query('SELECT * FROM accounts WHERE (id = '.$account.')');
				if (mysql_num_rows($query) == 0)
					{$error= "Numer nie istnieje";}	
			}

		$query2 = mysql_query("SELECT `bonus` FROM `accounts` WHERE (`id` = '$account') ") or die(mysql_error());


		//reading raw POST data from input stream. reading pot data from $_POST may cause serialization issues since POST data may contain arrays
		$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_exits = true;
		} 
		foreach ($myPost as $key => $value)
		{        
			if($get_magic_quotes_exits == true && get_magic_quotes_gpc() == 1)
			{ 
				$value = urlencode(stripslashes($value)); 
			}
			else
			{
				$value = urlencode($value);
			}
			$req .= "&$key=$value";
		}
 
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, 'https://www.sandbox.paypal.com/cgi-bin/webscr');
		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_HTTPHEADER, array('Host: www.paypal.com'));
		// In wamp like environment where the root authority certificate doesn't comes in the bundle, you need
		// to download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path 
		// of the certificate as shown below.
		// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
		$res = curl_exec($ch);
		curl_close($ch);
 
		if (strcmp ($res, "VERIFIED") == 0) {
			// check the payment_status is Completed
			// check that txn_id has not been previously processed
			// check that receiver_email is your Primary PayPal email
			// check that payment_amount/payment_currency are correct
			// process payment
			
			// Assign posted variables to local variables
			$item_name = mysql_real_escape_string($_POST['item_name']);
			$item_number = mysql_real_escape_string($_POST['item_number']);
			$payment_status = mysql_real_escape_string($_POST['payment_status']);
			$mcGross = mysql_real_escape_string($_POST['mc_gross']);
			$mcCurrency = mysql_real_escape_string($_POST['mc_currency']);
			$txn_id = mysql_real_escape_string($_POST['txn_id']);
			$receiver_email = mysql_real_escape_string($_POST['receiver_email']);
			$payer_email = mysql_real_escape_string($_POST['payer_email']);
			$user_id = $_POST['custom']; // Our user's ID
			// Prices
			$prices = array('5.00' => 1000, '10.00' => 2500, '20.00' => 6000);
			// Setup
			$receiver = 'business email from sandbox mode';
			$currency = 'EUR';			
			
			if ($payment_status == 'Completed') 
			{
				// Builds a string to insert into the db so you can see everything that has come across from paypal.
				// Pairs are separated by commas and paired key-to-value with a / forward slash
				foreach($_POST as $k => $v)
				{ $valu.= $k.' / '.$v.', '; }

				$txn_id_check = mysql_query("SELECT `txn_id` FROM `log` WHERE `txn_id` = '".$txn_id."'");
				if (mysql_num_rows($txn_id_check) !=1) 
				{
					if ($receiver_email == $receiver) 
					{
						if (isset($prices[$mcGross]) && $mcCurrency == $currency) 
						{
							$data = date("U");					
							// Process payment
							// add txn_id to database
							// Add a column to hold the $valu var info
							$log_query = mysql_query("INSERT INTO `paypal_log` VALUES ('', '".$user_id."', '".$txn_id."', '".$payer_email."', '".$valu."', '".$data."') ");

							
							$data = mysql_select_single("SELECT `bonus` AS `old_bonus` FROM `accounts` WHERE `id` = '".$user_id."';");
							$new_points = $data['old_bonus'] + $prices[$mcGross];							
							$update_bobus = mysql_query("UPDATE `accounts` SET `bonus` = ".$new_points." WHERE `id` = '".$user_id."'");
						}
					}
				}
			}

		}
		else if (strcmp ($res, "INVALID") == 0) {
			// log for manual investigation
			//$db->query("INSERT INTO `".PURCHASES."` SET `test` = 'not valid response'");
		}
	}
?>
Link to post
Share on other sites

You're still using the session stuff, the IPN backend process will have a separate session, so all this will be in error

 

So at the top of your code it looks like this:

 

$account = $_SESSION["acc"];
    if ($_SERVER['REQUEST_METHOD'] == "POST")
    {
        if ($account == "111111" || $account == "1")
            { $error='Dla bezpieczeństwa ten numer jest zablokowany!';    }
        elseif (empty($account))
            { $error='Podaj Numer Konta!'; }
        elseif (!is_numeric($account))
            { $error='Numer konta może składać się wyłącznie z cyfr!'; }             
        if (empty($error))
            {$query=mysql_query('SELECT * FROM accounts WHERE (id = '.$account.')');
                if (mysql_num_rows($query) == 0)
                    {$error= "Numer nie istnieje";}    
            }

        $query2 = mysql_query("SELECT `bonus` FROM `accounts` WHERE (`id` = '$account') ") or die(mysql_error());
        
        ...

 

On my test server here if I try to access a non existent $_SESSION variable I get an error printed to screen,in your case that'll break the whole process because its sending output back to PayPal that it isn't expecting, so they won't then confirm it.

 

$account = $_SESSION["acc"];

 

 

When I did this I made a new database table that I dumped feedback to, or in the PayPal example I believe they log to a file.

Link to post
Share on other sites

Thanks for answer. I changed some piece of code and again not working. Can you tell me why I can't find ipn.log in my server(tested on VPS)?

<b>PayPal Shop System.</b><br><br>
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="email">
<input type="hidden" name="lc" value="US">
<b>Account number: </b> <font color="black"><?php echo $_SESSION["acc"]; ?></font></br>
<select id="item_name" name="item_name" style="width:256px; height:32px;"></select>
<input id="amount" name="amount" type="hidden" value="">
<script>
  var items = [
      { name: "1000 PP", amount: 5.00 },
      { name: "2500 PP", amount: 10.00 },
      { name: "6000 PP", amount: 20.00 }
  ];
  var itemNameElement = document.getElementById("item_name");

  itemNameElement.onchange = (function(){
      var amount = items[this.selectedIndex].amount;
      document.getElementById("amount").value = amount;
  }).bind(itemNameElement);

  document.getElementById("amount").value = items[0].amount;
  for(var i=0; i<items.length; i++){
      var item = document.createElement("option");
      item.value = items[i].name;
      item.text = items[i].name+" - "+items[i].amount+" EUR";
      itemNameElement.add(item);
  }
</script>
<input type="hidden" name="button_subtype" value="products">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="currency_code" value="EUR">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynowCC_LG.gif:NonHostedGuest">
<input type="hidden" name="return" value="http://site/sms_shop/">
<input type="hidden" name="cancel_return" value="http://site/sms_shop/">
<input type="hidden" name="notify_url" value="http://site/sms_shop/shop/ipn-test.php" />
<input type="hidden" name="custom" value='<?php echo $_SESSION["acc"]; ?>'>
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
<?php
	baza();
	ob_start();	
		
		// 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", 1);
		define("LOG_FILE", "./ipn.log");


		// Read POST data	
		//reading raw POST data from input stream. reading pot data from $_POST may cause serialization issues since POST data may contain arrays
		$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_exits = true;
		} 
		foreach ($myPost as $key => $value)
		{        
			if($get_magic_quotes_exits == 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_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
		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);
			}
			curl_close($ch);
			exit;

		} 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);
			}
			curl_close($ch);
		}			

		// Inspect IPN validation result and act accordingly
		// Split response headers and payload, a better way for strcmp
		$tokens = explode("\r\n\r\n", trim($res));
		$res = trim(end($tokens));

		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
			$item_name = $_POST['item_name'];
			$item_number = $_POST['item_number'];
			$payment_status = $_POST['payment_status'];
			$mcGross = $_POST['mc_gross'];
			$mcCurrency = $_POST['currency_code'];
			$currency = 'EUR';
			$txn_id = $_POST['txn_id'];
			$receiver_email = $_POST['receiver_email'];
			$receiver = 'email';
			$payer_email = $_POST['payer_email'];
			$user_id = $_POST['custom'];
			
			$prices = array('5.00' => 1000, '10.00' => 2500, '20.00' => 6000);
			if ($payment_status == 'Completed') 
			{
				$txn_id_check = mysql_query("SELECT `txn_id` FROM `paypal_log` WHERE `txn_id` = '".$txn_id."'");
				if (mysql_num_rows($txn_id_check) !=1) 
				{
					if ($receiver_email == $receiver) 
					{
						if (isset($prices[$mcGross]) && $mcCurrency == $currency)
						{
							// Process payment										
							// add txn_id to database
							$data = date("U");	
							
							/*
							CREATE TABLE `paypal_log` (
							  `id` int(10) NOT NULL auto_increment,
							  `account` int(15) NOT NULL,
							  `email` varchar(20) NOT NULL,
							  `prices` int(15) NOT NULL,
							  `txn_id` varchar(20) NOT NULL,
							  `data` timestamp NOT NULL,
							  PRIMARY KEY  (`id`)
							) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
							*/
							
							$log_query = mysql_query("INSERT INTO `paypal_log` VALUES ('', '".$user_id."', '".$payer_email."'', '".$prices[$mcGross]."', '".$txn_id."', '".$data."') ");

							// add points to user
							$data = mysql_result(mysql_query("SELECT `bonus` AS `old_bonus` FROM `accounts` WHERE `id` = '".$user_id."' LIMIT 1"),0);
							$new_points = $data['old_bonus'] + $prices[$mcGross];							
							$update_bonus = mysql_query("UPDATE `accounts` SET `bonus` = ".$new_points." WHERE `id` = '".$user_id."'");							
						}
					}					
				}				
			}
			
			if(DEBUG == true) {
				error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE); 
			}
			
		} else if (strcmp ($res, "INVALID") == 0) {
			// log for manual investigation
			// Add business logic here which deals with invalid IPN messages
			if(DEBUG == true) {	
				error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE); 
			}
		}
		
?>

Link to post
Share on other sites

for debugging purposes, you can make a test page with a form that submits some dummy post data to your ipn.php script so that you can SEE what your script is doing. you would also temporarily set php's error_reporting to E_ALL and display_errors to ON in your ipn.php code.

 

doing this will run your code and let you see any php errors (i suspect you may have a permission problem with the log file) and let you see if the curl statements are producing errors. this will at least get you to the point of having an "INVALID" response back from paypal and getting the error logging to work, since the dummy post data won't match a real transaction.

 

you can also use this test method and 'fake' a "VERIFIED" response in your logic to let you test and debug your database code.

Link to post
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.