Couple of days ago, I asked a question about PayPal IPN txn_id check and I got an informative response. For those that want to find out what the role of txn_id is, its there to check if the transaction has not been previously processed. So now my question is, after checking and seeing that it doesn't exist, you store it (txn_id) in database and then the payment is processed, but how does PayPal know if the payment is ok to process and you found 0 rows with txn_id?
<?php
// PHP 4.1
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// 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);
// 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 (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
// check the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
}
else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
}
fclose ($fp);
}
?>
Um, not sure what you mean, but PayPal is kind of the one sending you this information to begin with? They are the ones telling you if a payment has gone through or not, and therefore knows this, obviously. They are just letting you know so you can do whatever you want to do with that information.
They send you this information so that you can transmit back to them and verify that the information is real, and not just someone POSTing crap information to your server. The txn_id is a completely unique ID "number" for that transaction on PayPal, so you should only ever see it once (theoretically). This serves two purposes:
Allows you to verify with PayPal by sending back all the information. Only one transaction exists with this ID, so every single piece of information you send back to them should match what is on file for that transaction. They then send either a Yay or Nay of whether it was valid. If it's Nay, you know it's fake information.
Allows you to determine if the transaction has already been processed by you, and prevents users from POSTing duplicate information to your server over and over. The transaction is valid, yes, but you don't want them to be getting 10 of a product when they only paid for one.
IPN (instant payment notification) has nothing to do with the processing of payments. IPN only comes into play after a transaction has occurred. The transaction ID is the ID of the transaction that has occurred.
If you are trying to prevent duplicate payments, you should utilize PayPal's invoice ID and duplicate payment settings within your PayPal account's profile.
Related
Hopefully someone will be able to help me or point me in the right direction. I have set up a Paypal IPN listener but don't seem to be getting any response (verified / invalid) back from the paypal after confirming the payment with paypal
I know the code works to some degree as the IPN History page confirms that a response was received back from the listener so paypal does not attempt to resend the message. If I place a code snippet to insert into my database outside of the verified/invalid if statement it works fine. It just does not do anything when I place the same code snippet inside the verified / invalid loop.
The PHP code I am using for the listener is as follows:
Hopefully someone will be able to help me or point me in the right direction. I have set up a Paypal IPN listener but
don't seem to be getting any response (verified / invalid) back from the paypal after confirming the payment with paypal
I know the code works to some degree as the IPN History page confirms that a response was received back from the listener
so paypal does not attempt to resend the message. If I place a code snippet to insert into my database outside of the verified/invalid
if statement it works fine. It just does not do anything when I place the same code snippet inside the verified / invalid loop.
The PHP code I am using for the listener is as follows:
<?php
// STEP 1 - be polite and acknowledge PayPal's notification
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
$record_id = $_POST['custom'];
$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'];
// 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.1\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.sandbox.paypal.com', 443, $errno, $errstr, 30);
$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) {
}
else {
fputs ($fh, $httphead . $resp);
while (!feof($fh))
{
$readresp = fgets ($fh, 1024);
if (strcmp ($readresp, "VERIFIED") == 0)
{
}
else if (strcmp ($readresp, "INVALID") == 0)
{
}
}
fclose ($fh);
}
?>
I'm using the Paypal IPN to update the status of an order to 2. Once we deliver the order, we manually change it to 3, but then sometimes it goes back to 2 on it's own (note that until it's 2, we can't even see it in our order panel).
Does the Paypal IPN update it again? What's causing this?
<?php
include('includes/config.php');
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// 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 (strpos($res, "VERIFIED") == 0) {
...
It sounds like IPN is updating it again, but there are a number of a reasons that could be happening.
If your IPN script has a problem with it that keeps from fully completing then PayPal will send the IPN again until it receives a 200 response back from your server. It's possible you have some sort of a syntax error or something that happens after the majority of the code runs. So the code runs, updates your system to a 2, but then fails after that, so PayPal sends the IPN again. Log into your PayPal account and check your IPN History to see if that could be what's going on.
Another reason could be if the original payment was pending for some reason. When IPN hit with the pending status it updates to 2, then it hits again once the payment status updates and updates it to a 2 again.
These are just a couple of ideas of what could possibly be happening. I would recommend adding some logging to your IPN script of some sort. A log file, database log, or even an email to yourself so you can see if it's running more than once for the same transaction. From there you could figure out exactly why it's running more than once.
You know those times where you feel really dumb after asking a question? This is one of those times.
I added AND 'status' = '0' to the query so even if it does check it later, it won't do anything.
this is the first time I am using Paypal to process a payment. I have set up a developer account, created some test merchant/buyer accounts and successfully created a cart that I sent to paypal sandbox. Here, using one of my buyers accounts, I can, again, successfully complete the purchase. In the account history of paypal, it shows that the transaction was completed. All good till now.
The problem lays in the return url, where I get the IPN. Here is the code I copied from paypal website.
<?php
include($_SERVER['WROOT'].'core/init.php');
//Put together postback info
$postback = 'cmd=_notify-validate';
foreach($_POST as $key =>$value){
$postback .= "&$key=$value";
}
// EMAIL $postback
// build the header string to post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sandbox.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";
// also tried with $fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);
if(!$fp){ //no conn
die();
}
//post data back
fputs($fp, $header . $postback);
while(!feof($fp)){
$res=stream_get_contents($fp, 1024);
if((strcmp($res, "VERIFIED")) == 0){
// EMAIL with a success notification
// update the database - THIS IS ONLY A SMALL TEST TO SEE IF THE TRANSACTION IS SUCCESSFUL -
$new = $dbh->prepare(" ISNERT INTO orders(txn_id) VALUES(:txn_id) ");
$new->execute(array( 'txn_id' => $_POST['txn_id'] ));
} else if ( strcmp ($res, "INVALID") == 0 ) {
// EMAIL with a error notification
// LOG THE ERROR TO A FILE
}
}
?>
Ok, first of all I don't do any check to see if the email, gross amount and other parameters are valid, that is something I will do after I can solve this problem.
Anyway, after a buyer pays, my database should update, but it does not.
What I did ?
First of all, I email me the $postback variable as soon as it's created and it worked. I got the email with a huge string of response and all the data was correct.
But the $res variable is not either VERIFIED or INVALID so anything past the fsockopen() does not work.
As I said, the payment on the paypal website is successfull. The documentation is fairly poor and I can't get an answer.
The one thing I want to add is that my website does NOT have a SSL certificate, but I do not store any of the customer data, everything is processed on the secure paypal website. Do I need a SSL certificate ?
One last thing. I tried using this class https://github.com/Quixotix/PHP-PayPal-IPN and the error log message is Invalid response status: 302
I'm developing a lightweight e-commerce solution that uses PayPal as the payment gateway. However, my IPN callback is constantly returning an INVALID response. I even tried using the sample PHP script provided by PayPal:
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// 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);
// 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 (!$fp) {
// HTTP ERROR
}
else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
// check the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
}
else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
}
fclose ($fp);
}
But as I say, I keep getting an INVALID response. This is in fact the response I get using a PHP class that writes the response to a log file:
[2011-06-02 19:18:49] - FAIL: IPN Validation Failed.
IPN POST data from PayPal:
test_ipn=1,
payment_type=instant,
payment_date=11:07:43 Jun 02, 2011 PDT,
payment_status=Completed,
payer_status=verified,
first_name=John,
last_name=Smith,
payer_email=buyer#paypalsandbox.com,
payer_id=TESTBUYERID01,
business=seller#paypalsandbox.com,
receiver_email=seller#paypalsandbox.com,
receiver_id=TESTSELLERID1,
residence_country=US,
item_name1=something,
item_number1=AK-1234,
quantity1=1,
tax=2.02,
mc_currency=USD,
mc_fee=0.44,
mc_gross=15.34,
mc_gross_1=12.34,
mc_handling=2.06,
mc_handling1=1.67,
mc_shipping=3.02,
mc_shipping1=1.02,
txn_type=cart,
txn_id=4362187,
notify_version=2.4,
custom=xyz123,
invoice=abc1234,
charset=windows-1252,
verify_sign=AjbdIvvDAW2fh1O9jAbEym4myX.WAV7-jCEiEWMqoSkewvM6L3Co6oUQ
This is from the official PayPal IPN test tool. So something between PayPal's sample code and test tool is causing my script to fail. Any one have any ideas?
what is encoding of your html/php page ? (charset=windows-1252) ?
I'm using PayPal 'buy it now' buttons on my website to sell products. Because I keep track of the number of units in stock for each product in a MySQL database and I'd like the inventory tracking in the system to be automated, I am using PayPal's Instant Payment Notification functionality to let me know when a purchase has been completed. When Paypal notifies my handler that a valid purchase has been made, the script updates my MySQL database by subtracting '1' from the inventory of the product purchased.
I've attached my IPN PHP code below that works successfully with Paypal buy it now buttons (one purchase at a time).
My Question is: I would like to substitute 'buy it now' buttons with PayPal's 'add to cart' buttons so that customers can purchase more than one product at a time. I'm unsure how I have to alter my code below to let it loop through all items purchased and update my database accordingly. Any help would be greatly appreciated!
The Code:
// Paypal POSTs HTML FORM variables to this page
// we must post all the variables back to paypal exactly unchanged and add an extra parameter cmd with value _notify-validate
// initialise a variable with the requried cmd parameter
$req = 'cmd=_notify-validate';
// go through each of the POSTed vars and add them to the variable
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// 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";
// In a live application send it back to www.paypal.com
// but during development you will want to uswe the paypal sandbox
// comment out one of the following lines
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);
//$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
// or use port 443 for an SSL connection
//$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) {
$item_name = stripslashes($_POST['item_name']);
$item_number = $_POST['item_number'];
$item_id = $_POST['custom'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross']; //full amount of payment. payment_gross in US
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id']; //unique transaction id
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
$size = $_POST['option_selection1'];
$item_id = $_POST['item_id'];
$business = $_POST['business'];
if ($payment_status == 'Completed') {
// UPDATE THE DATABASE
}
It would probably be easier for you to collect all item-ID's into an order-ID, then send that order-ID with your POST to PayPal. Once the IPN comes back with your details, check and calculate that the total sum of all items in the order-ID, matches the sum the IPN said was paid.
Query your database for the order-ID and get all the item-ID's connected to it, then decrease the stock number for each of the item-ID's.
Hope it helps. It's just one way of doing it!
It is not clear what you are asking. I wrote a PayPal IPN listener for Website Payment Pro. I started by looking here PayPal Documentation: IPN Sample Code and then changing the code as I needed. Please add some more details to your question.