问题
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);
}
?>
回答1:
$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. :)
回答2:
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";
回答3:
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);
来源:https://stackoverflow.com/questions/34958560/paypal-ipn-and-fsockopen