Basj
Basj

Reputation: 46333

Paypal IPN Verification Postback with HTTPS

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:

enter image description here


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

Answers (6)

andy3d
andy3d

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

deceze
deceze

Reputation: 522510

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.

Upvotes: 3

gri2a
gri2a

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

gri2a
gri2a

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

gri2a
gri2a

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

FluxCoder
FluxCoder

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

Related Questions