Jump to content

Recommended Posts

hey im currently trying to create a website which works as an item exchange, it allows people to sell goods and my site would be the middleman, basically securing the payment and then notifying the seller they are okay to send the goods. but im a little stuck with the IPN and how to get the values from it.

 

i have currently got this code:

<?PHP

use PayPal\Api\Payment;
use PayPal\Api\PaymentExecution;
require 'app/start.php';

if (!isset($_GET['success'], $_GET['paymentId'], $_GET['PayerID'])) {
    die();
    //we will instead redirect the user to confirm something went wrong.
}

if ((bool)$_GET['success'] === false) {
    die(); 
    //we will instead redirect the user to inform something went wrong.
}

$paymentId = $_GET['paymentId'];
$payerId   = $_GET['PayerID'];

$payment = Payment::get($paymentId, $paypal);

$execute = new PaymentExecution();
$execute->setPayerId($payerId);

try {
    $result = $payment->execute($execute, $paypal);
    echo "Payment made. Thanks<br />";
    echo $result;
} catch (Exception $e) {
    $data = json_decode($e->getData());
    var_dump($data);
    die();
}
?>

this was created using the paypal REST API,

 

i currently returns true if the process was successful that doesnt necessarily mean the payment is completed, as this still happens when the payment is pending.

when i echo $result it returns all of the payers details, but im not sure how to get separate parts,

so for example how do i get the status of the payment? i tried something like $result['state'] but that didnt return anything,

also i would like to extract other variables from the result such as name/address of the payer.

 

also i have another question, in regards to how the ipn works, if the payment is for example pending and then the payment status changes to completed, does paypal then notify my listener script of the change in status?

 

any help would be great!

 

thanks

sean

I have not used the new paypal api as of yet, but understand it a little.  The IPN service i don't think is really part of the api.  IPN is a separate thing that paypal does on it's own AFTER a transaction has happened on THEIR site.  I use the IPN for my business so I know how it works not using the api anyway.  I have considered switching to the new api stuff as it seems easier to understand and gives a certainly faster response of the transaction than the classic IPN does.  The IPN can take sometimes a minute to finally process to your server, where as the new api it's pretty much handled by you instantly.

 

Here is a good tutorial on the new api https://youtu.be/BD1dOWIABe0

I have not used the new paypal api as of yet, but understand it a little.  The IPN service i don't think is really part of the api.  IPN is a separate thing that paypal does on it's own AFTER a transaction has happened on THEIR site.  I use the IPN for my business so I know how it works not using the api anyway.  I have considered switching to the new api stuff as it seems easier to understand and gives a certainly faster response of the transaction than the classic IPN does.  The IPN can take sometimes a minute to finally process to your server, where as the new api it's pretty much handled by you instantly.

 

Here is a good tutorial on the new api https://youtu.be/BD1dOWIABe0

 

that's actually the tutorial i used to create this script, but from what i can see it has some holes in it. looks like ill be outsourcing this.

 

thanks

That code doesn't look like IPN...

 

IPN is where PayPal hits a URL on your server with information about some thing they did (eg, transaction). Do you have something for that?

 

i followed the guide linked above, this means i now have 3 files:

 

gateway.php

        $product  = "myproduct Test";
        $price    = $order['price'];
        $shipping = 0.00;

        $total = $price + $shipping;

        $payer = new Payer();
        $payer->setPaymentMethod('paypal');

        $item = new Item();
        $item->setName($product)
            ->setCurrency('USD')
            ->setQuantity(1)
            ->setPrice($price);

        $itemList = new ItemList();
        $itemList->setItems([$item]);

        $details = new Details();
        $details->setShipping($shipping)
            ->setSubtotal($price);

        $amount = new Amount();
        $amount->setCurrency('USD')
            ->setTotal($total)
            ->setDetails($details);

        $transaction = new Transaction();
        $transaction->setAmount($amount)
            ->setItemList($itemList)
            ->setDescription("description here")
            ->setInvoiceNumber(uniqid());

        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl(SITE_URL . '/pay.php?success=true')
            ->setCancelUrl(SITE_URL . '/pay.php?success=false');

        $payment = new Payment();
        $payment->setIntent('sale')
            ->setPayer($payer)
            ->setRedirectUrls($redirectUrls)
            ->setTransactions([$transaction]);

        try {
            $payment->Create($paypal);
        } catch (Exception $e) {
                $data = json_decode($e->getData());
                var_dump($data);
                die();
        }

        $approvalUrl = $payment->getApprovalLink();

the above code does redirect to my page which is what the IPN does?

 

then i have pay.php which the code i have already posted in the earlier thread. but ill show it again.

 

pay.php

<?PHP

use PayPal\Api\Payment;
use PayPal\Api\PaymentExecution;
require 'app/start.php';

if (!isset($_GET['success'], $_GET['paymentId'], $_GET['PayerID'])) {
    die();
    //we will instead redirect the user to confirm something went wrong.
}

if ((bool)$_GET['success'] === false) {
    die(); 
    //we will instead redirect the user to inform something went wrong.
}

$paymentId = $_GET['paymentId'];
$payerId   = $_GET['PayerID'];

$payment = Payment::get($paymentId, $paypal);

$execute = new PaymentExecution();
$execute->setPayerId($payerId);

try {
    $result = $payment->execute($execute, $paypal);
    echo "Payment made. Thanks<br />";
    //echo $result;
} catch (Exception $e) {
    $data = json_decode($e->getData());
    var_dump($data);
    die();
}
?>

then the last page is just a config page which has the include to the api and my keys.

Edited by seany123

No the IPN doesn't redirect, paypal redirects and then paypal sends a separate POST to the IPN file you list inside your actual paypal account.  The tutorial does not have anything to do with IPN.  Like I said, I'm pretty sure that the new api doesn't handle anything with IPN, that's something totally different.

You're confusing the 2 concepts between the tutorial and what IPN is.

 

ok yeah it seems i got confused, because the api code uses a returnURL i was then assuming all the datebase updates are done from their but now i understand that's to complete the payment process and then handle the user immediately.

 

ok ill have to look at an ipn tutorial, any suggestions of a good ipn tutorial?

Not from me. It's pretty straightforward though, most tutorials will have about the same thing: receive big $_POST array, validate with PayPal by sending them almost the same thing, then do what you want with the data.

 

I suggest storing all the IPN data in a table somewhere, though. It's great to have it available if you need it and will include things like dates, email addresses, money amounts, transaction and billing IDs...

Edited by requinix

Here is the basic script that paypal provides for the ipn service.

<?php

 //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.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);
 
// assign posted variables to local variables

//$item_name = htmlentities($_POST['item_name_1']);
//$item_number = (int)$_POST['item_number_1']; 
 
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
}
else if (strcmp ($res, "INVALID") == 0) {
	// log for manual investigation
}

?>

You then go into your paypal account and turn on ipn service and provide the full url path to your ipn processing script.  I can tell you from experience that making sure your script works properly can be tricky since your can't really see the errors it throws when ran cause it's called from paypals side.  I started by making a simple db table to hold the full posted results from paypal so I could see what they send back and how it was named.

foreach($_POST as $k => $v)
{ $value.= $k.' / '.$v.', '; }

$set = $db->prepare("INSERT INTO `test` SET `posted_values` = ?");
$set->execute(array($value));

Then to do my own testing based on the posted info I made another script that puts all the info into a form with text fields so I could repeatedly post the info and test additional query stuff or processing stuff.

<?php
// This list needs to be coppied from the posted text in the table paypal posted to in the other script I showed you.
$test = 'mc_gross / 65.00, protection_eligibility / Eligible, address_status / confirmed, item_number1 / 14, payer_id / KP3ZSMAGVU764, tax / 0.00, address_street / 3415 Delaware Dr., payment_date / 15:22:41 Mar 04 2014 PST, payment_status / Completed, charset / windows-1252, address_zip / 29040, mc_shipping / 0.00, mc_handling / 0.00, first_name / kevin, mc_fee / 2.19, address_country_code / US, address_name / kevin korstange, notify_version / 3.7, custom / 0::standard::c, payer_status / unverified, business / cwpsumter@yahoo.com, address_country / United States, num_cart_items / 1, mc_handling1 / 0.00, address_city / Dalzell, verify_sign / AFcWxV21C7fd0v3bYYYRCpSSRl31AQ50RA-5fQQNKQEAtVOK-KdptSAk, payer_email / kmkorstange1584@gmail.com, mc_shipping1 / 0.00, tax1 / 0.00, txn_id / 8JV23519RC589220L, payment_type / instant, last_name / korstange, address_state / SC, item_name1 / CWP Certification Course  - 3/15/2014, receiver_email / cwpsumter@yahoo.com, payment_fee / 2.19, quantity1 / 1, receiver_id / UK6DJWQC3AXBE, txn_type / cart, mc_gross_1 / 65.00, mc_currency / USD, residence_country / US, transaction_subject / 0::standard::c, payment_gross / 65.00, ipn_track_id / 1d2ea5b3bcfad';

if(isset($_POST['submit']))
{
  print_r($_POST);
}
?>
<div style="width:350px; text-align: right">
<form action="" method="post">

<?php
$item = explode(",", $test);
echo '<input type="submit" name="submit" value="submit"><br>';
foreach($item as $i)
{
	$ex = explode(" / ", $i);
	echo trim($ex[0]).' <input type="text" name="'.trim($ex[0]).'" value="'.$ex[1].'"><br>'."\n";
	//$ex[] = explode(" / ", $i);
}

$op_array = array();


foreach($item as $i)
{
	$ex = explode(" / ", $i);
	//echo $ex[0].' - '.$ex[1].'<br>';
}
echo '';

?>
<input type="submit" name="submit" value="submit">
</form>
</div>

Be aware though, at least cause of how I stored the info and how I explode it, the "address_street /" in the $test usually contains a "," in the value part.  I usually just delete the last half of the address since that's not really the data you need to deal with anyway, otherwise the explode will give you unexpected results and the input names won't match up right.

 

Here is what one of my full ipn scripts looks like.

<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/site_req/core.php');
require_once(ROOT.'site_inc/system_inc/includes.php');

 //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.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);
 
// assign posted variables to local variables

//$item_name = sanitize($_POST['item_name_1']);
//$item_number = (int)$_POST['item_number_1'];

if(isset($_POST['item_name_1'])){ $item_name = sanitize($_POST['item_name_1']); }
elseif(isset($_POST['item_name1'])){ $item_name = sanitize($_POST['item_name1']); }
elseif(isset($_POST['item_name'])){ $item_name = sanitize($_POST['item_name']); }

if(isset($_POST['item_number_1'])){ $item_number = (int)$_POST['item_number_1']; }
elseif(isset($_POST['item_number1'])){ $item_number = (int)$_POST['item_number1']; }
elseif(isset($_POST['item_number'])){ $item_number = (int)$_POST['item_number']; }
else{$item_number = '';}

$payment_status = $_POST['payment_status'];
$payment_amount = (float)$_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = sanitize($_POST['txn_id']);
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$product_id = (int)$_POST['custom'];
 
 
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
	$bad = FALSE;
	
	if($payment_status != 'Completed'){ $bad = TRUE; $what='status';}
	if(checkDepositTxn($txn_id) === TRUE) { $bad = TRUE; $what='txn';}
	if($receiver_email != Settings::get('paypal_email')) { $bad = TRUE; $what='email';}
	//if($payment_amount != '20.00') { $bad = TRUE; $what='amount';}
	if($payment_currency != 'USD') { $bad = TRUE; $what='currency';}
	if(empty($item_number)){ $bad = TRUE; $what = 'other charge'; }

	$db = DB::getInstance();
	
	if($bad === FALSE)
	{
		//Process payment stats
		$now = time();
		$set = $db->prepare("INSERT INTO `".QUOTE_DEPOSITS."` SET `txn_id`= ?, 
																  `q_id`='$item_number', 
																  `part_num`= ?, 
																  `dep_date`='$now', 
																  `product_id`=$product_id,
																  `deposit_amount`=$payment_amount");
		$set->execute(array($txn_id, $item_name));
		
		//$db->query("UPDATE `".QUOTE_RESPONSES."` SET `purchased`=1 WHERE `id`='$item_number'");
	}
	elseif($bad === TRUE && $what != 'other charge')
	{
		$valu='';
		
		foreach($_POST as $k => $v)
		{ $valu.= $k.' / '.$v.', '; }
		
		$valu = sanitize($valu);
		
		$set2 = $db->prepare("INSERT INTO `".QUOTE_DEPOSITS."` SET `why` = ?, `params` = ?");
		$set2->execute(array($what, $valu));
	}
}
else if (strcmp ($res, "INVALID") == 0) {
	// log for manual investigation
}

?>

I have another that my cms uses that is much longer. And no you can't use 2 different ipn scripts, I have this one as a special use for a particular site, whereas my cms script is used in typical cases. Hope that helps get you started.

 

NOTE: the code I posted to test the values sent by paypal was pieced together on this post since the whole script contains other stuff that was specific to my site and would only confuse anyone else using it so I removed those parts.  So that code is untested as it sits on this post, maybe syntax errors if I missed something in the piecing together.

Here is the basic script that paypal provides for the ipn service.

<?php

 //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.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);
 
// assign posted variables to local variables

//$item_name = htmlentities($_POST['item_name_1']);
//$item_number = (int)$_POST['item_number_1']; 
 
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
}
else if (strcmp ($res, "INVALID") == 0) {
	// log for manual investigation
}

?>

You then go into your paypal account and turn on ipn service and provide the full url path to your ipn processing script.  I can tell you from experience that making sure your script works properly can be tricky since your can't really see the errors it throws when ran cause it's called from paypals side.  I started by making a simple db table to hold the full posted results from paypal so I could see what they send back and how it was named.

foreach($_POST as $k => $v)
{ $value.= $k.' / '.$v.', '; }

$set = $db->prepare("INSERT INTO `test` SET `posted_values` = ?");
$set->execute(array($value));

Then to do my own testing based on the posted info I made another script that puts all the info into a form with text fields so I could repeatedly post the info and test additional query stuff or processing stuff.

<?php
// This list needs to be coppied from the posted text in the table paypal posted to in the other script I showed you.
$test = 'mc_gross / 65.00, protection_eligibility / Eligible, address_status / confirmed, item_number1 / 14, payer_id / KP3ZSMAGVU764, tax / 0.00, address_street / 3415 Delaware Dr., payment_date / 15:22:41 Mar 04 2014 PST, payment_status / Completed, charset / windows-1252, address_zip / 29040, mc_shipping / 0.00, mc_handling / 0.00, first_name / kevin, mc_fee / 2.19, address_country_code / US, address_name / kevin korstange, notify_version / 3.7, custom / 0::standard::c, payer_status / unverified, business / cwpsumter@yahoo.com, address_country / United States, num_cart_items / 1, mc_handling1 / 0.00, address_city / Dalzell, verify_sign / AFcWxV21C7fd0v3bYYYRCpSSRl31AQ50RA-5fQQNKQEAtVOK-KdptSAk, payer_email / kmkorstange1584@gmail.com, mc_shipping1 / 0.00, tax1 / 0.00, txn_id / 8JV23519RC589220L, payment_type / instant, last_name / korstange, address_state / SC, item_name1 / CWP Certification Course  - 3/15/2014, receiver_email / cwpsumter@yahoo.com, payment_fee / 2.19, quantity1 / 1, receiver_id / UK6DJWQC3AXBE, txn_type / cart, mc_gross_1 / 65.00, mc_currency / USD, residence_country / US, transaction_subject / 0::standard::c, payment_gross / 65.00, ipn_track_id / 1d2ea5b3bcfad';

if(isset($_POST['submit']))
{
  print_r($_POST);
}
?>
<div style="width:350px; text-align: right">
<form action="" method="post">

<?php
$item = explode(",", $test);
echo '<input type="submit" name="submit" value="submit"><br>';
foreach($item as $i)
{
	$ex = explode(" / ", $i);
	echo trim($ex[0]).' <input type="text" name="'.trim($ex[0]).'" value="'.$ex[1].'"><br>'."\n";
	//$ex[] = explode(" / ", $i);
}

$op_array = array();


foreach($item as $i)
{
	$ex = explode(" / ", $i);
	//echo $ex[0].' - '.$ex[1].'<br>';
}
echo '';

?>
<input type="submit" name="submit" value="submit">
</form>
</div>

Be aware though, at least cause of how I stored the info and how I explode it, the "address_street /" in the $test usually contains a "," in the value part.  I usually just delete the last half of the address since that's not really the data you need to deal with anyway, otherwise the explode will give you unexpected results and the input names won't match up right.

 

Here is what one of my full ipn scripts looks like.

<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/site_req/core.php');
require_once(ROOT.'site_inc/system_inc/includes.php');

 //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.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);
 
// assign posted variables to local variables

//$item_name = sanitize($_POST['item_name_1']);
//$item_number = (int)$_POST['item_number_1'];

if(isset($_POST['item_name_1'])){ $item_name = sanitize($_POST['item_name_1']); }
elseif(isset($_POST['item_name1'])){ $item_name = sanitize($_POST['item_name1']); }
elseif(isset($_POST['item_name'])){ $item_name = sanitize($_POST['item_name']); }

if(isset($_POST['item_number_1'])){ $item_number = (int)$_POST['item_number_1']; }
elseif(isset($_POST['item_number1'])){ $item_number = (int)$_POST['item_number1']; }
elseif(isset($_POST['item_number'])){ $item_number = (int)$_POST['item_number']; }
else{$item_number = '';}

$payment_status = $_POST['payment_status'];
$payment_amount = (float)$_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = sanitize($_POST['txn_id']);
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$product_id = (int)$_POST['custom'];
 
 
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
	$bad = FALSE;
	
	if($payment_status != 'Completed'){ $bad = TRUE; $what='status';}
	if(checkDepositTxn($txn_id) === TRUE) { $bad = TRUE; $what='txn';}
	if($receiver_email != Settings::get('paypal_email')) { $bad = TRUE; $what='email';}
	//if($payment_amount != '20.00') { $bad = TRUE; $what='amount';}
	if($payment_currency != 'USD') { $bad = TRUE; $what='currency';}
	if(empty($item_number)){ $bad = TRUE; $what = 'other charge'; }

	$db = DB::getInstance();
	
	if($bad === FALSE)
	{
		//Process payment stats
		$now = time();
		$set = $db->prepare("INSERT INTO `".QUOTE_DEPOSITS."` SET `txn_id`= ?, 
																  `q_id`='$item_number', 
																  `part_num`= ?, 
																  `dep_date`='$now', 
																  `product_id`=$product_id,
																  `deposit_amount`=$payment_amount");
		$set->execute(array($txn_id, $item_name));
		
		//$db->query("UPDATE `".QUOTE_RESPONSES."` SET `purchased`=1 WHERE `id`='$item_number'");
	}
	elseif($bad === TRUE && $what != 'other charge')
	{
		$valu='';
		
		foreach($_POST as $k => $v)
		{ $valu.= $k.' / '.$v.', '; }
		
		$valu = sanitize($valu);
		
		$set2 = $db->prepare("INSERT INTO `".QUOTE_DEPOSITS."` SET `why` = ?, `params` = ?");
		$set2->execute(array($what, $valu));
	}
}
else if (strcmp ($res, "INVALID") == 0) {
	// log for manual investigation
}

?>

I have another that my cms uses that is much longer. And no you can't use 2 different ipn scripts, I have this one as a special use for a particular site, whereas my cms script is used in typical cases. Hope that helps get you started.

 

NOTE: the code I posted to test the values sent by paypal was pieced together on this post since the whole script contains other stuff that was specific to my site and would only confuse anyone else using it so I removed those parts.  So that code is untested as it sits on this post, maybe syntax errors if I missed something in the piecing together.

thanks for the code now that all looks a lot easier to understand.

I have another question which i related this Api/ipn

 

the way i have it currently is before the person is directed to paypal to make the payment, there is a insertion into my db table which holds all of the current information about the order, then once the person completes the payment and returns to the success url via the api, it updates the table row with transaction number etc.

 

then when the ipn is received it updates the table row based on the transaction id.

 

the issue im thinking could easily occur is, what happens if they go to paypal and make the payment but is unable to return to my success url?

 

im assuming there would need to be failsafes in the ipn itself but without the transaction id, how would the listener script know which row to update?

Edited by seany123

In my opinion, you don't need both the api and the ipn, it's one or the other depending on your system.  With the api all of the transaction is taken care of right then, you can update and do whatever you want in the db at that time.  With the ipn, you need to wait for paypal to send the ipn and then update the db.  Also with the ipn way, paypal doesn't redirect back immediately after the transaction, whereas the api redirects back as soon as paypal secures the payment on your behalf so you can finish the payment on your site.

 

The new api is really the better way to go about taking payments on your website, I only used the ipn cause the other older versions of things they had were far to complicated to integrate easily, unlike the new api which is far easier now.

In my opinion, you don't need both the api and the ipn, it's one or the other depending on your system.  With the api all of the transaction is taken care of right then, you can update and do whatever you want in the db at that time.  With the ipn, you need to wait for paypal to send the ipn and then update the db.  Also with the ipn way, paypal doesn't redirect back immediately after the transaction, whereas the api redirects back as soon as paypal secures the payment on your behalf so you can finish the payment on your site.

 

The new api is really the better way to go about taking payments on your website, I only used the ipn cause the other older versions of things they had were far to complicated to integrate easily, unlike the new api which is far easier now.

 

how can the api do this all alone though?, if for example my server is down as paypal tries redirecting to the return url then the script will never receive the notification to update the db. i thought it was only by the ipn that you could send notifications after the payment, again the same thing would apply for payment_status changes and refund notifications surely that would all be done via ipn.

Edited by seany123

If your server goes down in the 30 seconds between you sending them to paypal and paypal directing them back, well you probably have much bigger problems.  Plus the ipn only sends once too, so it wouldn't make any difference which method you chose, if your server is all the sudden down, the ipn won't work either.

 

I still think you don't understand the difference between the api and ipn.  The api allows you to send them to paypal to enter their payment info and then be sent right back to your site where your php processes the payment and you perform any other db update you want IMMEDIATELY.  Then you redirect your customer to whatever page you want and allow them access to whatever they just paid for.

 

The ipn works like this.  You setup a button with far more details than the api button, they click and go to paypal to enter their payment info.  They stay on paypal until the payment is fully finished.  Paypal shows them a thank you screen and redirects them a few seconds later to the page you setup in the paypal account itself.  Now in the time they are redirected, it is possible that paypal has already sent the ipn info to your page, but it's not a guaranteed to be that fast.  I've seen it take upto a minute.  So even if they are redirected back, the ipn may not have finished yet, so now what do you do, make them wait an unknown amount of time until the ipn finishes and keep refreshing the page and checking if it has.

 

Do you see the difference now?  The api is the better way to go.  The only reason the ipn works good for me if cause I need to verify each transaction as the ipn sends to make sure I have everything on hand for the product the customer requested.  So the customer is informed right away that I need to verify and get things ready for them BEFORE they are allowed to do anything else on the website.

 

The tutorial shows you more than enough to get you going.  There aren't any good ipn tutorials out there that deal with the most recent code from paypal, so you'll be left to figure alot of that out yourself, although I gave you pretty much everything you need already.

If your server goes down in the 30 seconds between you sending them to paypal and paypal directing them back, well you probably have much bigger problems.  Plus the ipn only sends once too, so it wouldn't make any difference which method you chose, if your server is all the sudden down, the ipn won't work either.

 

I still think you don't understand the difference between the api and ipn.  The api allows you to send them to paypal to enter their payment info and then be sent right back to your site where your php processes the payment and you perform any other db update you want IMMEDIATELY.  Then you redirect your customer to whatever page you want and allow them access to whatever they just paid for.

 

The ipn works like this.  You setup a button with far more details than the api button, they click and go to paypal to enter their payment info.  They stay on paypal until the payment is fully finished.  Paypal shows them a thank you screen and redirects them a few seconds later to the page you setup in the paypal account itself.  Now in the time they are redirected, it is possible that paypal has already sent the ipn info to your page, but it's not a guaranteed to be that fast.  I've seen it take upto a minute.  So even if they are redirected back, the ipn may not have finished yet, so now what do you do, make them wait an unknown amount of time until the ipn finishes and keep refreshing the page and checking if it has.

 

Do you see the difference now?  The api is the better way to go.  The only reason the ipn works good for me if cause I need to verify each transaction as the ipn sends to make sure I have everything on hand for the product the customer requested.  So the customer is informed right away that I need to verify and get things ready for them BEFORE they are allowed to do anything else on the website.

 

The tutorial shows you more than enough to get you going.  There aren't any good ipn tutorials out there that deal with the most recent code from paypal, so you'll be left to figure alot of that out yourself, although I gave you pretty much everything you need already.

thanks for your continued help, i agree that its a very unlikely thing to happen, but it also depends on the user themselves returning back to the success page, so it could go wrong for something as simple as the user losing internet connection before being redirect.. which makes you think this probably happens more often than first thought.

 

I have now done a little research and apparently IPNs requires a HTTP/1.1 200 OK HTTP status response whenever PayPal POST's the IPN data to it, if not it will try again.

 

anyway i shall continue to look into this, so thanks for your help.

Edited by seany123
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • 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.