I am testing my IPN notifier using the PayPal IPN simulator. When verifying the response using:
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
I get INVALID
however when I verify the response using this one (the correct one):
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
it does not return either VERIFIED OR INVALID, just false.
Full code here:
// read the data send by PayPal
$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 ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
//http error
}
else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
//verified
}
else if (strcmp ($res, "INVALID") == 0) {
//invalid
}
}
fclose ($fp);
}
Ended up using cURL which seems to work https://gist.github.com/CodeAngry/5957044
Related
My PHP code currently uses the following to Postback to PayPal. How do I update this to meet the new payment security standards coming into effect?
$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.paypal.com', 80, $errno, $errstr, 30);
if (!$fp)
{
//an error occurred...
}
else
{
fputs ($fp, $header . $req);
while (!feof($fp))
{
$res = fgets ($fp, 1024);
if(strcmp ($res, "VERIFIED") == 0)
{
//all is well...
}
}
}
To enable IPN Verification Postback to HTTPS, change your code as follows,
$fp = fsockopen( 'tls://ipnpb.paypal.com', 443, $errno, $errstr, 30);
PayPal recommends to use ipnpb.paypal.com endpoint in production environment.
Also, you need to change HTTP/1.0 to HTTP/1.1 to comply with PayPal upgrades.
The Header you need to submit,
$header = "POST /cgi-bin/webscr HTTP/1.1\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";
$header .= "Connection: close\r\n\r\n";
For the sandbox change,
$header .= "Host: www.paypal.com\r\n";
to
$header .= "Host: www.sandbox.paypal.com\r\n";
The new code suggested above has now had a chance to be tested.
This did NOT work:
// 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.1\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen( 'tls://ipnpb.paypal.com', 443, $errno, $errstr, 30);
This is what works:
// 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.paypal.com', 80, $errno, $errstr, 30);
What to do about using HTTP/1.0 and posting to www.paypal.com rather than using HTTP/1.1 and posting to tls://ipnpb.paypal.com?
There's a couple of things that need to be done:
You're still on HTTP/1.0; you need to switch to HTTP/1.1.
You need to switch to using TLS.
Most PHP setups nowadays should have cURL installed, so the easiest thing to do would be to use cURL. To do this, the last four lines of your code would change to the following:
$curl = curl_init( 'https://ipnpb.paypal.com/cgi-bin/webscr' );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, $req );
$response = curl_exec( $curl );
if( 'VERIFIED' == trim( $response ) ) {
// IPN verified by PayPal
}
I have a similiar issue with Paypal IPN verification and using this code
// post back to PayPal system to validate
// 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.paypal.com', 80, $errno, $errstr, 30);
If I understand the solution correctly, the new code should look like
// post back to PayPal system to validate
$header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: ipnpb.paypal.com\r\n";
$header .= "Connection: close\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen( 'tls://ipnpb.paypal.com', 443, $errno, $errstr, 30);
I have a problem when integrating paypal IPN to my website.
The warning is
"Warning: fsockopen() [function.fsockopen]: SSL operation failed with
code 1
OpenSSL Error messages:
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert
handshake failure in /home/..." on this line: $fp = fsockopen
('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
I use fsockopen as I can only find working sample few months ago. But when I tried 2 days ago, it shown the error. Here's my code:
<?php
$return_url = "http://www.domain.com.au/member/payment.php";
$cancel_url = "http://www.domain.com.au/member/request.php";
$notify_url = "http://www.domain.com.au/member/payment.php";
// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])){
$querystring = '';
$paypalQueryString = "?business=".urlencode('my-facilitator#yahoo.com')."&";
$paypalQueryString .= "item_name=".urlencode('Item Name')."&";
$paypalQueryString .= "amount=".urlencode($totalPrice)."&";
$paypalQueryString .= "cmd=_xclick&no_note=1&lc=AU¤cy_code=AUD&bn=PP-BuyNowBF%3Abtn_buynow_LG.gif%3ANonHostedGuest&";
$paypalQueryString .= "first_name=".urlencode($niceName)."&";
$paypalQueryString .= "last_name=&";
$paypalQueryString .= "payer_email=".urlencode($payerEmail)."&";
$paypalQueryString .= "item_number=".urlencode($accountID)."&";
$paypalQueryString .= "submit=Submit+Payment&";
$paypalQueryString .= "return=".urlencode(stripslashes($return_url))."&";
$paypalQueryString .= "cancel_return=".urlencode(stripslashes($cancel_url))."&";
$paypalQueryString .= "notify_url=".urlencode($notify_url);
// Redirect to paypal IPN
header('location:https://www.sandbox.paypal.com/cgi-bin/webscr'.$paypalQueryString);
//header('location:https://www.paypal.com/cgi-bin/webscr'.$paypalQueryString);
exit();
}
else {
//Response from Paypal
//echo "<pre>".var_dump($_POST)."</pre>";
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value);// IPN fix
$req .= "&$key=$value";
}
// assign posted variables to local variables
$data['item_name'] = $_POST['item_name'];
$data['item_number'] = $_POST['item_number'];
$data['payment_status'] = $_POST['payment_status'];
$data['payment_amount'] = $_POST['mc_gross'];
$data['payment_currency'] = $_POST['mc_currency'];
$data['txn_id'] = $_POST['txn_id'];
$data['receiver_email'] = $_POST['receiver_email'];
$data['payer_email'] = $_POST['payer_email'];
$data['custom'] = $_POST['custom'];
$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 ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
//$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
echo "HTTP Error";
}
else {
fputs($fp, $header . $req);
$res = stream_get_contents($fp, 2048);
if (strpos(trim($res), "VERIFIED") !== false) {
// update transaction --> paid status
}
else if (strpos (trim($res), "INVALID") !== false) {
}
else{
}
fclose ($fp);
}
}
?>
Hope anyone can help me. Thanks
I'm in a similar situation, but I'm using cURL instead of fsockopen. PayPal just started requiring all connections to the sandbox be done over TLS 1.2 (and HTTP 1.1).
https://devblog.paypal.com/upcoming-security-changes-notice/
Maybe try changing this line
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
to this?
$fp = fsockopen ('tls://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
You have to use the new CURL approach and ditch the old fsockopen way, this seems to be part of the new Paypal TLS 1.2 campaign.
I tested tls:// ssl:// and just plain https://www.sandbox.paypal.com/cgi-bin/webscr which all failed until I implemented the PHP Curl apprach below.
https://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.php
<?php
// CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
// Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
// Set this to 0 once you go live or don't require logging.
define("DEBUG", 1);
// Set to 0 once you're ready to go live
define("USE_SANDBOX", 1);
define("LOG_FILE", "./ipn.log");
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$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";
}
// Post IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data
if(USE_SANDBOX == true) {
$paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
$paypal_url = "https://www.paypal.com/cgi-bin/webscr";
}
$ch = curl_init($paypal_url);
if ($ch == FALSE) {
return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
if(DEBUG == true) {
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
}
// CONFIG: Optional proxy configuration
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below. Ensure the file is readable by the webserver.
// This is mandatory for some environments.
//$cert = __DIR__ . "./cacert.pem";
//curl_setopt($ch, CURLOPT_CAINFO, $cert);
$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
{
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
exit;
} else {
// Log the entire HTTP response if debug is switched on.
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
}
// Inspect IPN validation result and act accordingly
// Split response headers and payload, a better way for strcmp
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your PayPal email
// check that payment_amount/payment_currency are correct
// process payment and mark item as paid.
// 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'];
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
// Add business logic here which deals with invalid IPN messages
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
}
}
?>
I got my Paypal ipn to work. But I changed some things and now I can not remember what I changed. I used a series of INSERTS to monitor where the process of the ipn fails. It looks like it does not pass this part of my ipn. Here is the code:
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp($res, "VERIFIED") == 0) {
Why is my Paypal ipn not working?
I had also struggled a lot with paypal ipn last week,below is my working code you can refer
header('HTTP/1.1 200 OK');
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
$fp = fsockopen('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
fputs($fp, "POST /cgi-bin/webscr HTTP/1.1\r\n");
fputs($fp, "Host: www.sandbox.paypal.com\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ".strlen($req)."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $req. "\r\n\r\n");
$ipn_response = '';
while (!feof($fp)) {
$ipn_response .= fgets($fp, 1024);
}
fclose($fp);
if ( eregi("VERIFIED",$ipn_response) ) {}
i have been working on a scraper tool that rips google search results and then crawls the results websites looking to match specific items.
I'm having an issue with cURL though. I have come accross a site that is causing curl to go into an infinite loop.
website in question.
http://www.darellyelectrical.com/
when i open up my packet sniffer and look through tcp http packets ive found the same request is being sent over and over again.
i can not pinpoint the reason why, I have no trouble with any other websites.
I have tried setting the following curl options
curl_setopt($this->sessions[$key], CURLOPT_TIMEOUT, $timeout);
curl_setopt($this->sessions[$key], CURLOPT_MAXREDIRS, 2);
curl_setopt($this->sessions[$key], CURLOPT_CONNECTTIMEOUT, 1);
be great if someone could test that url with curl and let me know if the issue persists.
thanks
EDIT**
function sck_send()
{
$host = "www.darellyelectrical.com";
$path = "";
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /".$path." HTTP/1.1\r\n";
$out .= "Host: ".$host."\r\n";
$out .= "Connection: Close\r\n\r\n";
$data = "";
fwrite($fp, $out);
while (!feof($fp))
{
$data .= fgets($fp, 128);
}
fclose($fp);
echo $data;
}
}
sck_send();
this will produce the loop same as curl.
That server needs the User-Agent header to be included or it does not respond. PHP's curl doesn't set this by default and it wouldn't be included in a socket request unless you specify it. The code below works for me:
<?php
function sck_send() {
$host = "www.darellyelectrical.com";
$path = "";
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /".$path." HTTP/1.1\r\n";
$out .= "Host: ".$host."\r\n";
$out .= "User-Agent: Mozilla/5.0 \r\n";
$out .= "Connection: Close\r\n\r\n";
$data = "";
fwrite($fp, $out);
while (!feof($fp)) {
$data .= fgets($fp, 128);
}
fclose($fp);
echo $data;
}
}
sck_send();
I have asked my paypal api to send email of $res to see what the response is. Inside of the while (!feof($fp)) {}
This is what i'm getting from $res to my email
HTTP/1.0 400 Bad Request
Server: BigIP
Connection: close
Content-Length: 19
Invalid Host header
This is my paypal code.
$req = 'cmd=_notify-validate';
$req .= payment_safe_check ($_POST);
$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";
$mode = strtolower($paymod_data['PAYMENT_MODE']);
mail("to email", "subject",serialize($_POST) );
if ($mode == 'test')
{
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
}
else
{
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
}
if (!$fp)
{
// HTTP ERROR
die ("Error");
}
else
{
// NO HTTP ERROR
fputs ( $fp, $header . $req );
while (!feof($fp))
{
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0)
{
processing ( $_POST );
}
else if (strcmp ($res, "INVALID") == 0)
{
}
}
fclose ($fp);
}
This doesn't work if (strcmp ($res, "VERIFIED") == 0){} but if I change it to if (strcmp ($res, "VERIFIED") == -1){} It works fine.
UPDATE
I have change my code to
$mode = $paymod_data['PAYMENT_MODE'];
$paypal_url = ($mode == 'test') ? 'www.sandbox.paypal.com' : 'www.paypal.com';
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$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';
$req .= payment_safe_check ($_REQUEST);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://'.$paypal_url.'/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Host: '.$paypal_url));
$res = curl_exec($ch);
curl_close($ch);
if (strcmp ($res, "VERIFIED") == 0) {
processing ( $_POST );
}
else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
Can someone please confirm if they is any problem with this code.
VERIFIED and INVALID are the two responses that you need to validate once you get the response back from PAYPAL. You can refer the code samples at https://www.x.com/developers/PayPal/documentation-tools/code-sample/216623
strcmp check to see if one string is "larger" than the other. Is is case sensitive and can be thrown off by spaces and line feeds.
I would recommend you instead use
if ( stristr($res,"VERIFIED") === 0 ) {}
else if ( stristr($res,"INVALID") === 0 ) {}
the problem in the url could not be found ...
you have to change the url to be like the following ...
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr'); // (for Sandbox IPNs)
$ch = curl_init(' https://www.paypal.com/cgi-bin/webscr'); // (for live IPNs)