I want to send a request (POST or GET) and don't wait for answer
I have the code:
$fp = stream_socket_client("tcp://requestb.in:80", $errno, $errstr, 30, STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
$out = "GET /15b3x0v1 HTTP/1.1\r\n";
$out .= "Host: requestb.in\r\n";
$out .= "Connection: Close\r\n\r\n";
fputs($fp, $out);
//usleep(500);
fflush($fp);
fclose($fp);
Without the usleep, it doesn't work (the server doesn't receive the request). Why have i to wait 500ms before close the socket ?
According to new security requirements (2016, 2017 and 2018), it seems that HTTPS will be required for exchange between server and Paypal, during an "IPN". This question is linked to this subject and also this.
How should we adapt this PHP IPN code?
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.paypal.com:80\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
$req = 'cmd=_notify-validate';
...
fputs ($fp, $header . $req);
Would replacing the two occurences of www.paypal.com by https://www.paypal.com be enough?
Also, is the fact my shop website is not HTTPS a problem, will this connection be refused?
Here is part of the email received from Paypal:
Edit (2018/06/22), here is the actual IPN code, after applying the accepted answer code. Strangely, I still get: "IPN Verification postback to HTTPS. Update needed: YES". So this means the following code is still not 100% compliant to HTTPS. Why?
<?php
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = trim(urlencode(stripslashes($value)));
$req .= "&$key=$value";
}
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('tls://www.paypal.com', 443, $errno, $errstr, 30);
// variables
$item_name = $_POST['item_name'];
$business = $_POST['business'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
// and many more
if (!$fp)
{
// HTTP ERROR
} else
{
fputs ($fp, $header . $req);
while (!feof($fp))
{
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0)
{
// send email to customer, etc.
}
}
fclose ($fp);
}
?>
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.
http://www.php.net/fsockopen
The port would also need to change to 443. So:
$header .= "Host: www.paypal.com\r\n";
...
$fp = fsockopen('ssl://www.paypal.com', 443, $errno, $errstr, 30);
...
fputs ($fp, $header . $req);
https:// would not work because you're opening a socket, which is a low-level transport. HTTP is an application level protocol on top of that, which the socket doesn't know or care about. At the socket level it's a TLS connection.
Also, is the fact my shop website is not HTTPS a problem, will this connection be refused?
What kind of connection a browser has to your server is irrelevant and nobody knows that. You're opening a socket from a PHP program to Paypal, you may as well be doing that directly from the command line without any "HTTP" connection involved at all.
I would suggest you abandon fsocket in favour of curl. Its a lot more reliable (I think). Ive just updated my curl component within my ipn.php to be compliant with Paypals requirements of HTTP1/1 and TLS1.2 (sslversion=6)
The code is
//NOW SEND IT ALL BACK TO PAYPAL AS CONFIRMATION//
// USING CURL AS PHP FSOCKOPEN IS LESS RELIABLE //
$curl_result=$curl_err='';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$paypal_url);
curl_setopt($ch, CURLOPT_CAINFO, "cacert.pem");
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
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, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$curl_result = #curl_exec($ch);
$curl_err = curl_error($ch);
curl_close($ch);
It works fine
Further to my comments about Curl, Paypal developers have supplied several sets of source code for IPN. They supply C++, Python, ASP, PHP, and a whole bunch more. You will find them on github. All of the PHP solutions use Curl. Even though some of the uploads are a few years old they must have known that Paypal would update their full comms (for IPN) to HTTPS.
Even with some of the older packages, a simple adding of 1 line of code to set SSL version to 6 and the old codes are good to go for Paypals new requirements
The link at https://github.com/paypal/ipn-code-samples is a compilation of codes in Python, Ruby, Perl, Asp and a few others including PHP.
A direct link to the PHP component is at https://github.com/paypal/ipn-code-samples/tree/master/php
This is the one I use because its already HTTP1/1 and TLS1.2 "ready" and therefore ticks all the boxes for Paypal's new security compliance.
There area few other github samples, some over 6 years old, but this is the most up to date
You should probably also change that HTTP/1.0 to HTTP/1.1 now, as per PayPal's latest requirements. So that's:
$header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
In addition to deceze's answer, PayPal's update now requires you to use ipnpb.paypal.com
Try using this code instead.
$fp = fsockopen('ssl://ipnpb.paypal.com', "443", $err_num, $err_str, 60);
I'm trying to set up a simple PHP script to emulate an audio file with fpassthru.
However, the source that I'm pulling the content from naturally adds headers to the actual data-stream fetched by my PHP script, which I think is the reason my setup doesn't work.
What I'd like to do is skip/ignore the first 13 lines/289 bytes of the data fetched by fpassthru, so the stream is "clean" and uncontaminated with headers from the original server - is this possible?
here's my current code:
// Make socket connection
$errno = "errno";
$errstr = "errstr";
$fp = fsockopen($ip, $port, $errno, $errstr, 30);
if(!$fp) exit;
// Create send headers
fputs($fp, "GET /$path HTTP/1.0\r\n");
fputs($fp, "Host: $ip\r\n");
fputs($fp, "User-Agent: Script\r\n");
fputs($fp, "Accept: */*\r\n");
fputs($fp, "Connection: close\r\n\r\n");
// Write the returned data back to the resource
fpassthru($fp);
// close the socket when we're done
fclose($fp);
I have been using a php script as IPN listener for years and it works fine.
A few days ago, Paypal set a message warning that October 7, 2013 code must be using HTTP/1.1. So I copied the sample code provided in lieu of my current headers :
-- new code---
// post back to PayPal system to validate
$header="POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .="Content-Type: application/x-www-form-urlencoded\r\n";
$header .="Host: www.paypal.com\r\n";
$header .="Connection: close\r\n\r\n";
-- Existing code ---
$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
Now the script no longer works. I get a 401 error :(
What's wrong ? I am growing extremely concerned, if new code does not work, that it will no work on time. Looked everywhere and could not find a definitive answer. I would like to get this working.
Help !
Try this code:
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Connection: close\r\n";
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
I'm trying to upload a file using cURL and the PUT method, I have already a function that works using fsockopen but i would like to migrate it to cURL.
The function that uses fsockopen receives the content of a file, the filename and the credentials for auth and make the request:
function put_file($content, $filename, $username, $pass)
{
$header = "PUT /upload?username=".urlencode(user_name)."&passwd=".urlencode($pass)."&filename=".urlencode($file_name)." HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($content) . "\r\n\r\n";
$fp = #fsockopen("ssl://URL", 443, $errno, $errstr, 30);
if(!$fp)
{
return "ERROR";
}
else
{
fputs ($fp, $header.$content);
while (!feof($fp))
{
$res .= fread ($fp, 1024);
}
fclose($fp);
}
}
I have been trying to migrate that function to cURL, but I don't know how to do it without the need of have a "real" file on my filesystem. The only cURL options I know for this are CURLOPT_INFILE and CURLOPT_INFILESIZE, but I don't have the file (and don't want to write it to disk and after open it).
What I need is to send the "content" of the file, just like the fsockopen version does. How can this be achieved with cURL?
Thank you in advanced.
You could use php://temp wrapper, which is a temporary file stream in PHP.
First you write the data to the stream (don't forget to use rewind() so cURL will read all data):
$fp = fopen("php://temp", "r+");
fputs($fp, $content);
rewind($fp);
Then when setting up the cURL just use:
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($content)); #adding missing bracket
And at the end close temp file handler (optional):
fclose($fp);