PayPal IPN: (PHP) Send custom field without <form> - php

I'm using CodeIgniter to create a page where users can register for a charity cycle my club is organising.
The registration page accepts info such as name/dob/phone number etc etc, and asks the user if they want to pay online or on the day. Clicking the submit button sends the info to the same page, runs form validation, inserts the info to the database (including timestamp) and checks if the user selected to pay online. If so, the user is redirected to the PayPal donate button URL to complete their payment.
My question is, how do I send the PayPal custom field through the URL? I have tried appending &custom=whatever to the button URL, (where 'whatever' is the new database row ID and the timestamp) but this doesn't work after completing a donation with a sandbox account.
IPN:
public function ipn()
{
// STEP 1: Read POST data
// reading posted data from 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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp like environments that do not come bundled with root authority certificates,
// please 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');
if(!($res = curl_exec($ch)))
{
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if(strcmp($res, "VERIFIED") == 0)
{
// PAYMENT VALIDATED & VERIFIED!
$custom = explode('_', $_POST['custom']);
$reg_id = $custom[0];
$date_registered = $custom[1];
$query = $this->db->query('SELECT date_registered FROM registrations WHERE id=' . $reg_id);
$array = $query->result_array();
if($array['date_registered'] == $date_registered)
{
$data = array(
'has_payed' => 1
);
$this->db->where('id', $reg_id);
$this->db->update('registrations', $data);
}
}
elseif(strcmp($res, "INVALID") == 0)
{
// do whatever
}
}
Note that I need this to work to update the 'has_payed' field of the database, which requires the row ID to be sent when the registration page is submitted.
Registration controller:
// check payment online
if($this->input->post('pay_online') == 1)
{
// redirect to paypal depending on route
switch($route)
{
case '80':
case '40':
// €20 for over 16s
if($age >= 16)
{
redirect('https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H3PFP5GGN2X5J&custom=' . urlencode($reg_id . '_' . $sql['date_registered']));
}
// €10 for anyone under 16
else
{
redirect('a different button URL');
}
break;
case '15':
// €15 for family
redirect('a different button URL');
break;
}
}
The above code is working, as in, I'm being redirected to the button URL when I submit the form with the appropriate values (i.e. doing the 80km or 40km route, and being over 16yo). When I complete the payment at the PayPal page, the 'has_payed' field in the database is not updated as it should be (see IPN page).
Also, note that I do obviously have IPN enabled in my sandbox seller account.
I have searched for hours for a solution to this, but cannot find anything. Maybe I'm overlooking something simple! I'd appreciate any help.
Thanks!
UPDATE
Instead of redirecting the user to the PayPal button URL, I'm now redirecting them to a HTML page with the <form> code on it, and a Javascript which auto-submits the form when they load the page. This is working fine, but the database field is still not updating when the user has donated.
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" id="paypal_form" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="H3PFP5GGN2X5J">
<input type="hidden" name="notify_url" value="#IPN URL#" />
<input type="hidden" name="custom" value="<?php echo urlencode($reg_id . '_' . $sql['date_registered']); ?>">
<input type="image" src="https://www.sandbox.paypal.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online.">
<img alt="" border="0" src="https://www.sandbox.paypal.com/en_GB/i/scr/pixel.gif" width="1" height="1">
</form>

You can pass over the variable "custom" in your button code that you send over to PayPal. This would then be sent back to your system thru the IPN POST. You should then be able to grab it from there and perform your actions that you need to.

The problem, of course, was simple.
CSRF protection was enabled in config.php, which caused the IPN to not work. Disabling it fixed the issue.

Related

Why I can't access my $_POST variables in a form PayPal listener in PHP

I'm wondering where are my $_POST variables if I can't access them in my listener.php file and then how can I send these variables to this file correctly.
form code :
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="TTTTJP9E36G6W">
<input type="hidden" name="hosted_button_id" value="TTTTJP9E36G6W">
<input type="hidden" name="osoba" value="<?=$query?>">
<input type="hidden" name="login" value="<?=$_SESSION['zalogowany']?>">
<input type="hidden" name="idmecz" value="<?=$x?>">
<input type="hidden" name="notify_url" value="http://xxxxxx/listener">
<input type="submit" value="Zapłać" class="butonek" border="0" name="submit" alt="PayPal – Płać wygodnie i bezpiecznie">
<img alt="" border="0" src="https://www.paypalobjects.com/pl_PL/i/scr/pixel.gif" width="1" height="1">
</form>
listener code :
<?php
session_start();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "cmd=_notify-validate&" . http_build_query($_POST));
$response = curl_exec($ch);
curl_close($ch);
if ($response == "VERIFIED" && $_POST['receiver_email'] == "jxxxxm#.....") {
$paymentStatus = $_POST['payment_status'];
if ($paymentStatus == "Completed") {
$id = $_POST['idmecz'];
file_put_contents("file.txt",$id);
}
}
This file doesn't have access to any of the variables even the $_SESSION variables.
IPN listeners receive a direct POST from PayPal when there is an IPN event message. As such, $_SESSION will be irrelevant -- it is a one-time post, there is no session.
If you are expecting a customer's $_SESSION to appear, you are looking for the wrong thing in the wrong place, since the customer's browser is not communicating with your IPN listener.
If you want additional data to be available to you as part of a PayPal IPN message, you can include that data as part of the PayPal transaction using the fields INVNUM (invoice number, which must be unique, never before used for a transaction on your account) and CUSTOM, which may contain anything. These data values will appear somewhere in the IPN $_POST data (print out the array to find what the parameter name is)
Reviewing your listener.php, the problem may be that the verification step is failing. You should debug the curl call -- log curl_error ( $ch ) to a file, and also log $response and a dump of the original $_POST received, so you can review what's actually happening.
( If the certificate can't be verified, you probably need to download an updated Certificate Authority bundle .pem file, and pass a path to that file in CURLOPT_CAINFO )
In my case helped the input named "custom" -> it sends the post variable to my listener, we can access it by simple $_POST or if it contains more than 1 word I separated it with space in the first file, then created a list by explode function in listener and then inserted into database. Hope this one might help others.

PayPal IPN - Testing - Does Not Work in Sandbox

I am trying to code a simple PayPal purchase page with options on my site. However, I am having trouble testing it. For some odd reason, when I use "http://paypal.com/cgi-bin/webscr" as the form URL, it seems to work, however I can't test the IPN without someone purchasing it. If I leave it as "http://sandbox.paypal.com/cgi-bin/webscr", it will pop up with this on the PayPal Sandbox site,
PayPal cannot process this transaction because of a problem with the seller's website. Please contact the seller directly to resolve this problem.
Below is my Form:
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="YDPZBQ2LRBCSE">
<table>
<tr><td><input type="hidden" name="on0" value="Packages">Packages</td></tr><tr><td><select name="os0">
<option value="Bronze">Bronze $10.00 USD</option>
<option value="Silver">Silver $25.00 USD</option>
<option value="Gold">Gold $50.00 USD</option>
</select> </td></tr>
<tr><td><input type="hidden" name="on1" value="Wubbo Username">Wubbo Username</td></tr><tr><td><input type="text" name="os1" maxlength="200"></td></tr>
</table>
<input type="hidden" name="currency_code" value="USD">
<input type="image" src="http://wubbo.me/paypal/buynow.png" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
And here is my IPN Code:
<?php
mysql_connect("localhost", "root", "PASSWORD") or die(mysql_error());
mysql_select_db("DATABASE") or die(mysql_error());
// STEP 1: read POST data
// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
// Instead, read raw POST data from the input stream.
$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 IPN message sent from PayPal and prepend 'cmd=_notify-validate'
$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";
}
// Step 2: POST IPN data back to PayPal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp-like environments that do not come bundled with root authority certificates,
// please 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');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
// The IPN is verified, process it:
// check whether 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 the notification
// assign posted variables to local variables
$package = $_POST['os0'];
$username = $_POST['os1'];
if ($package == 'Bronze') {
mysql_query("UPDATE `users` SET `vip_points` = (`vip_points` + 10) WHERE username = '" . $username . "'");
} else if ($package == 'Silver') {
mysql_query("UPDATE `users` SET `vip_points` = (`vip_points` + 30) WHERE username = '" . $username . "'");
} else if ($package == 'Gold') {
mysql_query("UPDATE `users` SET `vip_points` = (`vip_points` + 100) WHERE username = '" . $username . "'");
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
?>
Any assistance would be greatly appreciated!
When you're using a hosted button you can't just change it from paypal.com to sandbox.paypal.com. You would have to create a separate hosted button from within your sandbox seller account.
Then you would use a sandbox buyer account to make purchases through that button, and if IPN is setup in the seller account it will trigger accordingly.

Track user activity when recurring payment occurs using paypal subscribe button

I have a working IPN script. It is updating the data correctly in database according to the logged in user. So, Once the payment is successfully made user account will be upgraded as a paid member. And since i am using a paypal subscribe button. So, from the next month billing process will occur automatically.
So, here what i think(I am not sure), Paypal will not interact with my IPN script stored in my server.
So, My question is :-
If my assumption about IPN script is correct then how could i track which user has made a payment for the next billing cycle? (I don't want to be involved with manual work like tracking user payment information from my Paypal merchant account. I just want to do it through a script. So, once the subscription amount has been deducted from user Paypal account his account on my website will be upgraded as a paid member.)
For the reference what exactly i wanted to update through my ipn script. Below is my IPN script.
<?php
// STEP 1: Read POST data
// reading posted data from 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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp like environments that do not come bundled with root authority certificates,
// please 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');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: 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 Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_name=strip_tags($item_name);
$item_number = strip_tags($_POST['item_number']);
$payment_status = strip_tags($_POST['payment_status']);
$payment_amount = strip_tags($_POST['mc_gross']);
$payment_currency = strip_tags($_POST['mc_currency']);
$txn_id = strip_tags($_POST['txn_id']);
$user_id=strip_tags($_POST['custom']);
$receiver_email = strip_tags($_POST['receiver_email']);
$payer_email = strip_tags($_POST['payer_email']);
//if(strcmp($receiver_email, "h_1356964205_per#gmail.com") == 0)
//{
/*if($payment_status != "Completed")
{
$msg="Transaction with id ".$txn_id." status is not completed..";
mail("support#example.com","Transaction with the same id already exists in database.",$msg,"From:admin#leadstool.net");
exit();
}*/
include_once('connection.php');
//$user_id=getfield('id');
// Query to check the duplicate transaction id.
$query="SELECT `User_id` FROM `transaction` WHERE `Transaction_id`='".mysql_real_escape_string($txn_id)."'";
if($query_run=mysql_query($query))
{
$num=mysql_num_rows($query_run);
if($num == 0)
{
// Query to check the number of times for subscription.
$query="SELECT `Transaction_id` FROM `transaction` WHERE `User_id`='".mysql_real_escape_string($user_id)."'";
if($query_run=mysql_query($query))
{
$num=mysql_num_rows($query_run);
if($num>=1)
{
$type_of_subscription=2;// This 2 will denote the user is rnewing his account
} else {
$type_of_subscription=1;// Here 1 is denoting that user has subscribed for the 1st time.
}
$query="SELECT `B_ad_no_paid_user`,`T_ad_no_paid_user` FROM `WebsiteContent` WHERE `Creator_id`='1' ORDER BY `Date_of_update` DESC LIMIT 1";
if($query_run=mysql_query($query))
{
while($rows=mysql_fetch_array($query_run))
{
$banner_ad_limit=$rows['B_ad_no_paid_user'];
$text_ad_limit=$rows['T_ad_no_paid_user'];
}
}
}// Query to check the number of times for subscription ends here.
//Query to insert the transaction details in database.
$query="INSERT INTO `transaction` VALUES('".$txn_id."','".$user_id."','".$payment_amount."','".$type_of_subscription."','".$payment_status."','1','".$payer_email."',now())";
if($query_run=mysql_query($query))
{
$query="UPDATE `user` SET `User_type`='1', `Banner_ad_limit`='".$banner_ad_limit."', `Text_ad_limit`='".$text_ad_limit."' WHERE `id`='".mysql_real_escape_string($user_id)."'";
if($query_run=mysql_query($query))
{
$msg="Thank you for subscribing to our service. Your Transaction Id is $txn_id.";
mail("$payer_email","Subscription confirmation mail",$msg,"From:admin#example.com");
} else {
$msg="Thank you! Your transaction is successful with transaction id:- $txn_id. But we are unable to upgrade your profile right now. Please contact admin to resolve the problem.";
mail("$payer_email","Subscription confirmation mail",$msg,"From:admin#example.com");
}
} else {
$msg="For Transaction with id ".$txn_id." failed to update in database.";
mail("support#example.com","Unable to update the details in database.",$msg,"From:admin#example.com");
exit();
}
// Query to insert data in database ends here.
} else {
$msg="Transaction with id $txn_id already exists in database. Admin please verify the details manually and contact the user. Email id of user is: $payer_email";
mail("support#example.com","Transaction with the same id already exists in database.",$msg,"From:admin#example.com");
exit();
}// Query to check the duplicate transaction id ends here.
}
//} else {
//$msg="Investigate the reason why the registered email id with paypal does not matched with this id $receiver_email";
//mail("support#example.com","Receiver email address do not matched",$msg,"From:admin#example.com");
//exit();
//}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
$msg="Dear administrator please verify the reason why the transaction failure occures. The details is:- $res";
mail("support#example.com","IPN interaction was not verified.",$msg,"From:admin#example.com");
exit();
}
?>
This is possible using the IPN and notify_url.
Here is a good tutorial:
http://www.techrepublic.com/article/handling-recurring-payments-with-paypal-subscriptions-and-ipn/5331883

Grabbing a Custom Field with PayPal IPN in PHP

So on my website, people are able to order upgrades by clicking "Pay Now" for the upgrade they want and by entering their username in the box. Up until now, I would then go in to PayPal, see the payment, and look at the Minecraft Username field, and upgrade it by hand. Now I want to start using IPN so that I can automate all of the work that i've been doing.
Here is the HTML code for a upgrade option:
<div class=donationBox>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="W2RH7VA3YS3Y4">
<table>
<tr>
<td width=75%><h2>Custom Maps - $5.00</h2></td>
<td><input type="hidden" name="on0" value="Minecraft Username">Minecraft Username<input type="text" name="os0" maxlength="200"></td>
</tr>
<tr>
<td>The ability to upload any map you want to play on! Maybe a survival island map? You bet! Or the Hunger Games!</td>
<td><input type="submit" class="btn btn-primary" value="Buy Now" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!"></td>
</tr>
</table>
</form>
</div>
And here is the PHP code to simply send me an Email with the info I need to try to get it working:
<?php
// STEP 1: Read POST data
// reading posted data from 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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init('https://www.paypal.com/cgi-bin/webscr');
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp like environments that do not come bundled with root authority certificates,
// please 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');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: 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 Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$custom = $_POST['on0'];
$message = "An order has been IPNified! " . $item_name . " " . $item_number . " " . $payment_amount . " " . $txn_id . " " . $custom;
mail('myemail#gmail.com', 'PayPal IPN', $message);
} else if (strcmp ($res, "INVALID") == 0) {
mail('myemail#gmail.com', 'PayPal IPN', 'Errors');
}
?>
The problem that I'm having is here:
$custom = $_POST['on0'];
I tried it as the above and:
$custom = $_POST['custom'];
The first one because that's the name of the Minecraft Username field, and the second I tried because I read that's what it should be, but either way it just didn't return anything. Any help would be great! Thanks!
Dump the results of $_POST and see which key fits the values you're looking for. Use either var_dump or print_r
// Displays more detail such as value types
var_dump($_POST);
// Only displays key => value relationships of an array
print_r($_POST);
EDIT
I just realized it's not possible to see the output of this using ipn. In this case you can always serialize the entire $_POST array and send it in an email.
$post_data_string = serialize($_POST);
mail('myemail#gmail.com', 'PayPal IPN', $post_data_string);
First, you're using a hosted button. As such, any custom options you set must be done within the button creation wizard or edit area of your PayPal account.
The actual parameter name for sending custom data is indeed "custom" so that's what you'll need to use in IPN to get values back for that particular parameter.
What was mentioned before (changing the field name on your form to custom) would work except that you're using a hosted button, so again, that will need to be adjusted in the button manager in your PayPal account.

php - PayPal IPN Not Working (on Sandbox)

UPDATE: I've even tried having the whole PHP script just be a single line that emails me when the script is viewed, and it still doesn't email me.
I've tried everything, or so I feel like. I have the script set up (very basic just so I can learn how to use it) to insert a row based upon the IPN response (completed, invalid, etc.)
However, it does not appear to be doing anything.
I've tried using the sandbox's built in IPN tester, and it does not do anything. I've tried using a Sandbox-based donator button with "notify_url" as a hidden input type, and it does not do anything.
The only time a row is actually inserted, is when I visit the file directly over my browser (the IPN file, of course). In that case, it inserts a row saying it was an invalid payment.
What in the dickens am I doing wrong or is PayPal's sandbox temporarily screwed up?
<?php
require("../functions.php");
sql();
// read the post from PayPal system and add 'cmd'
$req = 'cmd=' . urlencode('_notify-validate');
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.sandbox.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_HEADER, 0);
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'));
$res = curl_exec($ch);
curl_close($ch);
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
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
if($payment_status == "Completed") {
mysql_query("INSERT INTO donators (username)
VALUES ('completed')");
} else {
mysql_query("INSERT INTO donators (username)
VALUES ('wrong payment status')");
}
}
else if (strcmp ($res, "INVALID") == 0) {
mysql_query("INSERT INTO donators (username)
VALUES ('invalid')");
}
?>
Donator form:
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="QJUZ2AGANUWYS">
<input name="notify_url" value="http://mysite.com/libs/paypal/ipn.php" type="hidden">
<input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.sandbox.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
Probably the invalid payment could be coming from the fact you were using a credit card or a credit card simulation by a sandbox account.
Throw away the condition if (strcmp ($res, "VERIFIED") == 0) { and you'll be ok.
i do not know the sandbox's built in IPN tester, sorry, probably someone here will help you on that point.
as this page says:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
"NOTIFYURL is deprecated since version 63.0. Use
PAYMENTREQUEST_0_NOTIFYURL instead. "
Link to post here
Also, as a Code Injection point, do not put your IPN address in your HTML code. Anyone can view it, and as such know the location of the file. For safety purposes, put it inside your background PHP script, inside you're Paypal functions file, inside the function call ConfirmPayment.
Have you tried trimming $res?
strcmp(trim($res), "VERIFIED")
instead of
strcmp($res, "VERIFIED")
That's what finally got it working for me...

Categories