Grabbing a Custom Field with PayPal IPN in PHP - 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.

Related

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.

PayPal IPN with standart button says that result is invalid

I don't know why am I getting INVALID result, while everything works fine with IPN simulator.
IPNSampleCode.php
<?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'); // change to [...]sandbox.paypal[...] when using sandbox to test
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
echo $res;
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'];
if ($_POST['mc_gross'] != NULL)
$payment_amount = $_POST['mc_gross'];
else
$payment_amount = $_POST['mc_gross1'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$custom = $_POST['custom'];
// Insert your actions here
if($payment_status == "Completed" && $receiver_email == "naujas#mt2014.com"){
$db = new mysqli('localhost', 'myDetails', 'myDetails', 'myDetails');
if($db->connect_errno > 0){
die('Unable to connect to database [' . $db->connect_error . ']');
}
//
$time = strtotime(date("Y-m-d"));
$final = date("Y-m-d", strtotime("+12 month", $time));
$db->query("UPDATE users SET active_till='2012-05-05' WHERE email='naujas#mt2014.com';");
$db->close();
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
echo "err";
}
?>
My botton:
<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="QKH6ANQ9ZGXUU">
<table>
<tr><td><input type="hidden" name="on0" value="Period:">Period:</td></tr><tr><td><select name="os0">
<option value="1 month">1 month €50.00 EUR</option>
<option value="3 months">3 months €130.00 EUR</option>
<option value="6 months">6 months €200.00 EUR</option>
<option value="1 year">1 year €350.00 EUR</option>
</select> </td></tr>
</table>
<input type="hidden" name="currency_code" value="EUR">
<input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/btn_paynowCC_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>
When I has been creating my button, I added IPN code url into a finish field.
Maybe I don't have to use button, what then should I use? Thanks for help.
looks good, but try this when looking for a verified:
...
if (strcmp (trim($res), "VERIFIED") == 0) {
...
the payload may contain carriage return and line feed characters when using HTTP 1.1

IPN simulator Successful but does not Run database manipulation

For some reason im having more trouble with this then I should... I have a IPN listner for Paypal and the IPN Simulator says successful each time and with different methods, but I can not get it to then manipulate the database based on a successful response.
Any ideas anyone?
<?php
//INCLUDE CONNECTION STRING
include('connect.php');
// 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.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'));
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) {
// 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($payment_status=="Completed"){
$selectuser = mssql_query("select statsmemberid from statsmembers where email='$payer_email'");
if(mssql_num_rows($selectuser) != 0){
$row = mssql_fetch_row($selectuser);
$statsmemberid = $row[0];
$getCredits = mssql_query("select creditsbought from statsmuplayers where statsmemberid='$statsmemberid'");
$row = mssql_fetch_row($getCredits);
$totalCredits = $row[0]+11;
$updatemu = mssql_query("update statsmuplayers set creditsbought='$totalCredits' where statsmemberid='$statsmemberid'");
echo "Credits Applyed";
}else{
echo "Invalid Email";
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
?>
I'd suggest you encapsulate all of this code into a few objects. That will really help you figure out where things are going wrong.
You want an object that handles the IPN communication with paypal (you don't need to write that from scratch here's the first PHP implementation I found in a google search https://github.com/dodev34/paypal-ipn-response-client)
Then you want a base object that handles your database connections. And finally you want a statsmembers object that extends your database connection object and enforces your business logic. You might just lift some PHP ORM code like what you see here http://www.phpactiverecord.org/projects/main/wiki/Quick_Start
That would allow you to test the update functionality separately from the actual paypal IPN communication. The good news is at that point you don't have to rely on writing to system files for debugging like someone suggested in a comment above.
I don't see the specific error in your code just from a glance sorry, are you sure your passing the payer_email through paypal correctly?

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

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.

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