I was trying to follow the tutorial on paypal's developer site on setting up a basic IPN listener, and it looks nearly syntax for syntax like on their example. In fact when I started receiving the errors I've been receiving, I thought I would create a new ipn listener and use just the code they have in their example to see if it was my code or not and received the same errors.
Warning: fgets(): supplied argument is not a valid stream resource in \supergate\ipn.php on line 42
Warning: feof(): supplied argument is not a valid stream resource in \supergate\ipn.php on line 39
the errors on those line numbers are for these pieces of code at those lines:
while (!feof($fp)) //line 39
{
$res = fgets($fp, 1024); //line 42
here is the rest of the entire code
<?php
//Empty Header HTTP 200 OK reponse to ack 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_ammount =$_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 ack message of notification just received
$req = 'cmd=_notify-validate'; //add 'cmd=_notify-validate' to beginning of acknowledgement
foreach ($_POST as $key => $value) { //loop through the notification nv pairs
$value = urlencode(stripcslashes($value)); //encode these values
$req .= "&$key=$value"; //add the nv pairs to the acknowledgement
}
//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('ssl://sandbox.paypal.com', 443, $errno, $errstr, 30);
//send the http post request back to paypal for validation
fputs($fp, $header . $req);
while (!feof($fp))
{
$res = fgets($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0)
{
//WRITE TO EMAIL
*/
}
//--------------------------------------------------------------------------------------------------------------------
else if (strcmp($res,"INVALID") == 0)
{
//write to email
}
fclose($fp); //close the file
?>
Okay now the new warning I get is this:
Warning: fgets() [function.fgets]: SSL: An existing connection was forcibly closed by the remote host.
You're not checking the result of fsockopen() which returns FALSE upon failure.
$fp = fsockopen(...);
if ($fp === FALSE) {
exit("Could not open socket");
}
One clue from the documentation is the first parameter:
hostname
If OpenSSL support is installed, you may prefix the hostname with either ssl:// or tls:// to use an SSL or TLS client connection over TCP/IP to connect to the remote host.
Note that your example is using ssl:// - it is possible your server is not correctly configured with OpenSSL.
Related
I know that there have been a few changes to the PayPal IPN system as of May 15th, 2018. I happen to be in the middle of implementing my first IPN listener; and I'm not sure if I'm lost because of my resources (including SO posts, some dating back to 2009 on this subject) have been obsolesced by PayPal's changes, or merely because I am inexperienced in this field.
My suspicion is that I am pointing to the incorrect PayPal address:
$fh = fsockopen('ssl://www.paypal.com',443,$errno,$errstr,30);
//$fh = fsockopen('https://www.sandbox.paypal.com',80,$errno,$errstr,30);
//$fh = fsockopen('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr',80,$errno,$errstr,30);
The first address completes in a successful handshake with PayPal, but returns INVALID as the IPN response. The second two handshake, but don't pass the if (!$fh) test.
I have tried both the code below, and the CURL code found in Chris Muench's answer here: PayPal IPN returns invalid in sandbox
<?php
// Paypal IPN code
header('HTTP/1.1 200 OK'); // send header
$resp = 'cmd=_notify-validate';
foreach ($_POST as $parm => $var){
$var = urlencode(stripslashes($var));
$resp .= "&$parm=$var";
}
$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";
// 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);
//$fh = fsockopen('https://www.sandbox.paypal.com',443,$errno,$errstr,30);
//$fh = fsockopen('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr',443,$errno,$errstr,30);
if (!$fh) {
// if-fh does not work - cnxn FAILED
}
else{
// Connection opened, so spit back the response and get PayPal's view whether it was an authentic notification
fputs ($fh,$httphead.$resp);
while (!feof($fh)){
$readresp = fgets ($fh, 1024);
if (strcmp(trim($readresp),"VERIFIED") == 0){
// if-fh works - cnxn OPEN';
// WE ALL WIN!!
}
else if(strcmp(trim($readresp),"INVALID") == 0){
// A possible hacking attempt, or
// In my case, a possible hacking false-positive
}
}
fclose ($fh);
}
?>
I'm testing using the IPN simulator in my sandbox account.
None of the SO recommended solutions have worked.
Please help!
For the record, this url seems to be working fine: https://www.paypal.com/cgi-bin/webscr
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.
Having made a newbe error let me try again.
The website is for a non-profit and I added a PayPal "donation" page couple of years ago; done as a shopping cart (3 types of donations and/or membership). PP returns to a php script that uses the PDT data to build a thank-you page and set cookies for a double-opt-in mailing list. IPN sends the thank-you email, opt-in email, database, etc. works fine.
Now adding a PayPal option to ticket reservation pages. Again, a shopping cart that calls PayPal and seems to work fine in the sandbox; PayPal screen is as expected, and the two foo emails look correct. I pass a different return URL which gets called but bombs on the hand shake. The code is cut&past from the previous effort and based on the PayPal PDT example.
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-synch';
$header = "";
$tx_token = $_GET['tx'];
$req .= "&tx=$tx_token&at=$auth_token"; // see header for def of auth_token
// 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";
// If possible, securely post back to paypal using HTTPS
// Your PHP server will need to be SSL enabled
$fp = fsockopen ($socket, $port, $errno, $errstr, 30); // see header
if (!$fp)
{
// HTTP ERROR
mail($email, "PDT Error", "fsockopen error " . $errno . ": " . $errstr);
}
else
{
fputs ($fp, $header . $req);
// read the body data
$res = '';
$headerdone = false;
while (!feof($fp))
{
$line = fgets ($fp, 1024);
echo $line . "<br>";
if (strcmp($line, "\r\n") == 0)
{
// read the header
$headerdone = true;
}
else if ($headerdone)
{
// header has been read. now read the contents
$res .= $line;
}
}
The results from the added echo are:
HTTP/1.0 302 Found
Location: https://www.sandbox.paypal.com
Server: BigIP
Connection: close
Content-Length: 0
The var $auth_token, $socket and $port are set up with a "if ($test)" to switch between the sandbox and live. Obviously with 0 length payload, nothing else works.
I read here that about the new Auth_token. There are some other nits about testing for SUCCESS that I haven't gotten to, with no data to play with..
I can't remember, does the sandbox trigger the IPN? I'm getting nothing there either - set for the sandbox. Thanks for any suggestions about where to look.
I'm implementing an IPN listener for Paypal Adaptive Payments, I downloaded the sample code from here:
https://cms.paypal.com/cms_content/IT/it_IT/files/developer/IPN_PHP_41.txt
Then I made a transaction (with sandbox) but I think the sample is wrong because the code returns some errors in my error_log file:
[16-Jun-2013 16:11:34 UTC] PHP Warning: stripslashes() expects parameter 1 to be string, array given in /var/www/actions/IPNListener.php on line 7
[16-Jun-2013 16:11:34 UTC] PHP Notice: Undefined variable: header in /var/www/actions/IPNListener.php on line 12
[16-Jun-2013 16:11:34 UTC] PHP Notice: Undefined index: item_name in /var/www/actions/IPNListener.php on line 18
The Undefined index is not only "item_name" but ALL INDEXES!!!!!
The transaction works correctly and the IPN is called by paypal automatically after the transaction...but the paypal sample code does not work at all! Do you know how to fix it?
Try this one
<?php
//Build the data to post back to Paypal
$postback = 'cmd=_notify-validate';
// go through each of the posted vars and add them to the postback variable
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$postback .= "&$key=$value";
}
// build the header string to 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($postback) . "\r\n\r\n";
// Send to paypal or the sandbox depending on whether you're live or developing
// comment out one of the following lines
//$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);//open the connection
$fp = fsockopen ('ssl://www.paypal.com', 443, $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 Failed to connect
//error handling
}
else // if we've connected OK
{
fputs ($fp, $header . $postback);//post the data back
while (!feof($fp))
{
$response = fgets ($fp, 1024);
if (strcmp ($response, "VERIFIED") == 0) //It's verified
{
//do something
}
else if (strcmp ($response, "INVALID") == 0)
{
//the Paypal response is INVALID, not VERIFIED
// This implies something is wrong
}
} //end of while
fclose ($fp);
}
?>
I'm trying to set up the PayPal IPN on my web application, I copied from PayPal's documentation on an example PHP snippet which is found here.
However, when I'm testing with the PayPal's sandbox, sending an IPN with the simulator which is found here.
Now, when PayPal sends the IPN, I log the actions and data of the IPN, when trying to open an connection with fsockopen, it is NULL when I do var_export on it.
I don't understand why it's not going any further with the code when the fsockopen connection is NULL.
I'm using Codeigniter for my application, and this is the part of the code that fails:
if($this->uri->segment(3) == 'ipn')
{
$error_msg = '';
$error_msg .= " initiated ";
$req = 'cmd=_notify-validate';
$error_msg .= " \n\n req: " . var_export($req, true);
foreach($this->input->post() as $key => $value)
{
$value = urlencode(stripslashes($value));
$req .= "&" . $key . "=" . $value;
}
$error_msg .= " \n\n req: " . var_export($req, true);
$header = '';
$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";
$error_msg .= " \n\n headers: " . var_export($header, true);
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
$error_msg .= " \n\n fp: " . var_export($fp, true);
I use $error_msg to log the data, this is an example what is logged:
initiated
req: 'cmd=_notify-validate'
req: 'cmd=_notify-validate&test_ipn=1&payment_type=echeck&payment_date=17%3A30%3A40+Jan+03%2C+2012+PST&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&payer_id=TESTBUYERID01&address_name=John+Smith&address_country=United+States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San+Jose&address_street=123%2C+any+street&business=seller%40paypalsandbox.com&receiver_email=seller%40paypalsandbox.com&receiver_id=TESTSELLERID1&residence_country=US&item_name=something&item_number=DX4WYSur44CQICgO2lC%2FB10NmdaiPNH3xPZXQNAlfrEqpse0xnime22zaNXDFgbRrOL4Xsz4emkhqFw4JhOSHzCtaHt9%2B0p9p8xW6R71PVbFXNyEVjkPeHNdQm32PJg&quantity=1&shipping=3.04&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&txn_type=web_accept&txn_id=4014130¬ify_version=2.1&custom=xyz123&invoice=abc1234&charset=windows-1252&verify_sign=An5ns1Kso7MWUdW4ErQKJJJ4qi4-AN8d2a.xggmx9Dn4AgHpvPHJHTAp'
headers: 'POST /cgi-bin/webscr HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 969
'
fp: NULL
As you can see $fp is returning NULL on the last line of the logged data. Is there any idea why this is happening?
I can confirm I have OpenSSL enabled and installed on my server:
EDIT: Just tested fsockopen on port 80 to google.com, I still get NULL with no error number or message. So this problems occurs to every URL.
EDIT #2: Tested on my server by doing this:
fsockopen('ssl://www.paypal.com/cgi-bin/webscr', 443, $errno, $errstr, 30)
A PHP Error was encountered
Severity: Warning
Message: fsockopen(): unable to connect to
ssl://www.paypal.com/cgi-bin/webscr:443 (php_network_getaddresses:
getaddrinfo failed: nodename nor servname provided, or not known)
If anyone else is having the same problem, try using HTTPS instead of SSL
$fp = fsockopen ('https://www.paypal.com', 443, $errno, $errstr, 30);
And if your testing on Paypals Sandbox use:
$fp = fsockopen ('https://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
Be careful of $config['csrf_protection'] = TRUE; this will block all external POSTS as they will not come with a CSRF token, I've had this with my paypal IPN before, I needed to enable a crude but effective way to get the callback (in config.php):
if(stripos($_SERVER["REQUEST_URI"],'/paypal') === FALSE) {
// disable CSRF for the /paypal
$config['csrf_protection'] = TRUE;
} else {
$config['csrf_protection'] = FALSE;
}
I'm guessing this could be an issue, you would get null as no data would be captured as CI reviews your $_POST/$_GET vars for security reasons.
If I misunderstood your question and am way off track, just let me know via a comment.
Check List
sandbox url: "https://www.sandbox.paypal.com/cgi-bin/webscr";
like #jakub says CSRF must be disabled for your paypal controller
IPN wont validate on localhost, however you should still get vars back.
var_dump(fsockopen ('https://www.sandbox.paypal.com/', 443, $errno, $errstr, 30));
var_dump($_POST);
The variables $errno and $errstr probably hold the reason for the failure. Echo them out with your error message.