Reputation: 46333
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);
}
?>
Upvotes: 2
Views: 1402
Reputation: 11
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";
Upvotes: 1
Reputation: 522510
hostname
If OpenSSL support is installed, you may prefix the
hostname
with eitherssl://
ortls://
to use an SSL or TLS client connection over TCP/IP to connect to the remote host.
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.
Upvotes: 3
Reputation: 59
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
Upvotes: 2
Reputation: 59
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
Upvotes: 2
Reputation: 59
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
Upvotes: 2
Reputation: 1276
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);
Upvotes: 0