Related
So I am working on a small site which takes a single payment per user.
When I do payments through the sandbox, if the payment is a success then i always get a IPN response and am able to use that to update my DB
BUT when using live even if paypal says it was a success the IPN returns INVALID
I have looked around for an answer and people have been saying make sure you are using paypal.com and not sandbox.paypal.com, I have done all the suggestions but still no luck.
A working answer to this would be much appreciated!
Here is my code:
This is the html form:
<form class="paypal" action="payments.php" method="post" id="paypal_form" target="_blank">
<input type="hidden" name="cmd" value="_xclick" />
<input type="hidden" name="no_note" value="1" />
<input type="hidden" name="lc" value="UK" />
<input type="hidden" name="currency_code" value="GBP" />
<input type="hidden" name="first_name" value="Customer's First Name" />
<input type="hidden" name="last_name" value="Customer's Last Name" />
<input type="hidden" name="payer_email" value="customer#example.com" />
<input type="hidden" name="item_number" value="123456" / >
<input type="hidden" name="invoice" value="<?php echo $invoice ?>" / >
<input class="submit" type="submit" name="submit" value="Start Payment Process"/>
</form>
This code starts the payment:
<?php
// Database variables
$servername = "localhost";
$username = "un";
$password = "pw";
$dbname = "dbn";
// PayPal settings
$paypal_email = 'email#email.com';
$return_url = 'http://domain.co.uk/paypal/test.php';
$cancel_url = 'http://domain.co.uk/paypal/payment-cancelled';
$notify_url = 'http:/domain.co.uk/paypal/test.php';
$item_name = 'Item Name';
$item_amount = 1.00;
$invoice = $_POST['invoice'];
// Include Functions
include("functions.php");
// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){
$querystring = '';
// Firstly Append paypal account to querystring
$querystring .= "?business=".urlencode($paypal_email)."&";
// Append amount& currency (£) to quersytring so it cannot be edited in html
//The item name and amount can be brought in dynamically by querying the $_POST['item_number'] variable.
$querystring .= "item_name=".urlencode($item_name)."&";
$querystring .= "amount=".urlencode($item_amount)."&";
$querystring .= "invoice=".urlencode($invoice)."&";
//loop for posted values and append to querystring
foreach($_POST as $key => $value){
$value = urlencode(stripslashes($value));
$querystring .= "$key=$value&";
}
// Append paypal return addresses
$querystring .= "return=".urlencode(stripslashes($return_url))."&";
$querystring .= "cancel_return=".urlencode(stripslashes($cancel_url))."&";
$querystring .= "notify_url=".urlencode($notify_url);
// Append querystring with custom field
//$querystring .= "&custom=".USERID;
// Redirect to paypal IPN
header('location:https://www.paypal.com/cgi-bin/webscr'.$querystring);
exit();
} else {
//Database Connection
$link = mysql_connect($servername, $username, $password);
mysql_select_db($db_name);
// Response from Paypal
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value);// IPN fix
$req .= "&$key=$value";
}
// assign posted variables to local variables
$data['item_name'] = $_POST['item_name'];
$data['item_number'] = $_POST['item_number'];
$data['payment_status'] = $_POST['payment_status'];
$data['payment_amount'] = $_POST['mc_gross'];
$data['payment_currency'] = $_POST['mc_currency'];
$data['txn_id'] = $_POST['txn_id'];
$data['receiver_email'] = $_POST['receiver_email'];
$data['payer_email'] = $_POST['payer_email'];
$data['custom'] = $_POST['custom'];
// post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
// HTTP ERROR
} else {
fputs($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp($res, "VERIFIED") == 0) {
// Used for debugging
// mail('user#domain.com', 'PAYPAL POST - VERIFIED RESPONSE', print_r($post, true));
// Validate payment (Check unique txnid & correct price)
$valid_txnid = check_txnid($data['txn_id']);
$valid_price = check_price($data['payment_amount'], $data['item_number']);
// PAYMENT VALIDATED & VERIFIED!
if ($valid_txnid && $valid_price) {
$orderid = updatePayments($data);
if ($orderid) {
// Payment has been made & successfully inserted into the Database
mail("email#email.com","Success","Payment made successfully");
echo "updated";
} else {
// Error inserting into DB
// E-mail admin or alert user
mail('email#email.com', 'PAYPAL POST - INSERT INTO DB WENT WRONG', print_r($data, true));
}
} else {
// Payment made but data has been changed
// E-mail admin or alert user
}
} else if (strcmp ($res, "INVALID") == 0) {
// PAYMENT INVALID & INVESTIGATE MANUALY!
// E-mail admin or alert user
// Used for debugging
//#mail("user#domain.com", "PAYPAL DEBUGGING", "Invalid Response<br />data = <pre>".print_r($post, true)."</pre>");
}
}
fclose ($fp);
}
}
?>
This code handles the response from paypal:
<?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'));
// 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
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
}
// 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
$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'];
$invoice = $_POST['invoice'];
// IPN message values depend upon the type of notification sent.
// To loop through the &_POST array and print the NV pairs to the screen:
foreach($_POST as $key => $value) {
echo $key . " = " . $value . "<br>";
}
echo $res;
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "Error! Paypal stated payment as: <b>" .$res ."<br />";
}
?>
Sorry i'm a newbie at this PAYPAL IPN
so i got my IPN file.
so my question is, once the user has paid gets redirected to the success/ipn page my script updates the records. but what if the user didnt get redirected, i tested it myself paypal redirects me to the PAYPAL THANK YOU ORDER page and then mozilla pops up a window alert saying that "your connecting to an insecure site bla bla CLICK (yes) or (cancel)" if i click yes then i get redirected to my success/ipn page, but if i click cancel i will stay on the PAYPAL THANK YOU PAGE so im worried that the user might click cancel and he wont get redirected and the data wont be updated
so how do i update the database without paypal redirecting me to my success/ipn page
Please advise, and dont vote down please,
$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,true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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);
// WRITE LOG
$fh = fopen('result.txt', 'w');
fwrite($fh, $res .'--'. $req);
fclose($fh);
// 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'];
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 == 'paypal-test#bolddata-ap.com')){
include('php/config.php'); //Open Database connection
$check = $dbo->prepare('SELECT * FROM order_bytes WHERE trans_id = ?');
$check->execute(array($txn_id));
if($check->rowCount() >= 1){
die('ERROR FOUND SAME TXN ID IN DATABASE');
}else{
/*Data token = EAZZn0OkC2zo4H3CF9vrrSlU-grBGr0oQzE8NZ6jnYGRTuJkJS0howDNK48*/
$stmt = $dbo->prepare("UPDATE `order_bytes` SET stat=? ,paid=?, trans_id=? WHERE `tracker`=? AND `user_id`=?");
/*$stmt->bindValue(":stats", 'PAID', PDO::PARAM_STR);
$stmt->bindParam(":pay", $item_price, PDO::PARAM_STR);
$stmt->bindParam(":it", $item_transaction, PDO::PARAM_STR);
$stmt->bindParam(":trackers", $_SESSION['random_id'], PDO::PARAM_STR);
$stmt->bindParam(":uid", $_SESSION['byte_user'], PDO::PARAM_STR);*/
if(!$stmt->execute(array($payment_status,$payment_amount,$txn_id,$_SESSION['tracker'],$_SESSION['byte_user']))){
//print_r($stmt->errorInfo());
}else {
if($stmt->rowCount() == 1){
notify(getbyteuser($item_no),'A User Ordered your Byte<br />Tracker ID: '.$_SESSION['tracker'].'','NEW ORDER');
//mail_booked($item_transaction);
echo "<div class=\"alert alert-success\"><h3>Thank you<br>Your payment has been successfull</h3>
<p>Keep track of your order in your Dashboard</p>
</div>";
echo 'Done';
}else{
print alert_danger('Error');
}
}
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
?>
You are confused about IPN and return page.
This is wrong:
so my question is, once the user has paid gets redirected to the success/ipn page
IPN is not a success page. It is a page that PayPal calls in the background to notify you about a transaction status. It is independent of the user session. (notify_url)
The page that PayPal redirects the user after complete (or cancel) a payment is different from the IPN. This is related to the user session (return_url)
Update - I removed the validation, and added an $item_id = $_POST['option_selection1']; to my field, and suddenly it worked!
I have a website for a photographer, and she is selling time slots throughout the day. I am trying to set her up so that someone chooses a time slot, purchases it through paypal, and then when the data is returned via IPN, I capture the email address used to make the purchase, as well as an id that I have associated with the time slot. With that ID, I am setting a toggle in the database that causes that time slot to no longer populate on the form, so others can't purchase the same time slot. Every time I make a test transaction, the database doesn't update, and I don't know why. When I manually set the values of the variables $payer_email and $item_number, the database does what I would expect it to do. From this, I'm under the impression that PayPal isn't validating the data, or it isn't sending the data in a way I'd expect it to.
Here is my code for the form that is running through PayPal:
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT id,hour,minute,toggle FROM mini ORDER BY id ASC";
$result = $conn->query($sql);
echo '<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">';
echo '<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="B6XDLRPVAUBQJ">
<input type="hidden" name="on0" value="item_number">
<select name="os0">';
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
if ($row["toggle"] == 0){
echo '<option value="'.$row["id"].'">'.$row["hour"].':'.$row["minute"].' '; //id is what I'm trying to extract from paypal via IPN
if ($row["id"] < 13){echo 'AM';}else{echo 'PM';}; //if-then statement determines if it is AM or PM based on id
echo '</option>';
};
}
} else {
echo "Sorry, I'm fully booked!";
}
?>
</select>
<input type="submit" name="submit" value="Book Your Session">
</form>
Here is my code for the IPN
<?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'));
// 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) {
// 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
$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'];
// IPN message values depend upon the type of notification sent.
// To loop thffrough the &_POST array and print the NV pairs to the screen:
foreach($_POST as $key => $value) {
echo $key." = ". $value."<br>";
}
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
//database credentials intentionally omitted :)
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "UPDATE mini SET toggle='1',email='".$payer_email."' WHERE id=('".$item_number."')";
if ($conn->query($sql) === TRUE) {
echo "<br/> - Record updated successfully";
} else {
echo "Error updating record: " . $conn->error;
}
?>
I'd suggest you to try using PaymentDetails API to debug the payment statuses that you are testing.
https://developer.paypal.com/docs/classic/api/adaptive-payments/PaymentDetails_API_Operation/
Also with PayPal sandbox is a little bit different, slower and buggier - so you should employ some logging. Log everything that is posted and the validation response. After having that you could focus on the app's logic.
I'm a noob to paypal and php so I followed this tutorial online: https://www.developphp.com/video/PHP/PayPal-IPN-PHP-Instant-Payment-Notification-Script
My problem is that my IPN results are all fine but my ipn.php script won't connect to my database so that I can store the results.
IPN.PHP CODE
<?php
//connect to the database
include_once ('includes/connect_to_mysql.php');
// Check to see there are posted variables coming into the script
if ($_SERVER['REQUEST_METHOD'] != "POST") die ("No Post Variables");
// Initialize the $req variable and add CMD key value pair
$req = 'cmd=_notify-validate';
// Read the post from PayPal
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// Now Post all of that back to PayPal's server using curl, and validate everything with PayPal
// We will use CURL instead of PHP for this for a more universally operable script (fsockopen has issues on some environments)
$url = "https://www.sandbox.paypal.com/cgi-bin/webscr"; //USE SANDBOX ACCOUNT TO TEST WITH
//$url = "https://www.paypal.com/cgi-bin/webscr"; //LIVE ACCOUNT
$curl_result=$curl_err='';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($req)));
curl_setopt($ch, CURLOPT_HEADER , 0);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$curl_result = #curl_exec($ch);
$curl_err = curl_error($ch);
curl_close($ch);
$req = str_replace("&", "\n", $req); // Make it a nice list in case we want to email it to ourselves for reporting
// Check that the result verifies with PayPal
if (strpos($curl_result, "VERIFIED") !== false) {
$req .= "\n\nPaypal Verified OK";
mail("email#gmail.com", "Verified OK", "$req", "From: email#gmail.com" );
} else {
$req .= "\n\nData NOT verified from Paypal!";
mail("email#gmail.com", "IPN interaction not verified", "$req", "From: email#gmail.com" );
exit();
}
/* CHECK THESE 4 THINGS BEFORE PROCESSING THE TRANSACTION, HANDLE THEM AS YOU WISH
1. Make sure that business email returned is your business email
2. Make sure that the transaction’s payment status is “completed”
3. Make sure there are no duplicate txn_id
4. Make sure the payment amount matches what you charge for items. (Defeat Price-Jacking) */
// Check Number 1 ------------------------------------------------------------------------------------------------------------
$receiver_email = $_POST['receiver_email'];
if ($receiver_email != "email-facilitator#hotmail.com") {
$message = "Investigate why and how receiver_email variable sent back by PayPal does not match the buisness email set in cart.php. Email = " . $_POST['receiver_email'] . "\n\n\n$req";
mail("email#gmail.com", "Receiver Email is incorrect", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 2 ------------------------------------------------------------------------------------------------------------
if ($_POST['payment_status'] != "Completed") {
// Handle how you think you should if a payment is not complete yet, a few scenarios can cause a transaction to be incomplete
$message = "Investigate why payment was not completed. Email = " . $_POST['receiver_email'] . "\n\n\n$req";
mail("email#gmail.com", "Payment not complete", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 3 ------------------------------------------------------------------------------------------------------------
$this_txn = $_POST['txn_id'];
$sql = mysqli_query($conn, "SELECT id FROM transactions WHERE txn_id='$this_txn' LIMIT 1"); //check to see transaction id exists in the DB
$numRows = mysqli_num_rows($sql);
if ($numRows == 0) {
$message = "Duplicate transaction ID occured so we killed the IPN script. \n\n\n$req";
mail("email#gmail.com", "Duplicate transaction ID(txn_id) in the IPN system", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 4 ------------------------------------------------------------------------------------------------------------
$product_id_string = $_POST['custom'];
$product_id_string = rtrim($product_id_string, ","); // remove last comma
// Explode the string, make it an array, then query all the prices out, add them up, and make sure they match the payment_gross amount
$id_str_array = explode(",", $product_id_string); // Uses Comma(,) as delimiter(break point)
$fullAmount = 0;
foreach ($id_str_array as $key => $value) {
$id_quantity_pair = explode("-", $value); // Uses Hyphen(-) as delimiter to separate product ID from its quantity
$product_id = $id_quantity_pair[0]; // Get the product ID
$product_quantity = $id_quantity_pair[1]; // Get the quantity
$sql = mysqli_query($conn, "SELECT price FROM products WHERE id='$product_id' LIMIT 1");
while($row = mysqli_fetch_array($sql)){
$product_price = $row["price"];
}
$product_price = $product_price * $product_quantity;
$fullAmount = $fullAmount + $product_price;
}
$fullAmount = number_format($fullAmount, 2);
$grossAmount = $_POST['mc_gross'];
if ($fullAmount != $grossAmount) {
$message = "Possible Price Jack: " . $_POST['mc_gross'] . " != $fullAmount \n\n\n$req";
mail("email#gmail.com", "Price Jack or Bad Programming", $message, "From: email#gmail.com" );
exit(); // exit script
}
// END ALL SECURITY CHECKS NOW IN THE DATABASE IT GOES ------------------------------------
////////////////////////////////////////////////////
// Assign local variables from the POST PayPal variables
$custom = $_POST['custom'];
$payer_email = $_POST['payer_email'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$payment_date = $_POST['payment_date'];
$mc_gross = $_POST['mc_gross'];
$payment_currency = $_POST['payment_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payment_type = $_POST['payment_type'];
$payment_status = $_POST['payment_status'];
$txn_type = $_POST['txn_type'];
$payer_status = $_POST['payer_status'];
$address_street = $_POST['address_street'];
$address_city = $_POST['address_city'];
$address_state = $_POST['address_state'];
$address_zip = $_POST['address_zip'];
$address_country = $_POST['address_country'];
$address_status = $_POST['address_status'];
$notify_version = $_POST['notify_version'];
$verify_sign = $_POST['verify_sign'];
$payer_id = $_POST['payer_id'];
$mc_currency = $_POST['mc_currency'];
$mc_fee = $_POST['mc_fee'];
// Place the transaction into the database
$sql = mysqli_query($conn, "INSERT INTO transactions (product_id_array, payer_email, first_name, last_name, payment_date, mc_gross, payment_currency, txn_id, receiver_email, payment_type, payment_status, txn_type, payer_status, address_street, address_city, address_state, address_zip, address_country, address_status, notify_version, verify_sign, payer_id, mc_currency, mc_fee)
VALUES('$custom','$payer_email','$first_name','$last_name','$payment_date','$mc_gross','$payment_currency','$txn_id','$receiver_email','$payment_type','$payment_status','$txn_type','$payer_status','$address_street','$address_city','$address_state','$address_zip','$address_country','$address_status','$notify_version','$verify_sign','$payer_id','$mc_currency','$mc_fee')") or die ("unable to execute the query");
mysqli_close();
// Mail yourself the details
mail("email#gmail.com", "NORMAL IPN RESULT - Transaction Entered", $req, "From: email#gmail.com");
?>
When testing I sent myself an email with the IPN results which were returned as verified and payment_status=Completed.
Here is the IPN output:
cmd=_notify-validate
mc_gross=9.99
protection_eligibility=Eligible
address_status=confirmed
item_number1=
payer_id=BDK3Z8X34KY3Y
tax=0.00
address_street=1+Maire-Victorin
payment_date=13%3A51%3A16+Jul+06%2C+2015+PDT
payment_status=Completed
charset=windows-1252
address_zip=M5A+1E1
mc_shipping=0.00
mc_handling=0.00
first_name=test
mc_fee=0.59
address_country_code=CA
address_name=test+buyer
notify_version=3.8
custom=9-1%2C
payer_status=verified
business=email-facilitator%40hotmail.com
address_country=Canada
num_cart_items=1
mc_handling1=0.00
address_city=Toronto
verify_sign=A2YnYs6LuOd-R8BHIdbWTA6xHgalAu.DiwxDdytu5YxLaIvebtzbprOA
payer_email=email-buyer%40hotmail.com
mc_shipping1=0.00
tax1=0.00
txn_id=6HE31475W2614530U
payment_type=instant
last_name=buyer
address_state=Ontario
item_name1=Leather+Pouch
receiver_email=email-facilitator%40hotmail.com
payment_fee=
quantity1=1
receiver_id=SF4CCTMHQJMF8
txn_type=cart
mc_gross_1=9.99
mc_currency=CAD
residence_country=CA
test_ipn=1
transaction_subject=9-1%2C
payment_gross=
ipn_track_id=a7531c2eb1cec
Paypal Verified OK
I further debugged the code down to the fact that my select statements were not returning any values which to me indicates that the connection to the database was failing.
Can anyone help me out as to why I can't connect to my database using the ipn.php code? All other pages of my website will connect using the same code except this page, so I can only think that it has something to do with PayPal calling this page and not my server.
Here is my connection code in connect_to_mysql.php:
<?php
/* PHP Database Connection */
$servername = "localhost";
$username = "user";
$password = "passs";
$database = "database";
// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully";
?>
You need to troubleshoot the script to find the issue. This can be difficult with IPN, but if you follow these steps for how to test PayPal IPN you should be able to track it down pretty quickly.
I fixed the issue. I needed to switch all of the queries to Mysqli versions instead of the old Mysql. Fixed code below:
<?php
// Report all PHP errors
ini_set('error_reporting', E_ALL);
// Destinations
define("ADMIN_EMAIL", "email#gmail.com");
// Destination types
define("DEST_EMAIL", "1");
/* PHP Database Connection */
$servername = "localhost";
$username = "aesirleatherworks";
$password = "";
$database = "my_aesirleatherworks";
// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);
// Send an e-mail to the administrator if connection fails
if (!$conn) {
error_log("Failed to connect to MYSQL ".mysqli_connect_error(), DEST_EMAIL, ADMIN_EMAIL);
}
// Check to see there are posted variables coming into the script
if ($_SERVER['REQUEST_METHOD'] != "POST") die ("No Post Variables");
// Initialize the $req variable and add CMD key value pair
$req = 'cmd=_notify-validate';
// Read the post from PayPal
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// Now Post all of that back to PayPal's server using curl, and validate everything with PayPal
// We will use CURL instead of PHP for this for a more universally operable script (fsockopen has issues on some environments)
$url = "https://www.sandbox.paypal.com/cgi-bin/webscr"; //USE SANDBOX ACCOUNT TO TEST WITH
//$url = "https://www.paypal.com/cgi-bin/webscr"; //LIVE ACCOUNT
$curl_result=$curl_err='';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($req)));
curl_setopt($ch, CURLOPT_HEADER , 0);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$curl_result = #curl_exec($ch);
$curl_err = curl_error($ch);
curl_close($ch);
$req = str_replace("&", "\n", $req); // Make it a nice list in case we want to email it to ourselves for reporting
// Check that the result verifies with PayPal
if (strpos($curl_result, "VERIFIED") !== false) {
$req .= "\n\nPaypal Verified OK";
} else {
$req .= "\n\nData NOT verified from Paypal!";
mail("email#gmail.com", "IPN interaction not verified", "$req", "From: email#gmail.com" );
exit();
}
/* CHECK THESE 4 THINGS BEFORE PROCESSING THE TRANSACTION, HANDLE THEM AS YOU WISH
1. Make sure that business email returned is your business email
2. Make sure that the transaction’s payment status is “completed”
3. Make sure there are no duplicate txn_id
4. Make sure the payment amount matches what you charge for items. (Defeat Price-Jacking) */
// Check Number 1 ------------------------------------------------------------------------------------------------------------
$receiver_email = $_POST['receiver_email'];
if ($receiver_email != "email-facilitator#hotmail.com") {
$message = "Investigate why and how receiver_email variable sent back by PayPal does not match the buisness email set in cart.php. Email = " . $_POST['receiver_email'] . "\n\n\n$req";
mail("email#gmail.com", "Receiver Email is incorrect", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 2 ------------------------------------------------------------------------------------------------------------
if ($_POST['payment_status'] != "Completed") {
// Handle how you think you should if a payment is not complete yet, a few scenarios can cause a transaction to be incomplete
$message = "Investigate why payment was not completed. Email = " . $_POST['receiver_email'] . "\n\n\n$req";
mail("email#gmail.com", "Payment not complete", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 3 ------------------------------------------------------------------------------------------------------------
$this_txn = $_POST['txn_id'];
$query = "SELECT id FROM transactions WHERE txn_id='$this_txn' LIMIT 1";
$result = mysqli_query($conn, $query);
$numRows = mysqli_num_rows($result);
if ($numRows != 0) {
$message = "Duplicate transaction ID occured so we killed the IPN script. Transaction ID:$this_txn \n\n\n$req";
mail("email#gmail.com", "Duplicate transaction ID(txn_id) in the IPN system", $message, "From: email#gmail.com" );
exit(); // exit script
}
// Check number 4 ------------------------------------------------------------------------------------------------------------
$product_id_string = $_POST['custom'];
$product_id_string = rtrim($product_id_string, ","); // remove last comma
// Explode the string, make it an array, then query all the prices out, add them up, and make sure they match the payment_gross amount
$id_str_array = explode(",", $product_id_string); // Uses Comma(,) as delimiter(break point)
$fullAmount = 0;
foreach ($id_str_array as $key => $value) {
$id_quantity_pair = explode("-", $value); // Uses Hyphen(-) as delimiter to separate product ID from its quantity
$product_id = $id_quantity_pair[0]; // Get the product ID
$product_quantity = $id_quantity_pair[1]; // Get the quantity
$query = "SELECT price FROM products WHERE id='$product_id' LIMIT 1";
$result = mysqli_query($conn, $query);
while($row = mysqli_fetch_array($result)){
$product_price = $row["price"];
}
$product_price = $product_price * $product_quantity;
$fullAmount = $fullAmount + $product_price;
}
$fullAmount = number_format($fullAmount, 2);
$grossAmount = $_POST['mc_gross'];
if ($fullAmount != $grossAmount) {
$message = "Possible Price Jack: " . $_POST['mc_gross'] . " != $fullAmount \n\n\n$req";
mail("email#gmail.com", "Price Jack or Bad Programming", $message, "From: email#gmail.com" );
exit(); // exit script
}
// END ALL SECURITY CHECKS NOW IN THE DATABASE IT GOES ------------------------------------
////////////////////////////////////////////////////
// Assign local variables from the POST PayPal variables
$custom = $_POST['custom'];
$payer_email = $_POST['payer_email'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$payment_date = $_POST['payment_date'];
$mc_gross = $_POST['mc_gross'];
$payment_currency = $_POST['payment_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payment_type = $_POST['payment_type'];
$payment_status = $_POST['payment_status'];
$txn_type = $_POST['txn_type'];
$payer_status = $_POST['payer_status'];
$address_street = $_POST['address_street'];
$address_city = $_POST['address_city'];
$address_state = $_POST['address_state'];
$address_zip = $_POST['address_zip'];
$address_country = $_POST['address_country'];
$address_status = $_POST['address_status'];
$notify_version = $_POST['notify_version'];
$verify_sign = $_POST['verify_sign'];
$payer_id = $_POST['payer_id'];
$mc_currency = $_POST['mc_currency'];
$mc_fee = $_POST['mc_fee'];
// Place the transaction into the database
$sql = mysqli_query($conn, "INSERT INTO transactions (id, product_id_array, payer_email, first_name, last_name, payment_date, mc_gross, payment_currency, receiver_email, payment_type, payment_status, txn_type, payer_status, address_street, address_city, address_state, address_zip, address_country, address_status, notify_version, verify_sign, payer_id, mc_currency, mc_fee)
VALUES('$txn_id','$custom','$payer_email','$first_name','$last_name','$payment_date','$mc_gross','$payment_currency','$receiver_email','$payment_type','$payment_status','$txn_type','$payer_status','$address_street','$address_city','$address_state','$address_zip','$address_country','$address_status','$notify_version','$verify_sign','$payer_id','$mc_currency','$mc_fee')") or die ("unable to execute the query");
mysqli_close();
// Mail yourself the details
mail("email#gmail.com", "New Order Entered: ".$txn_id, $req, "From: email#gmail.com");
?>
My goal is to debug an IPN or any script that runs out off page. Normally errors can be easily handled, but when they are in the background it's tough to do. The script itself is set off when a user pays at paypal. I can't debug on that page.
How would you log an error off page when it is run when you leave your site as this paypal example.
What I've treid so far..
I've added
ini_set('log_errors', true);
ini_set('error_log', dirname(__FILE__).'/ipn_errors.log');
with a file called ipn_errors.log within the same folder as the page i'm trying to debug.
<?php
// Check to see there are posted variables coming into the script
if ($_SERVER['REQUEST_METHOD'] != "POST") die ("No Post Variables");
// Initialize the $req variable and add CMD key value pair
$req = 'cmd=_notify-validate';
// Read the post from PayPal
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// Now Post all of that back to PayPal's server using curl, and validate everything with PayPal
// We will use CURL instead of PHP for this for a more universally operable script (fsockopen has issues on some environments)
//$url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
$url = "https://www.paypal.com/cgi-bin/webscr";
$curl_result=$curl_err='';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($req)));
curl_setopt($ch, CURLOPT_HEADER , 0);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$curl_result = #curl_exec($ch);
$curl_err = curl_error($ch);
curl_close($ch);
$req = str_replace("&", "\n", $req); // Make it a nice list in case we want to email it to ourselves for reporting
// Check that the result verifies
if (strpos($curl_result, "VERIFIED") !== false) {
$req .= "\n\nPaypal Verified OK";
} else {
$req .= "\n\nData NOT verified from Paypal!";
mail("chris#test.com", "IPN interaction not verified", "$req", "From: chris#test.com" );
exit();
}
/* CHECK THESE 4 THINGS BEFORE PROCESSING THE TRANSACTION, HANDLE THEM AS YOU WISH
1. Make sure that business email returned is your business email
2. Make sure that the transaction’s payment status is “completed”
3. Make sure there are no duplicate txn_id
4. Make sure the payment amount matches what you charge for items. (Defeat Price-Jacking) */
// Check Number 1 ------------------------------------------------------------------------------------------------------------
$receiver_email = $_POST['receiver_email'];
if ($receiver_email != "chris#test.com") {
$message = "Investigate why and how receiver email is wrong. Email = " . $_POST['receiver_email'] . "\n\n\n$req";
mail("chris#test.com", "Receiver Email is incorrect", $message, "From: chris#test.com" );
exit(); // exit script
}
// Check number 2 ------------------------------------------------------------------------------------------------------------
if ($_POST['payment_status'] != "Completed") {
// Handle how you think you should if a payment is not complete yet, a few scenarios can cause a transaction to be incomplete
}
// Connect to database ------------------------------------------------------------------------------------------------------
require_once 'db_conx.php';
// Check number 3 ------------------------------------------------------------------------------------------------------------
//
$this_txn = $_POST['txn_id'];
$sql = "SELECT id FROM transactions WHERE txn_id='$this_txn' LIMIT 1";
$query = mysqli_query($db_conx, $sql);
$numRows = mysqli_num_rows($query);
//
if ($numRows > 0) {
$message = "Duplicate transaction ID occured so we killed the IPN script. \n\n\n$req";
mail("chris#test.com", "Duplicate txn_id in the IPN system", $message, "From: chris#test.com" );
exit(); // exit script
}
// Check number 4 ------------------------------------------------------------------------------------------------------------
$product_id_string = $_POST['custom'];
$product_id_string = rtrim($product_id_string, ","); // remove last comma
// Explode the string, make it an array, then query all the prices out, add them up, and make sure they match the payment_gross amount
$id_str_array = explode(",", $product_id_string); // Uses Comma(,) as delimiter(break point)
$fullAmount = 0;
foreach ($id_str_array as $key => $value) {
$id_quantity_pair = explode("-", $value); // Uses Hyphen(-) as delimiter to separate product ID from its quantity
$product_id = $id_quantity_pair[0]; // Get the product ID
$product_quantity = $id_quantity_pair[1]; // Get the quantity
$sqlCommand = "SELECT price FROM products WHERE id='$product_id' LIMIT 1";
$query = mysqli_query($db_conx, $sqlCommand);
while($row = mysqli_fetch_array($query)){
$product_price = $row["price"];
}
$product_price = $product_price * $product_quantity;
$fullAmount = $fullAmount + $product_price;
}
$fullAmount = number_format($fullAmount, 2);
$grossAmount = $_POST['mc_gross'];
if ($fullAmount != $grossAmount) {
$message = "Possible Price Jack: " . $_POST['payment_gross'] . " != $fullAmount \n\n\n$req";
mail("chris#test.com", "Price Jack or Bad Programming", $message, "From: chris#test.com" );
exit(); // exit script
}
//
//
// END ALL SECURITY CHECKS NOW IN THE DATABASE IT GOES ------------------------------------
////////////////////////////////////////////////////
// Homework - Examples of assigning local variables from the POST variables
$txn_id = $_POST['txn_id'];
$payer_email = $_POST['payer_email'];
$custom = $_POST['custom'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$payment_date = $_POST['payment_date'];
$mc_gross = $_POST['mc_gross'];
$payment_currency = $_POST['payment_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payment_type = $_POST['payment_type'];
$payment_status = $_POST['payment_status'];
$txn_type = $_POST['txn_type'];
$payer_status = $_POST['payer_status'];
$address_street = $_POST['address_street'];
$address_city = $_POST['address_city'];
$address_state = $_POST['address_state'];
$address_zip = $_POST['address_zip'];
$address_country = $_POST['address_country'];
$address_status = $_POST['address_status'];
$notify_version = $_POST['notify_version'];
$verify_sign = $_POST['verify_sign'];
$payer_id = $_POST['payer_id'];
$mc_currency = $_POST['mc_currency'];
$mc_fee = $_POST['mc_fee'];
//
require_once 'db_conx.php';
//$username .= substr($email, 0, strpos($email, '#'));
$user_name = $_POST['first_name'];
// Place the transaction into the database
$sql = "INSERT INTO transactions (product_id_array, email, first_name, last_name, payment_date, mc_gross, payment_currency, txn_id, receiver_email, payment_type, payment_status, txn_type, payer_status, address_street, address_city, address_state, address_zip, address_country, address_status, notify_version, verify_sign, payer_id, mc_currency, mc_fee, ip, username)
VALUES('$custom','$payer_email','$first_name','$last_name','$payment_date','$mc_gross','$payment_currency','$txn_id','$receiver_email','$payment_type','$payment_status','$txn_type','$payer_status','$address_street','$address_city','$address_state','$address_zip','$address_country','$address_status','$notify_version','$verify_sign','$payer_id','$mc_currency','$mc_fee','$ip','$user_name')";
$query = mysqli_query($db_conx, $sql);//or die (mysqli_error($myConnection)) add this before ; for error checking
$to = $payer_email;
$subject = '| Login Credentials';
$message = '
Your officially all ready to go. To login use the information below.
Your account login information
-------------------------
Email: '.$payer_email.'
Password: '.$password.'
-------------------------
You can now login at https://www.test.com/signin.php';
$headers = 'From:noreply#test.com' . "\r\n";
mail($to, $subject, $message, $headers);
// Mail yourself the details
mail("chris#test.com", "NORMAL IPN RESULT YAY MONEY!", $req, "From: chris#test.com");
?>
the ipn listener is triggered by an arbitrary transaction sobeit incomplete, invalid etc. in the ipn listener itself you can specify what should be handled whether it be error logging or even mailing the error to a specified email address. I have implemented the ipn handler to update database values on a valid transaction