3

We have been working to update our PayPal IPN scripts in accord with seom recent changes. Here is the instruction in part by PayPal ...

Your Action Required before February 1, 2013 You will need to update your IPN and/or PDT scripts to use HTTP 1.1, and include the "Host: www.paypal.com" and "Connection: close" HTTP headers in the IPN and PDT scripts.

We did that and the IPNs failed. PayPal Merchant Technical Service asked that we move to a SLL connection. Previously our connection to PayPal had been based upon ...

fsockopen ('www.paypal.com', 80, $errno, $errstr, 30)

Now it is ...

fsockopen ('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30)

We had to overcome an SSL problem with our host config to get this working, but it now makes the connection. However IPN requires us to post back to PayPal in order to receive a "VERIFIED" notification, at which point the script can do it's local processing based upon a confirmed payment.

This is where it fails.

The lines of the sample PayPal script are:

if (!$fp) {
    // HTTP ERROR

} else {
    fputs ($fp, $header . $req);
    while (!feof($fp)) {
        $res = fgets ($fp, 1024);

        if (strcmp (trim($res), "VERIFIED") == 0) {  ... rest of script

But the response comes back blank. I have inserted a diagnostic to email me the value of $res at each stage. If I do this with the old script it comes back with a "VERIFIED" value just fine. The old scripts on several sites have been stable for years. I have now tried these new updates on two distinct sites on two different servers and the result is the same.

When looking at the Apache logs I see this error:

PHP Warning: fgets() [function.fgets]: SSL: Connection reset by peer in /home/[sitename]/public_html/[IPN scriptname].php on line 145

So it appears that PayPal shuts down the connection without sending a Verified response. However PayPal support deny that this is a problem at this end since the connection is opening in the first place.

I have spent several days trying to fix this. I still have the deadline of Feb 1st after which PayPal say my old scripts may not work, but neither do the new ones.

Anyone else have any experience with this?

Sam Hosseini
  • 813
  • 2
  • 9
  • 17
flamegri11ed
  • 53
  • 2
  • 8
  • Did PayPal tell you to switch to HTTPS? Did you try running your old script with just the two changes PayPal actually said? **use HTTP 1.1** and **include the "Host: www.paypal.com" and "Connection: close" HTTP headers**. Changing to HTTP 1.1 does not, in of itself, require changing to HTTPS. – Remy Lebeau Jan 25 '13 at 00:19
  • I am seeing the same response, too have placed code to email me the contents of the response. I am expecting "VERIFIED" or "INVALID" but nothing is being returned). I am using cURL tho. We share the same problem, I'll share the solution if I come to it 1st. – Solid I Jan 31 '13 at 00:11
  • ANy updates on this? Same problem in 2014. – Ben Racicot Feb 20 '14 at 16:10
  • Same issue, anyone know what might cause this? – richbai90 Feb 21 '14 at 22:35

4 Answers4

2

If anyone else has the same issue, CURL seems to be the recommend choice for IPN by PayPal.

Check out there code sample on Github at: https://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.php

mattauckland
  • 483
  • 1
  • 7
  • 17
  • +1 I tried the other answers here and no longer got the "Connection reset" error, but the code didn't parse the response correctly until I switched to the code using CURL from PayPal's Github. – Matt Browne Mar 26 '15 at 20:55
2

After struggle this issue a few hours. I got it right.

first, the header should be correct.

$header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";

second, the buffer should be big enough to receive whole msg from Paypal. the original sample code use 1024, but the result got truncated.

$res = stream_get_contents($fp, 2048);

After the two, the result return good.

jp_eagle
  • 148
  • 1
  • 8
1

Finaly found an answer! Paypal IPN Script, issue with feof and fgets

remove while(!feof($fp) ... and change it to $res = stream_get_contents($fp, 1024); also add $header .= "Conection: Close" to make sure that paypal closes the connection.

Community
  • 1
  • 1
richbai90
  • 4,994
  • 4
  • 50
  • 85
0

UPDATE: Jan 7th 2016 I noticed that the below works very well for tls://www.sandbox.paypal.com but when you go live to tls://www.paypal.com connection is refused! (It doesn't work) I found the problem is in the headers, sandbox and production level live paypal needs different headers! This is a bug but for you to get it work both in production level and sandbox, please use these headers respectively:

Production Level (Live PayPal Headers):

  $header  = "POST /cgi-bin/webscr HTTP/1.1\r\n"; 
  $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";

Sandbox PayPal Headers:

$header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";

* The code below is the same and it works fine, just replace the headers respectively.* Here is the rest of the code (and answer):

I checked all of the answers here and there to make it work in Jan 5th 2016. All of them had good points, but didn't work for the whole picture.

So first, here is the complete code that actually works and then I'll go through it:

<?php

// =========================================================================
// PayPal Official PHP Codes and Tutorial:
// https://developer.paypal.com/webapps/developer/docs/classic/ipn/gs_IPN/
// =========================================================================

// 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

foreach ($_POST as $key => $value) {         
    // Loop through the notification NV pairs
    $value = urlencode(stripslashes($value));  // Encode these values
    $req  .= "&$key=$value";                   // Add the NV pairs to the acknowledgement
}


// Set up the acknowledgement request headers
// HTTP POST request
$header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";

// Open a socket for the acknowledgement request
$fp = fsockopen('tls://www.sandbox.paypal.com', 443, $errno, $errstr, 30);

// Send the HTTP POST request back to PayPal for validation
fputs($fp, $header . $req);

// Log the transaction:
file_put_contents("paypal_post.txt",  date('Y-m-d H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND);

// While not EOF
while (!feof($fp)) {
    // Get the acknowledgement response
    // $res = fgets($fp, 1024);  
    $res = stream_get_contents($fp, 1024);
    $responses = explode("\r\n", $res);
    foreach ($responses as $response) {
        if (strcmp ($response, "VERIFIED") == 0) {
            // Response contains VERIFIED - process notification

            // 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 ($response, "INVALID") == 0) {
            // Response contains INVALID - reject notification
            // Authentication protocol is complete - begin error handling

        }
    }
}
// Close the file
fclose($fp);
?>

OK, now you have the code that works to listen for a PayPal IPN, re-compile and send it back to PayPal for verification, receive the headers back, process the headers line by line to find the VERIFIED validation.

Which should be provided by PayPal all-in-one package in a working condition but their tutorial lacked very critical parts and it didn't work for me and many here on this thread.

So now what are the critical parts that makes it work: First, the headers provided by paypal didn't work, I found @jp_eagle 's headers work perfectly.

And paypal was wrong for $res = fgets($fp, 1024); as well...

However you dont need $res = stream_get_contents($fp, 2048); as @jp_eagle suggested, $res = stream_get_contents($fp, 1024); is just fine.

while (!feof($fp)) {} loop should stay there to make it work! Yes, even with the switch from fgets() to stream_get_contents() it should stay there!

@richbai90 was wrong for the suggestion to remove it. Try the other way and it won't work...

And the most critical part to get the VALIDATION is this loop:

    $res = stream_get_contents($fp, 1024);
    $responses = explode("\r\n", $res);
    foreach ($responses as $response) {}

Now you can look for the VALIDATION in each line here:

if (strcmp ($response, "VERIFIED") == 0) {}

Also logged the initial complete incoming POST request form paypal before anything else:

// Log the transaction: file_put_contents("paypal_post.txt", date('Y-m-d H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND);

That's it. Now go here and login with your paypal to see if it works: https://developer.paypal.com/developer/ipnSimulator/

Give your url select "cart checkout" for example and send a test IPN. If it works all good, go ahead and remove the .sandbox from the above paypal URL from www.sandbox.paypal.com to www.paypal.com and you are live.

Fill in the VERIFIED and INVALID sections in the if-else statements with your database actions and it is done.

Tarik
  • 4,270
  • 38
  • 35