PayPal IPN and fsockopen

2019-09-03 16:08发布

Well I can't use cURL, the version installed on my web host that can't be changed doesn't support TLS.

I'm trying to use fsockopen now to see if i'll be able to use IPN.

It just hangs and browser throws connection timeout.

PHP 7, open_ssl is enabled

<?php
    header('HTTP/1.1 200 OK'); 

    $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'];

    $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
    $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://www.sandbox.paypal.com', 443, $errno, $errstr, 30);

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

    while (!feof($fp)) {                     // While not EOF

    $res = trim(fgets ($fp, 1024));

    if (strcmp ($res, "VERIFIED") == 0) {  // Response contains VERIFIED - process notification

      file_put_contents('./log_'.date("j.n.Y").'.txt', 'VERIFIED', FILE_APPEND);
    } 
    else if (strcmp ($res, "INVALID") == 0) { Response contains INVALID - reject notification
        file_put_contents('./log_'.date("j.n.Y").'.txt', 'INVALID', FILE_APPEND);
    }
  }
  //close
  fclose($fp);
?>

UPDATE

So it appears to be an issue with my current code. I used the following to see if I could talk to PayPal.

<?php

$site = "sandbox.paypal.com";//works
$port = 443;

$fp = fsockopen($site, $port, $errno, $errstr, 30);

if(!$fp){
  echo "<b>The port is NOT open!</b>";
}else{
  echo "<b>The port is open!</b>";
  fclose($fp);
}

?>

The result is The port is open!

UPDATE 2

Ok I'm now getting the response from the IPN Simulator using the below code. The other issues I was having was I failed to use the FQDN of my listener and left off http://.

So now the next problem is $res = trim($res); is always blank but my post variables aren't. Is this because it's a sandbox?

<?php 
    $debug            = 1;
    $sandbox          = 1;

    header('HTTP/1.1 200 OK'); 

    $item_name        = $_POST['item_name'];
    $item_number      = $_POST['item_number'];
    $payment_status   = $_POST['payment_status'];   
    if ($_POST['mc_gross'] != null) {
        $payment_amount   = $_POST['mc_gross'];
    } else {
        $payment_amount   = $_POST['mc_gross1'];
    }
    $payment_currency = $_POST['mc_currency'];
    $txn_id           = $_POST['txn_id'];
    $receiver_email   = $_POST['receiver_email'];
    $payer_email      = $_POST['payer_email'];
    $payment_date     = $_POST['payment_date'];
    $first_name       = $_POST['first_name'];
    $last_name        = $_POST['last_name'];
    $item_name        = $_POST['item_name'];

    $sandbox_url      = "sandbox.paypal.com";
    $prod_url         = "paypal.com";
    $verfiy_email     = "you email address the payment should be made to";

    if ($sandbox) {
        $url = $sandbox_url;
    } else {
        $url = $prod_url;
    }

    if ($debug) {
        error_log(date('[Y-m-d H:i e] '). "IPN URL: " . $url . PHP_EOL, 3, LOG_FILE);
    }

    $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

        if ($debug) {
            error_log(date('[Y-m-d H:i e] '). "POST Data: " . $key . " - " . $value . PHP_EOL, 3, LOG_FILE);
        }
    }

    //post back to PayPal system to validate (replaces old headers)
    $header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Host: www." . $url . "\r\n";
    $header .= "Connection: close\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    $fp = fsockopen ('sandbox.paypal.com', 443, $errno, $errstr, 30);

    //error connecting to paypal
    if (!$fp) {
        error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . $errno . " - " . $errstr . PHP_EOL, 3, LOG_FILE);
    }

    //successful connection    
    if ($fp) {
        fputs ($fp, $header . $req);

        while (!feof($fp)) {
            $res = fgets ($fp, 1024);
            $res = trim($res); //NEW & IMPORTANT

            if ($debug) {
                error_log(date('[Y-m-d H:i e] '). "DEBUG: Validation - " . $res . PHP_EOL, 3, LOG_FILE);
                error_log(date('[Y-m-d H:i e] '). "DEBUG: Payment Status - " . $payment_status . PHP_EOL, 3, LOG_FILE);
                error_log(date('[Y-m-d H:i e] '). "DEBUG: Receiver Email - " . $receiver_email . PHP_EOL, 3, LOG_FILE);
                error_log(date('[Y-m-d H:i e] '). "DEBUG: Verify Email - " . $verfiy_email . PHP_EOL, 3, LOG_FILE);
            }

            //I don't see this
            if (strcmp($res, "VERIFIED") == 0) {
                //insert order into database    
                if ($debug) {
                    error_log(date('[Y-m-d H:i e] '). "Response Message: " . "VERIFIED" . PHP_EOL, 3, LOG_FILE);
                }
            }

            //I don't see this
            if (strcmp ($res, "INVALID") == 0) {
                //insert into DB in a table for bad payments for you to process later
                if ($debug) {
                    error_log(date('[Y-m-d H:i e] '). "Response Message: " . "INVALID" . PHP_EOL, 3, LOG_FILE);
                }
            }

            if (strcasecmp ($payment_status, "Completed") == 0 && strcasecmp($receiver_email, $verfiy_email) == 0) {
                if ($debug) {
                    error_log(date('[Y-m-d H:i e] '). "Response Message: " . "Payment VERIFIED" . PHP_EOL, 3, LOG_FILE);
                }
            } else {
                if ($debug) {
                    error_log(date('[Y-m-d H:i e] '). "Response Message: " . "Payment INVALID" . PHP_EOL, 3, LOG_FILE);
                }
            }
        }

        fclose($fp);
    }
?>

3条回答
Viruses.
2楼-- · 2019-09-03 16:23

I encountered the same issue, where the sandbox was returning an empty result. My code was similar to the Update 2 provided by Tsukasa.

I had to change sandbox.paypal.com to ipnbp.sandbox.paypal.com, and prefix it with ssl. The fsocket call looks like:

$fp = fsockopen ('ssl://ipnbp.sandbox.paypal.com', 443, $errno, $errstr, 30);
查看更多
手持菜刀,她持情操
3楼-- · 2019-09-03 16:33

$res is cycling through the request response, reading it 1024bytes at times, leaving it just empty on its last iteration.

If you try to log somewhere the $res variable through the loop, you'll just find the response line-by-line, eg.

HTTP/1.0 302 Found 
Location: https://www.sandbox.paypal.com
Server: BigIP
Connection: close 

It's not solving but.. just an idea. :)

查看更多
Juvenile、少年°
4楼-- · 2019-09-03 16:37

Thank you for posting! I was having a similar issue where the IPN was sent, my server validated it using fsocket, code was executed to write to database, but PayPal still reported a 'delivery status' of 'failed' after retrying several times causing duplicates in my database. After reviewing your code I found that the tutorial I've been working off of left out this line causing the request from PayPal to timeout instead of show 200:

    $header .= "Connection: close\r\n";
查看更多
登录 后发表回答