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);
}
?>
Related
I am testing with PayPals example IPN code which should return valid, or invalid for a transaction. I am testing with PayPals IPN simulator which should send some dummy data, and then validate it (returning "Valid").
I am testing with two separate web servers, both have OpenSSL installed and enabled.
On our local web server, we get this error message.
fgets(): SSL: An existing connection was forcibly closed by the remote host.
On our clients web server, with the same code, we get this:
fgets() [<a href='function.fgets'>function.fgets</a>]: SSL: Connection reset by peer in ...../paypal_ipn.php on line 43
PayPal doesn't seem to have a non-SSL version of this anymore.
paypal_ipn.php:
<?php
ini_set("log_errors", 1);
ini_set("error_log", "error.log");
// Send an empty HTTP 200 OK response to acknowledge receipt of the notification
header('HTTP/1.1 200 OK');
// Assign payment notification values 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'];
// Build the required acknowledgement message out of the notification just received
$req = 'cmd=_notify-validate'; // Add 'cmd=_notify-validate' to beginning of the acknowledgement
$req .= '&'.http_build_query($_POST);
// Set up the acknowledgement request headers
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n"; // HTTP POST request
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
// Open a socket for the acknowledgement request
//$fp = fsockopen('www.sandbox.paypal.com', 80, $errno, $errstr, 30);
//$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
if ($fp === FALSE) {
error_log("Could not open socket");
exit("Could not open socket");
}
// Send the HTTP POST request back to PayPal for validation
fputs($fp, $header . $req);
while (!feof($fp)) { // While not EOF
$res = fgets($fp, 1024); // Get the acknowledgement response
if (strcmp ($res, "VERIFIED") == 0) { // Response contains VERIFIED - process notification
// Send an email announcing the IPN message is VERIFIED
$mail_From = "IPN#example.com";
$mail_To = "Your-eMail-Address";
$mail_Subject = "VERIFIED IPN";
$mail_Body = $req;
file_put_contents("log.txt", "valid: " . $req, FILE_APPEND | LOCK_EX);
// Authentication protocol is complete - OK to process notification contents
// Possible processing steps for a payment include the following:
// Check that 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) { //Response contains INVALID - reject notification
// Authentication protocol is complete - begin error handling
// Send an email announcing the IPN message is INVALID
$mail_From = "IPN#example.com";
$mail_To = "Your-eMail-Address";
$mail_Subject = "INVALID IPN";
$mail_Body = $req;
file_put_contents("log.txt", "invalid: " . $req, FILE_APPEND | LOCK_EX);
}
}
fclose($fp); // Close the file
?>
I am not going to be using CURL, as that is whole other lot of problems! Can anyone see what could be causing these two (separate) errors?
EDIT:
I've just tested on another server running XAMPP (nearly everything enabled), and I now get this 'error':
PHP Warning: fgets(): SSL: The operation completed successfully.
Yet, the transaction doesn't get validated at all.
Right well after a day of struggling with this, I went home, and decided to tackle it this morning.
It looked like there was an issue with using fget / fputs. I could browse to the verification URL using the post data in my browser and could see that the URL I was using was working fine.
I couldn't use CURL due to some other issues and not enough time to solve them.
*Solution*:
Use file_get_contents() instead. This made things easier, and no need to send headers or anything else. This works flawlessly!
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?' . $req;
$res = file_get_contents($url);
I've had the same exact problem today but after a couple hours I finally located the root cause. It's perfectly fine to use Paypal's original PHP code but unfortunately it's fairly outdated ever since they switched over to HTTPS. In order to use fgets, you'll need to include the HOST in the header. For a quick fix, here is the code sample I used:
$parsed_url = parse_url('https://www.sandbox.paypal.com/cgi-bin/webscr'); // Development (sandbox) or production URL
$header = "POST $parsed_url[path] HTTP/1.1\r\n";
$header .= "Host: $parsed_url[host]\r\n";
Hope it works for you.
I have tried using a tutorial script for PayPal payment in the past and it worked. Now it doesn't. The script is simple, all I need is this one page for processing payment:
<?php
session_start();
include ('mydbconfig.php');
// payPal settings
$paypal_email = 'seller#yahoo.com';
$return_url = 'https://www.mytestsite.com/paypal-thanks.php';
$cancel_url = 'https://www.mytestsite.com/paypal-cancel.php';
$notify_url = 'https://www.mytestsite.com/paypal-notify.php';
$item_name = $_POST['item_name'];
$item_amount = $_POST['item_amount']; //price
// check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){
// 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;
// Redirect to paypal IPN
header('location:https://www.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) {
$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
header('location: https://www.mytestsite.com/paypal-error.php?tr='.$data['txn_id'].'&in='.$data['item_name'].'&pe='.$data['payer_email'].'&pa='.$data['payment_amount'].'&ps='.$data['payment_status']);
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
//payment accepted, insert transaction to my database
//function to insert transaction here
} else if (strcmp ($res, "INVALID") == 0) {
//something to do if failed
}
}
fclose ($fp);
}
}
?>
After the process is completed, I checked and the payment is paid successfully, but my function or anything I wrote in //function to insert transaction here won't be executed. In the end I'm forced to do the function on paypal-thanks.php page. Is there something's wrong in the script?
Is this script can be used to send more than one item purchasing? My cart is my own custom made and I only want to send Item name, number, and price detail, and total price to PayPal order summary.
I checked the other PayPal integration questions here, and most of them direct me to PayPal tutorial, documentation, or integration wizard which're confusing. I use this simple script before because I can't understand PayPal documentation (and the sample code, it didn't even let me know where to start) :(
And lastly my ultimate question, is this script is the correct and secure way to do a payment transaction?
use this
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
instead of
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
i think it is better to use CURL instead of socket
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.
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.