...

View Full Version : Paypal IPN listener help



toddbuckles
10-29-2012, 01:01 AM
I think I have tried everything, but obviously not. I have the issue narrowed to the fsocketopen part. When I use the ssl:// address in the tutorials, i get 400 bad request returned. when I use www.sandbox.paypal.com, 80... It returns


POST /cgi-bin/webscr HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 837

cmd=_notify-validate&test_ipn=1&payment_type=instant&payment_date=10%3A32%3A00+Oct+28%2C+2012+PDT&payment_status=Completed&address_status=confirmed&paye...

Never returns "verified", although verified is passed in the original $_POST data under payer_status.

Code never makes it past if (strcmp ($res, "VERIFIED") == 0) {

Here is the code I am using...


<?PHP
require '../glob.inc.php';

// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';

foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}

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


// assign posted variables 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'];
$memnum = mysql_real_escape_string($_POST['custom']); //our users ID


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

//the two lines below are only so I can see what is going on by logging to my database. I am not sure how to create an error log file.
$payer_email = $res;
$log_query = mysql_query("INSERT INTO ipn_log VALUES ('','".$memnum."','".$txn_id."','".$payer_email."','".$header.$req."')");

if (strcmp ($res, "VERIFIED") == 0) {

//code never makes it to this point.

if ($payment_status=='Completed'){
$txn_id_check = mysql_query("SELECT 'txn_id' FROM 'log' WHERE 'txn_id'='".$txn_id."'");
if (mysql_num_rows($txn_id_check)!=1){

if ($receiver_email == 'myemailaddress'){

//if ($payment_amount == '20.00' && $payment_currency == 'USD') {
//add txn_id to database log table
$log_query = mysql_query("INSERT INTO ipn_log VALUES ('','".$memnum."','".$txn_id."','".$payer_email."')");
//update paid field
$update_paid = mysql_query("UPDATE members SET cknum='".$txn_id."', Amount='".$payment_amount." WHERE 'memid'='".$memnum."'");
//}

}
}
}
}
else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
}
fclose ($fp);
}
?>

Any help you can give would certainly be appreciated.

Thanks in advance,

TB

toddbuckles
11-09-2012, 03:11 PM
Just in case someone stumbles on this and has the same problem, I was unable to resolve it. Instead, I went the cURL route, and it worked beautifully. I am not sure why the difference, but it solved the problem. Since I had no experience with cURL, it was a learning curve for me, but I made it through.

Here is my code that worked: I connect through a glob.inc.php, search that for more info.




// STEP 1: Read POST data

// reading posted data directly from $_POST causes serialization issues with array data in POST
// read raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input'); //read-only stream that allows you to read raw data from the request body. Result is a string...
$raw_post_array = explode('&', $raw_post_data); //put the string into an array
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
$value = urlencode(stripslashes($value));
} else {
$value = urlencode($value);
}
$req .= "&$key=$value";
}


// STEP 2: Post IPN data back to paypal to validate

$ch = curl_init('https://www.paypal.com/cgi-bin/webscr'); //initialize the curl session, and store it in object $ch

//below are the options applied to $ch
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); //pretty obvious, sets http version. paypal requires 1.1

curl_setopt($ch, CURLOPT_POST, 1); //tells curl to do a regular post (the 1 option indicates 'true'

curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //maybe someone can explain, manual states 'TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly.'

curl_setopt($ch, CURLOPT_POSTFIELDS, $req); //finally, the data we are posting. Parsed above in the foreach statements

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); //set to true here to verify paypal certificate

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //could use help here. paypal states 1 to check the existence of a common name in the SSL peer certificate. 2 to check the existence of a common name and also verify that it matches the hostname provided. In production environments the value of this option should be kept at 2 (default value).

curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); //closes the connection as soon as it is finished processing without pooling data

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close')); //adds connection close to header

// In wamp like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below.
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');

if( !($res = curl_exec($ch)) ) { //and, we execute the cURL with all options set as above and log an error if it comes back empty. Should return 'VERIFIED'
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);

// STEP 3: Inspect IPN validation result and act accordingly

if (strcmp ($res, "VERIFIED") == 0) {

// assign posted variables 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'];
$payment_date = date('Y-m-d', strtotime($_POST['payment_date']));
$memid = mysql_real_escape_string($_POST['custom']); //our users ID

// check whether the payment_status is Completed
if ($payment_status=='Completed'){
// check that txn_id has not been previously processed
$txn_id_check = mysql_query("SELECT 'cknum' FROM 'members' WHERE 'cknum'='".$txn_id."'"); //cknum is a field specific to my db.
if (mysql_num_rows($txn_id_check)!=1){ //we don't already have that txn_id stored
// check that receiver_email is your Primary PayPal email
if ($receiver_email == 'yourpaypalemail@here.com'){
// check that payment_amount/payment_currency are correct
if ($payment_amount == 'your amount' && $payment_currency == 'USD') {
// process payment
//add values to database members table
$log_query = mysql_query("UPDATE members SET receipt='Yes', Amount='$payment_amount', paytype='Paypal', cknum='$txn_id', paid_thru='$payment_date' WHERE memid='$memid'"); //or whatever our sql statement is, or an email string, or whatever you want to execute.
}
}
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
$memid = mysql_real_escape_string($_POST['custom']); //our users ID
$log_query = mysql_query("UPDATE members SET receipt='No', cknum='INVALID' WHERE memid='$memid'");
}
?>


I really hope this helps someone.

TB

Fumigator
11-10-2012, 12:40 AM
Thanks for posting the solution you ended up using. I'm not sure why using fsockopen() wouldn't work for you-- nothing jumped out at me. It's what I use and I have no problems with it.

minder
11-10-2012, 12:57 AM
Thanks for posting the solution you ended up using. I'm not sure why using fsockopen() wouldn't work for you-- nothing jumped out at me. It's what I use and I have no problems with it.



My ISP has fsockopen disabled by default on its hosting accounts citing security reasons.

After I submitted a support ticket explaining why I needed it enabled on my hosting account (all legitimate reasons), they enabled it on my account. Perhaps the op's hosting account also has fsockopen disabled.

felgall
11-10-2012, 01:59 AM
A lot of web hosts only have fsocketopen enabled for specific ports - although 80 is probably the one that they are most likely to have open if they allow it on any. The hosts I have used have only ever allowed it on shared hosting on ports 80 and 443.

In my experience cURL is more likely to be disabled on the hosting which is presumably why Paypal used fsocketopen instead in their example code.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum