I'm trying to figure out how to update a a column in my table called Premium_Access when a payment has gone thru with Paypal.
What i have figured out myself is how to update Premium_Access with typing in a specific memberID myself. Like you can see down below in my code.
How and where should i get the memberID for the logged in user?
Here is my listener.php
header('HTTP/1.1 200 OK');
//
// STEP 2 - create the response we need to send back to PayPal for them to confirm that it's legit
//
$resp = 'cmd=_notify-validate';
foreach ($_POST as $parm => $var)
{
$var = urlencode(stripslashes($var));
$resp .= "&$parm=$var";
}
// STEP 3 - Extract the data PayPal IPN has sent us, into 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'];
$record_id = $_POST['custom'];
// Right.. we've pre-pended "cmd=_notify-validate" to the same data that PayPal sent us (I've just shown some of the data PayPal gives us. A complete list
// is on their developer site. Now we need to send it back to PayPal via HTTP. To do that, we create a file with the right HTTP headers followed by
// the data block we just createdand then send the whole bally lot back to PayPal using fsockopen
// STEP 4 - Get the HTTP header into a variable and send back the data we received so that PayPal can confirm it's genuine
$httphead = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$httphead .= "Content-Type: application/x-www-form-urlencoded\r\n";
$httphead .= "Content-Length: " . strlen($resp) . "\r\n\r\n";
// Now create a ="file handle" for writing to a URL to paypal.com on Port 443 (the IPN port)
$errno ='';
$errstr='';
$fh = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
// STEP 5 - Nearly done. Now send the data back to PayPal so it can tell us if the IPN notification was genuine
if (!$fh) {
// Uh oh. This means that we have not been able to get thru to the PayPal server. It's an HTTP failure
//
// You need to handle this here according to your preferred business logic. An email, a log message, a trip to the pub..
}
// Connection opened, so spit back the response and get PayPal's view whether it was an authentic notification
else {
fputs ($fh, $httphead . $resp);
while (!feof($fh))
{
$readresp = fgets ($fh, 1024);
if (strcmp ($readresp, "VERIFIED") == 0)
{
$servername = "my db ip";
$username = "my db username";
$password = "my db password";
$dbname = "my database";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//When the payment is made i want to update Premiumaccsess in members to YES (for the logged in user)
//Im not sure if i should find out who is logged in by memberID or username
$sql = "UPDATE members SET Premium_Accsess='YES' WHERE memberID=15";
if ($conn->query($sql) === TRUE) {
echo "Record updated successfully";
} else {
echo "Error updating record: " . $conn->error;
}
$conn->close();
}
else if (strcmp ($readresp, "INVALID") == 0)
{
// Man alive! A hacking attempt?
}
}
fclose ($fh);
}
?>
And here is my user.php (I dont think i have to do anything more here but if you can see something wrong please tell.)
<?php
include('password.php');
class User extends Password{
private $_db;
function __construct($db){
parent::__construct();
$this->_db = $db;
}
private function get_user_hash($username){
try {
$stmt = $this->_db->prepare('SELECT password, username, memberID, Premium_Accsess FROM members WHERE username = :username AND active="Yes" ');
$stmt->execute(array('username' => $username));
return $stmt->fetch();
} catch(PDOException $e) {
echo '<p class="bg-danger">'.$e->getMessage().'</p>';
}
}
public function isValidUsername($username){
if (strlen($username) < 3) return false;
if (strlen($username) > 17) return false;
if (!ctype_alnum($username)) return false;
return true;
}
public function login($username,$password){
if (!$this->isValidUsername($username)) return false;
if (strlen($password) < 3) return false;
$row = $this->get_user_hash($username);
if($this->password_verify($password,$row['password']) == 1){
$_SESSION['loggedin'] = true;
$_SESSION['username'] = $row['username'];
$_SESSION['memberID'] = $row['memberID'];
return true;
}
}
public function logout(){
session_destroy();
}
public function is_logged_in(){
if(isset($_SESSION['loggedin']) && $_SESSION['loggedin'] == true){
return true;
}
}
}
I got a paypal ipn code from somewhere. Cant remember where but it works with the ipn simulator when you delete the date from the date field. It does not work with the date. I am now testing with a live payment and it sends the ipn successfully from paypal and no error log. The HTTP response is 200 but nothing is inserted into my database.
Any ideas why it is not working?
Here is the code:
<?php
class PayPal_IPN{
function infotuts_ipn($im_debut_ipn) {
define('SSL_P_URL', 'https://www.paypal.com/cgi-bin/webscr');
define('SSL_SAND_URL', 'https://www.sandbox.paypal.com/cgi-bin/webscr');
$hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
if (!preg_match('/paypal\.com$/', $hostname)) {
$ipn_status = 'Validation post isn\'t from PayPal';
if ($im_debut_ipn == true) {
// mail test
}
return false;
}
// parse the paypal URL
$paypal_url = ($_REQUEST['test_ipn'] == 1) ? SSL_SAND_URL : SSL_P_URL;
$url_parsed = parse_url($paypal_url);
$post_string = '';
foreach ($_REQUEST as $field => $value) {
$post_string .= $field . '=' . urlencode(stripslashes($value)) . '&';
}
$post_string.="cmd=_notify-validate"; // append ipn command
// get the correct paypal url to post request to
$paypal_mode_status = $im_debut_ipn; //get_option('im_sabdbox_mode');
if ($paypal_mode_status == true)
$fp = fsockopen('ssl://www.sandbox.paypal.com', "443", $err_num, $err_str, 60);
else
$fp = fsockopen('ssl://www.paypal.com', "443", $err_num, $err_str, 60);
$ipn_response = '';
if (!$fp) {
// could not open the connection. If loggin is on, the error message
// will be in the log.
$ipn_status = "fsockopen error no. $err_num: $err_str";
if ($im_debut_ipn == true) {
echo 'fsockopen fail';
}
return false;
} else {
// Post the data back to paypal
fputs($fp, "POST $url_parsed[path] HTTP/1.1\r\n");
fputs($fp, "Host: $url_parsed[host]\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: " . strlen($post_string) . "\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $post_string . "\r\n\r\n");
// loop through the response from the server and append to variable
while (!feof($fp)) {
$ipn_response .= fgets($fp, 1024);
}
fclose($fp); // close connection
}
// Invalid IPN transaction. Check the $ipn_status and log for details.
if (!preg_match("/VERIFIED/s", $ipn_response)) {
$ipn_status = 'IPN Validation Failed';
if ($im_debut_ipn == true) {
echo 'Validation fail';
print_r($_REQUEST);
}
return false;
} else {
$ipn_status = "IPN VERIFIED";
if ($im_debut_ipn == true) {
echo 'SUCCESS';
}
return true;
}
}
function ipn_response($request){
mail("info#vertexskysports.com","Order Recieved",print_r($request,true));
$im_debut_ipn=true;
if ($this->infotuts_ipn($im_debut_ipn)) {
// if paypal sends a response code back let's handle it
if ($im_debut_ipn == true) {
$sub = 'PayPal IPN Debug Email Main';
$msg = print_r($request, true);
$aname = 'infotuts';
//mail send
}
// process the membership since paypal gave us a valid +
$this->insert_data($request);
}
}
function issetCheck($post,$key){
if(isset($post[$key])){
$return=$post[$key];
}
else{
$return='';
}
return $return;
}
function insert_data($request){
$con=mysql_connect("xxx.xxx.xx.xx","USERNAME","PASSWORD") or die("Failed to connect with database!!!!");
mysql_select_db("DATABASENAME", $con);
$datetime = date("Y-m-d H:i:s");
$order_number = $_POST['custom'];
$receiver_email = $_POST['receiver_email'];
$txn_id = $_POST['txn_id'];
$payer_email = $_POST['payer_email'];
$payer_id = $_POST['payer_id'];
$payer_status = $_POST['payer_status'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$address_city = $_POST['address_city'];
$address_country = $_POST['address_country'];
$address_state = $_POST['address_state'];
$address_status = $_POST['address_status'];
$address_country_code = $_POST['address_country_code'];
$address_name = $_POST['address_name'];
$address_street = $_POST['address_street'];
$address_zip = $_POST['address_zip'];
$item_name = $_POST['item_name1'];
$fee = $_POST['mc_fee'];
$amount = $_POST['mc_gross_1'];
$payment_status = $_POST['payment_status'];
$shipping = $_POST['mc_shipping'];
$design = "INSERT INTO orders(datetime, order_number, receiver_email, txn_id, payer_email, payer_id, payer_status, first_name, last_name, address_city, address_country, address_state, address_country_code, address_name, address_street, address_zip, item_name, fee, amount, payment_status, shipping)
VALUES('".$datetime."', '".$order_number."', '".$receiver_email."', '".$txn_id."', '".$payer_email."', '".$payer_id."', '".$payer_status."', '".$first_name."', '".$last_name."', '".$address_city."', '".$address_country."', '".$address_state."', '".$address_country_code."', '".$address_name."', '".$address_street."', '".$address_zip."', '".$item_name."', '".$fee."', '".$amount."', '".$payment_status."', '".$shipping."')";
mysql_query($design);
$design2 = "INSERT INTO order_status(datetime, order_number, status) VALUES('".$datetime."','".$order_number."','Received')";
mysql_query($design2);
}
}
$obj = New PayPal_IPN();
$obj->ipn_response($_REQUEST);
?>
The best way to test that sort of thing is to setup your own simulator so you can run it in a browser and see the result on screen. This will help you troubleshoot any potential SQL insert problems, missing data, or whatever might be causing the problem.
So just setup an HTML form with hidden fields matching the fields you expect to get from IPN. Submit that directly to your listener in a browser.
Of course, make sure to handle the fact that the data isn't coming from PayPal, so it won't verify, but you can setup logic to handle that accordingly.
More details are available in this article I wrote about how to test PayPal IPN.
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");
?>
I have a website I am making in PHP and I need to sell an online product. When someone buys this product I need a way to have my website notified and change some information in an SQL table. I have googled this for the past five hours and I read things about paypal IPN and paypal sandbox. I am still very confused about what paypal IPN is and how to use it. As for paypal sandbox, several tutorials for what I am asking say to use paypal sandbox. I have tried, but every time I try to login to my account it says the password is wrong, and when I try to make another it redirects me to paypal.com.
My main question is: How do I make buying something on paypal change information in an SQL table on my website?
Update:
I have been trying to use this tutorial: http://www.evoluted.net/thinktank/web-development/paypal-php-integration
I have this php code here (modified from the tutorial):
<?php // Database variables
$host = "localhost"; //database location
$user = "user"; //database username
$pass = "pass"; //database password
$db_name = "db"; //database name
// PayPal settings
$paypal_email = 'my sandbox business email is here';
$return_url = 'http://painlessnotes.com/';
$cancel_url = 'http://painlessnotes.com/';
$notify_url = 'http://painlessnotes.com/Paypal/payments.php';
$item_name = 'Test Item';
$item_amount = 10.00;
// Include Functions
include("functions.php");
//Database Connection
$link = mysql_connect($host, $user, $pass);
mysql_select_db($db_name);
// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){
echo "<script type='text/javascript'>alert('send start');</script>";
$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)."&";
//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;
echo "<script type='text/javascript'>alert('{$querystring}');</script>";
// Redirect to paypal IPN
header('location:https://www.sandbox.paypal.com/cgi-bin/webscr'.$querystring);
exit();
}else{
// Response from Paypal
mail("my email", "response", "TEST", "From: my email is here");//I am using this to check if it works
// 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.sandbox.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
// HTTP ERROR
} else {
mail('my email', '0', '0');//used to check if it works
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
// 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
}else{
// Error inserting into DB
// E-mail admin or alert user
}
}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
}
}
fclose ($fp);
}
}
?>
The receiving part of this code works when I use IPN simulator in developer.paypal.com for paypal sandbox. The functions.php and the html code giving in the tutorial is the same as what is in the tutorial. When I test this I get the javascript alert boxes coming up and it all looks correct to me, but for some reason I am not getting a response after sending the paypal sandbox request.
What am I doing wrong and how do I fix it? I have create a buyer with a normal paypal account in the sandbox and a business account.
After doing more research and searching I found a paypal page called Paypal Integration Wizard.
This will generate the code needed for what I have asked. It gives a step by step process and is easy to follow. After reading the code I now understand more on how it works.
I'm creating an IPN for a custom digital ecommerce but i have a problem:
everything works file,i create a "pending payment" in my database with an ID that i call PID (payment id),the user go to the paypal page and when the payment is completed paypal contact me on the IPN listener that checks if the payment is completed and enable all the media that the user bought.
I successfully created an IPN using the micah carrick php class
( http://www.micahcarrick.com/php-paypal-ipn-integration-class.html ) and everything is working exept i ALWAYS get a pendign payment status and i can't get a confirmed one.
I'm currently testing it in the paypal sandbox,i created 2 buyers and one seller and i have enabled the "payment review" for everybody.
I tryed also different approach but i always get the same result.
Code:
file_put_contents('ipn.log',"\n>IPN\n",FILE_APPEND);
//Check the Payment ID,i pass it to the IPN by GET
if(!isset($_GET['pid'])|| !is_numeric($_GET['pid'])){
file_put_contents('ipn.log',"\n!!!IPN:INVALID PID(".$_GET['pid'].")!!!\n",FILE_APPEND);
exit('PID INVALIDO!');
}
//Logging errors
ini_set('log_errors', true);
ini_set('error_log', dirname(__FILE__).'/ipn_errors.log');
// instantiate the IpnListener class
require('ipnlistener.php');
$listener = new IpnListener();
//Use the sandbox instead of going "live"
$listener->use_sandbox = true;
//validate the request
try {
$listener->requirePostMethod();
$verified = $listener->processIpn();
}
catch (Exception $e) {
error_log($e->getMessage());
exit(0);
}
//Just for debug
file_put_contents('ipn.log',"\n###IPN:verifying...###\n",FILE_APPEND);
if($verified){//the payment is verified
file_put_contents('ipn.log',"\n###IPN:transaction verified(confirmed=".$_POST['payment_status'].")###\n".$listener->getTextReport(),FILE_APPEND);
/*
Once you have a verified IPN you need to do a few more checks on the POST
fields--typically against data you stored in your database during when the
end user made a purchase (such as in the "success" page on a web payments
standard button). The fields PayPal recommends checking are:
1. Check the $_POST['payment_status'] is "Completed"
2. Check that $_POST['txn_id'] has not been previously processed
3. Check that $_POST['receiver_email'] is your Primary PayPal email
4. Check that $_POST['payment_amount'] and $_POST['payment_currency']
are correct
Since implementations on this varies, I will leave these checks out of this
example and just send an email using the getTextReport() method to get all
of the details about the IPN.
*/
if($_POST['payment_status']=="Completed"){
//--check if the price is right and enable the user media--
confirm_payment($_GET['pid'],$_POST['payment_amount']);
file_put_contents('ipn.log',"\n###IPN:Transaction completed###\n".$listener->getTextReport(),FILE_APPEND);
}
}
else {
/*
An Invalid IPN *may* be caused by a fraudulent transaction attempt. It's
a good idea to have a developer or sys admin manually investigate any
invalid IPN.
*/
file_put_contents('ipn.log',"\n###IPN:ERROR###\n".$listener->getTextReport(),FILE_APPEND);
}
The debug log i created is always like this
> IPN <--it states that the ipn was correctly called
##IPN:verifying...### <--the IPN is verifying the transaction
##IPN:transaction verified(confirmed=Pending)<--the transaction is verified but it's NOT confirmed because it's pending,i can't enable the download!
Disable Payment Review. Payment Review will always place them in a Pending state.
That's actually the whole point of it; to be able to use negative testing and payment review in order to test 'negative' scenario's to verify your error handling.
I am not familiar with the class you are using, but this is what i have been using for PP IPN in all my work and it works like a charm, maybe one day i'll make my own Object Oriented way but for now this seems to be doing the trick and i hope it helps you.
(Just to get you on the right track, i am using the same file for incoming and outcoming messages to/from PP)
$sandbox="sandbox.";
$paypal_email="seller_XXXXX_biz#twbooster.com";
$item_id = "1XN12PJ";
$cost = "22.30";
$item_name = 'My Item';
$return_url = "http://www.example.com/return";
$cancel_url = "http://www.example.com/cancel";
$notify_url = "http://www.example.com/notify";
function check_txnid($tnxid){
global $link;
$sql = mysql_query("SELECT * FROM `payments_pending` WHERE `txnid` = '$tnxid'", $link);
return mysql_num_rows($sql)==0;
}
function check_price($price, $id){
$sql = mysql_query("SELECT `cost` FROM `orders` WHERE `id` = '$id'");
if (mysql_numrows($sql) != 0) {
$row = mysql_fetch_array($sql);
$num = (float) $row['cost'];
if($num - $price == 0){
return true;
}
}
return false;
}
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){ // Request TO Paypal
// Firstly Append paypal account to querystring
$querystring .= "?business=".urlencode($paypal_email)."&";
// Append amount& currency (£) to quersytring so it cannot be edited in html
$querystring .= "lc=CA&";
$querystring .= "currency_code=CAD&";
$querystring .= "item_number=".$item_id."&";
//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=".$cost."&";
//loop for posted values and append to querystring
foreach($_POST as $key => $value){
$value = urlencode(stripslashes($value));
$querystring .= "$key=$value&";
}
// Append paypal configs
$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.'.$sandbox.'paypal.com/cgi-bin/webscr'.$querystring);
exit();
}else{ // Response FROM Paypal
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$req .= "&$key=$value";
}
// assign posted variables to local variables
$data = array();
$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.'.$sandbox.'paypal.com', 443, $errno, $errstr, 30);
if(!$fp){
// HTTP ERROR : Do something to notify you
}
else {
fputs($fp, $header.$req);
$res = "";
while (!feof($fp)){
$res .= fgets($fp, 1024);
}
if(strpos($res, "VERIFIED")!==false){
// Validate payment (Check unique txnid & correct price)
$valid_txnid = check_txnid($data['txn_id']);
// $valid_price = check_price($data['payment_amount'], $data['item_number']);
$valid_price = check_price($data['payment_amount'], $_POST['item_number']);
// PAYMENT VALIDATED & VERIFIED!
if($valid_txnid && $valid_price){
$orderid = updatePayments($data);
if($orderid){
// Payment has been made & successfully inserted into the Database
}else{
// Error inserting into DB
}
}
else{
// Payment made but data has been changed : Do something to notify you
}
}
else{
if(strpos($res, "VERIFIED")!==false){
// PAYMENT INVALID & INVESTIGATE MANUALY! : Do something to notify you
}
}
fclose($fp);
}
}