Related
My PayPal IPN code receives the post data sent back from PayPal upon purchase completion but doesn't insert the data into the database. I had the code send me an email if it fails to insert the data into the database. If I take that post data and paste it into the address bar after ipn.php?, it works perfectly. What am I doing wrong? Do I have to post the data back to PayPal?
My IPN Listener
<?php
include 'includes/class.user.php';
$user = new USER();
$emailtext = "";
$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]);
}
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
$value = urlencode($value);
$req .= "&$key=$value";
}
$paypalURL = "https://ipnpb.paypal.com/cgi-bin/webscr";
$ch = curl_init($paypalURL);
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_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: Company name removed for security, LLC.'));
$res = curl_exec($ch);
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
//Payment data
$txn_id = $_GET['txn_id'];
$payment_gross = $_GET['mc_gross'];
$currency_code = $_GET['mc_currency'];
$pay_status = $_GET['payment_status'];
$payer_email = $_GET['payer_email'];
$date = date("m-d-y");
$invNum = md5($date);
$orderNum = md5($txn_id);
$fullname = $_GET['address_name'];
$address = $_GET['address_street'];
$city = $_GET['address_city'];
$state = $_GET['address_state'];
$zip = $_GET['address_zip'];
if($user->verify_txnid($txn_id)){
exit();
}else{
$insertPayment = $user->insert_purchase($fullname,$address,$city,$state,$zip,"0000000000",$payer_email,$orderNum,$date,$txn_id,$invNum,$pay_status);
if($insertPayment === TRUE){
$num_cart_items = $_GET['num_cart_items'];
for ($i = 0; $i <= $num_cart_items; $i++) {
$order_item_name = $_GET['item_name' . $i];
$order_item_quantity = $_GET['quantity' . $i];
$order_item_gross_amount = $_GET['mc_gross_' . $i];
$order_item_custom = $_GET['option_selection1_' . $i];
$user->insert_order($txn_id, $order_item_name, $order_item_quantity, $order_item_gross_amount, $order_item_custom);
}
}else{
foreach ($_GET as $key => $value)
{
$emailtext .= $key . " = " .$value ."\n\n";
}
mail('myemail#gmail.com', 'Insert Payment FAILED', $emailtext."\r\n".$req."\r\n".$res);
}
}
header("HTTP/1.1 200 OK");
I have confirmed that the post from PayPal matches the post back to PayPal but I still get an INVALID response and none of the data gets inserted into the database. If I take the raw post data string from PayPal's initial POST and paste it into the address bar, it inserts into the database just fine. I can't figure out for the life of me why it's not inserting the data into the database on its own. My IPN history on PayPal show an html response of 500 and that its retrying.
If IPN message you POST back does not exactly match the one PayPal sent to you, INVALID status will be occurred. You can double check if there have mess code in the IPN message you POST back, such as non-English characters. But if you still cannot find out the root cause, you have to contact PayPal technical support via https://www.paypal-techsupport.com/app/ask/ and provide PayPal transaction ID for the further checking.
Had the exactly same problem on sandbox.
Got it working by switching off "Negative Testing" on the account profile settings and removing the date from the ipn simulator.
That simple code made it work
$ipn_post_data = $_POST;
// Choose url
if (array_key_exists('test_ipn', $ipn_post_data) && 1 === (int) $ipn_post_data['test_ipn']){
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}else{
$url = 'https://www.paypal.com/cgi-bin/webscr';
}
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if ($status == 200 && $response == 'VERIFIED') {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, "Verified\r\n");
} else {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, $response."\r\n");
fwrite($file, $status."\r\n");
}
I would never use that code in production for many reasons.
Hope this can make things easy for u.
For some reason im having more trouble with this then I should... I have a IPN listner for Paypal and the IPN Simulator says successful each time and with different methods, but I can not get it to then manipulate the database based on a successful response.
Any ideas anyone?
<?php
//INCLUDE CONNECTION STRING
include('connect.php');
// STEP 1: read POST data
// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
// Instead, read raw POST data from the input stream.
$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 IPN message sent from PayPal and prepend 'cmd=_notify-validate'
$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');
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
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'];
if($payment_status=="Completed"){
$selectuser = mssql_query("select statsmemberid from statsmembers where email='$payer_email'");
if(mssql_num_rows($selectuser) != 0){
$row = mssql_fetch_row($selectuser);
$statsmemberid = $row[0];
$getCredits = mssql_query("select creditsbought from statsmuplayers where statsmemberid='$statsmemberid'");
$row = mssql_fetch_row($getCredits);
$totalCredits = $row[0]+11;
$updatemu = mssql_query("update statsmuplayers set creditsbought='$totalCredits' where statsmemberid='$statsmemberid'");
echo "Credits Applyed";
}else{
echo "Invalid Email";
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
?>
I'd suggest you encapsulate all of this code into a few objects. That will really help you figure out where things are going wrong.
You want an object that handles the IPN communication with paypal (you don't need to write that from scratch here's the first PHP implementation I found in a google search https://github.com/dodev34/paypal-ipn-response-client)
Then you want a base object that handles your database connections. And finally you want a statsmembers object that extends your database connection object and enforces your business logic. You might just lift some PHP ORM code like what you see here http://www.phpactiverecord.org/projects/main/wiki/Quick_Start
That would allow you to test the update functionality separately from the actual paypal IPN communication. The good news is at that point you don't have to rely on writing to system files for debugging like someone suggested in a comment above.
I don't see the specific error in your code just from a glance sorry, are you sure your passing the payer_email through paypal correctly?
I am using the PayPal IPN feature but I keep getting _POST[payment_status] = Pending when I make a test using the Sandbox. Because of that, I can't test the full purchase to make sure the whole process works properly.
Here is my code:
// Set the request paramaeter
$req = 'cmd=_notify-validate';
// Run through the posted array
foreach ($_POST as $key => $value)
{
// If magic quotes is enabled strip slashes
if (get_magic_quotes_gpc())
{
$_POST[$key] = stripslashes($value);
$value = stripslashes($value);
}
$value = urlencode($value);
// Add the value to the request parameter
$req .= "&$key=$value";
}
$ch = curl_init(); // Starts the curl handler
curl_setopt($ch, CURLOPT_URL,$paypal[$testing]); // Sets the paypal address for curl
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // Returns result to a variable instead of echoing
curl_setopt($ch, CURLOPT_TIMEOUT, 3); // Sets a time limit for curl in seconds (do not set too low)
curl_setopt($ch, CURLOPT_POST, 1); // Set curl to send data using post
curl_setopt($ch, CURLOPT_POSTFIELDS, $req); // Add the request parameters to the post
$result = curl_exec($ch); // run the curl process (and return the result to $result
curl_close($ch);
if (strcmp ($result, "VERIFIED") == 0) // It may seem strange but this function returns 0 if the result matches the string So you MUST check it is 0 and not just do strcmp ($result, "VERIFIED") (the if will fail as it will equate the result as false)
{
// It worked
// do my stuff
}else{
foreach($_POST as $key=>$value){
$post_str .="_POST[$key] = $value<br>";
}
$sub = "Paypal error";
$mes = "$post_str";
mailer($sub, $mes, $from, $to);
}
This is the error message I get in my email:
_POST[mc_gross] = 120.00
_POST[protection_eligibility] = Ineligible
_POST[address_status] = confirmed
_POST[payer_id] = QDZDYY4JWGFVG
_POST[tax] = 0.00
_POST[address_street] = 1 Maire-Victorin
_POST[payment_date] = 12:25:03 Apr 11, 2013 PDT
_POST[payment_status] = Pending
_POST[charset] = windows-1252
_POST[address_zip] = M5A 1E1
_POST[first_name] = gppl
_POST[address_country_code] = CA
_POST[address_name] = gppl Simard's Test Store
_POST[notify_version] = 3.7
_POST[custom] =
_POST[payer_status] = verified
_POST[address_country] = Canada
_POST[address_city] = Toronto
_POST[quantity] = 1
_POST[verify_sign] = An5ns1Kso7MWUdW4ErQKJJJ4qi4-AjAXF3KMNcb6G83Jslw18dw.Ti32
_POST[payer_email] = 78h23d8712h3d#x.com
_POST[txn_id] = 2KU491796R824634N
_POST[payment_type] = instant
_POST[payer_business_name] = gppl x's Test Store
_POST[last_name] = Simard
_POST[address_state] = Ontario
_POST[receiver_email] = x#x.x
_POST[pending_reason] = unilateral
_POST[txn_type] = web_accept
_POST[item_name] = 926537
_POST[mc_currency] = CAD
_POST[item_number] =
_POST[residence_country] = CA
_POST[test_ipn] = 1
_POST[handling_amount] = 0.00
_POST[transaction_subject] = 926537
_POST[payment_gross] =
_POST[shipping] = 0.00
_POST[ipn_track_id] = c7dcef0b1c333
This error is really driving me nuts.
Just so you guys know, I already have the Payment review set to off.
If you are useing sandbox, open developer.paypal.com->Dashboard->Accounts->facilitator#yourhost.com->Profile->Settings. Set 'Payment review' to 'Off'
Make sure your accounts have a confirmed email address. This typically happens when you send a payment to an account that doesn't have a confirmed email address. So check the seller account, and make sure the email address is confirmed that you sent the payment to.
I had this problem - I was sending PayPal my "live" payer_email, when I changed it to the business email in the sandbox it worked. same as my live email with ****-facilitator#yoursite.com
Login to your Sandbox account with the business, and see if you have any 'Unclaimed' payments. This could be because they are in a different currency than the one of your main account.
As mentioned in one of the comments in an answer below, I tried following this tutorial. So now I have the following:
The ipn.php file:
<?php
$ipn_post_data = $_POST;
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_CAINFO => 'cacert.pem',
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if($status == 200 && $response == 'VERIFIED')
{
$subject = "valid";
$message = "good";
}
else
{
$subject = "invalid";
$message = "bad";
}
$to = "oshirowanen#mail.com";
$from = "me#desktop.com";
$header = 'MIME-Version: 1.0' . "\r\n";
$header .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
$header .= 'To: Oshirowanen <oshirowanen#mail.com>' . "\r\n";
$header .= 'From: Me <me#desktop.com>' . "\r\n";
mail($to,$subject,$message,$header);
?>
The received email:
Subject "invalid"
Message "bad"
Edit:
Now that I can see the array you've outputted, try replacing this to get rid of the PHP array error:
foreach ($_POST as $key => $value) {
if (!is_array($value)) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
else if (is_array($value)) {
$paymentArray = explode(' ', $value[0]);
$paymentCurrency = urlencode(stripslashes($paymentArray[0]));
$paymentGross = urlencode(stripslashes($paymentArray[1]));
$req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
}
}
Here is the edited code in full:
// read the post from PayPal system and add 'cmd'
$req = 'cmd=' . urlencode('_notify-validate');
foreach ($_POST as $key => $value) {
if (!is_array($value)) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
else if (is_array($value)) {
$paymentArray = explode(' ', $value[0]);
$paymentCurrency = urlencode(stripslashes($paymentArray[0]);
$paymentGross = urlencode(stripslashes($paymentArray[1]);
$req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
}
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_HEADER, 0);
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: www.paypal.com'));
$res = curl_exec($ch);
curl_close($ch);
// 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 (strcmp ($res, "VERIFIED") == 0) {
// check 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 ($res, "INVALID") == 0) {
// log for manual investigation
}
Check this out!
Edit: Check out the PayPal troubleshooting tips:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNTesting
The problem is that you don't check the HTTP response code, so you are intepreting the "Invalid Host header" as the PayPal response, whilst it's the web server response (for the status code 400).
If you look at the PayPal documentation, there is a PHP example which is very similar to your code, since it uses the "fsockopen", "fputs" and "fgets" functions to communicate with the PayPal server.
But if you look carefully at the remark after the "fsockopen" call, you can read:
// Process validation from PayPal
// TODO: This sample does not test the HTTP response code. All
// HTTP response codes must be handled or you should use an HTTP
// library, such as cUrl
And this is exacty your problem: you don't check that the HTTP response code is 200 (OK), before parsing the response body.
Also, using the "strtolower" function is not correct, since the real response from the PayPal server is always uppercase, as shown in the above cited example.
Even if the PayPal example uses the "fsockopen" approach, I think it should be much better to use the PHP cURL library to implement your IPN listener.
Have also a look at the following answers:
PHP cURL PayPal Sandbox
cURL or fsockopen for paypal ipn
However, if you really want to use the "fsockopen" function, you should always specify the "Host" header field in the POST request, as shown in the following snippet of code (taken from the PHP manual):
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
UPDATE
Here is a simple function for recursive stripslashes/urlencoding:
<html>
<body>
<pre>
<?
$post = Array (
"transaction" => Array("USD 20.00"),
"payment_request_date" => "Sun Aug '05 08:49:20 PDT 2012",
"return_url" => "http://000.000.000.000/success.php"
);
echo "before myUrlencode...\n";
print_r($post);
function myUrlencode($post) {
foreach ($post as $key => $val) {
if (is_array($val)) {
$post[$key] = myUrlencode($val);
} else {
$post[$key] = urlencode(stripslashes($val));
}
}
return($post);
}
echo "\nafter myUrlencode...\n";
print_r(myUrlencode($post));
?>
</pre>
</body>
</html>
Got it working using the basic sample code 4b,
Cleared $ipnNotificationUrl = ""; from the basic sample code as I had a value in there which I added myself,
Created a seller account instead of a business pro account in sandbox,
Set the seller account to enable the ipn url,
Used the following PHP 5.2 sample code for the ipn listener
Added the 2 lines into the listener, as described here, the 2 lines can be seen below:
Downloaded the cacert.pem certificate to my server from here and put it in the same directory as the ipn listener:
The 2 lines mentioned in point 6:
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_CAINFO => 'cacert.pem',
I have no idea why the sandbox business pro account does not let me set an ipn url, but the seller account does.
These links may resolve your problem,
Paypal: Invalid IPN problem
http://www.webmasterworld.com/ecommerce/4292847.htm
Paypal sandbox IPN return INVALID
I am not sure what is exactly wrong right now with your code, but I was strugling wuth the same while ago and my fixes was to add HOST in the header and host have to be www.paypal.com. I used fsockopen method and work fine now.
In Curl I had a problem before with ssl. And solution was to put those lines:
curl_setopt($curl, CURLOPT_COOKIEJAR, dirname(__FILE__) . "/cookies.txt");
curl_setopt($curl, CURLOPT_COOKIEFILE, dirname(__FILE__) . "/cookies.txt");
where of course file cookies.txt have to exists.
and more over I had to run one connection to page to get session data and later send post data.
Below is a header what is working fine for me with fsockopen method
$header = "POST /cgi-bin/webscr HTTP/1.0\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\r\n";
It's a problem with the + character, it often get wrongly fetched so I made that workaround, and it worked for me.
payment_data = Sat Jun 04 2016 15:11:16 GMT+0200 (CEST)
foreach ($_POST as $key => $value) {
if($key !== "payment_date"){
$req .= '&' . $key . '=' . rawurlencode(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
}else{
$req .= '&' . $key . '=' . rawurlencode(str_replace(array('GMT '),array('GMT+'),$value));
}}
Here's how to avoid these errors...
foreach ($_POST as $key => $value) {
if ($key=='transaction')
foreach ($value as $key2=>$value2) {
$value['transaction'][$key2] = urlencode(stripslashes($value2));
}
else {
$value = urlencode(stripslashes($value));
}
$req .= "&$key=$value";
}
Hours of hair pulling until I saw Izudin's answer. He's right..The + in the date wasn't being transferred. Just to test, I removed it from the pre-populated field in the simulator and got a Verified at last.
I finally found an updated (August 5, 2016) working answer to this query. You can use this code as your final IPN for Sandbox or Live. With the following consideration:
Be sure to place your IPN listener to ->My selling tools -> instant payment notifications Section.
Do not use IPN Simulator in sandbox, it will always return INVALID.
Create and Use an actual Sandbox Button, but DO NOT put your IPN listener to RETURN PAGE that says "Take customers to this URL when they finish checkout".
That's all of it. I hope this will help.
And here is the working code:
<?php
$post_data = file_get_contents('php://input');
$post_array = explode('&', $post_data);
$dataFromPayPal = array();
foreach ($post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$dataFromPayPal[$keyval[0]] = urldecode($keyval[1]);
}
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($dataFromPayPal 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";
}
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
//use https://www.sandbox.paypal.com/cgi-bin/webscr in case you are testing this on a PayPal Sanbox environment
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_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
curl_close($ch);
exit;
}
curl_close($ch);
if (strcmp ($res, "INVALID") == 0) {
echo "INVALID";
}
else if (strcmp ($res, "VERIFIED") == 0) {
echo "VALID";
}
?>
I have implemented a function that runs on each page that I want to restrict from non-logged in users. The function automatically redirects the visitor to the login page in the case of he or she is not logged in.
I would like to make a PHP function that is run from a exernal server and iterates through a number of set URLs (array with URLs that is for each protected site) to see if they are redirected or not. Thereby I could easily make sure if protection is up and running on every page.
How could this be done?
Thanks.
$urls = array(
'http://www.apple.com/imac',
'http://www.google.com/'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
foreach($urls as $url) {
curl_setopt($ch, CURLOPT_URL, $url);
$out = curl_exec($ch);
// line endings is the wonkiest piece of this whole thing
$out = str_replace("\r", "", $out);
// only look at the headers
$headers_end = strpos($out, "\n\n");
if( $headers_end !== false ) {
$out = substr($out, 0, $headers_end);
}
$headers = explode("\n", $out);
foreach($headers as $header) {
if( substr($header, 0, 10) == "Location: " ) {
$target = substr($header, 10);
echo "[$url] redirects to [$target]<br>";
continue 2;
}
}
echo "[$url] does not redirect<br>";
}
I use curl and only take headers, after I compare my url and url from header curl:
$url="http://google.com";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, '60'); // in seconds
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($ch);
if(curl_getinfo($ch)['url'] == $url){
echo "not redirect";
}else {
echo "redirect";
}
You could always try adding:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
since 302 means it moved, allow the curl call to follow it and return whatever the moved url returns.
Getting the headers with get_headers() and checking if Location is set is much simpler.
$urls = [
"https://example-1.com",
"https://example-2.com"
];
foreach ($urls as $key => $url) {
$is_redirect = does_url_redirect($url) ? 'yes' : 'no';
echo $url . ' is redirected: ' . $is_redirect . PHP_EOL;
}
function does_url_redirect($url){
$headers = get_headers($url, 1);
if (!empty($headers['Location'])) {
return true;
} else {
return false;
}
}
I'm not sure whether this really makes sense as a security check.
If you are worried about files getting called directly without your "is the user logged in?" checks being run, you could do what many big PHP projects do: In the central include file (where the security check is being done) define a constant BOOTSTRAP_LOADED or whatever, and in every file, check for whether that constant is set.
Testing is great and security testing is even better, but I'm not sure what kind of flaw you are looking to uncover with this? To me, this idea feels like a waste of time that will not bring any real additional security.
Just make sure your script die() s after the header("Location:...") redirect. That is essential to stop additional content from being displayed after the header command (a missing die() wouldn't be caught by your idea by the way, as the redirect header would still be issued...)
If you really want to do this, you could also use a tool like wget and feed it a list of URLs. Have it fetch the results into a directory, and check (e.g. by looking at the file sizes that should be identical) whether every page contains the login dialog. Just to add another option...
Do you want to check the HTTP code to see if it's a redirect?
$params = array('http' => array(
'method' => 'HEAD',
'ignore_errors' => true
));
$context = stream_context_create($params);
foreach(array('http://google.com', 'http://stackoverflow.com') as $url) {
$fp = fopen($url, 'rb', false, $context);
$result = stream_get_contents($fp);
if ($result === false) {
throw new Exception("Could not read data from {$url}");
} else if (! strstr($http_response_header[0], '301')) {
// Do something here
}
}
I hope it will help you:
function checkRedirect($url)
{
$headers = get_headers($url);
if ($headers) {
if (isset($headers[0])) {
if ($headers[0] == 'HTTP/1.1 302 Found') {
//this is the URL where it's redirecting
return str_replace("Location: ", "", $headers[9]);
}
}
}
return false;
}
$isRedirect = checkRedirect($url);
if(!$isRedirect )
{
echo "URL Not Redirected";
}else{
echo "URL Redirected to: ".$isRedirect;
}
You can use session,if the session array is not set ,the url redirected to a login page.
.
I modified Adam Backstrom answer and implemented chiborg suggestion. (Download only HEAD). It have one thing more: It will check if redirection is in a page of the same server or is out. Example: terra.com.br redirects to terra.com.br/portal. PHP will considerate it like redirect, and it is correct. But i only wanted to list that url that redirect to another URL. My English is not good, so, if someone found something really difficult to understand and can edit this, you're welcome.
function RedirectURL() {
$urls = array('http://www.terra.com.br/','http://www.areiaebrita.com.br/');
foreach ($urls as $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// chiborg suggestion
curl_setopt($ch, CURLOPT_NOBODY, true);
// ================================
// READ URL
// ================================
curl_setopt($ch, CURLOPT_URL, $url);
$out = curl_exec($ch);
// line endings is the wonkiest piece of this whole thing
$out = str_replace("\r", "", $out);
echo $out;
$headers = explode("\n", $out);
foreach($headers as $header) {
if(substr(strtolower($header), 0, 9) == "location:") {
// read URL to check if redirect to somepage on the server or another one.
// terra.com.br redirect to terra.com.br/portal. it is valid.
// but areiaebrita.com.br redirect to bwnet.com.br, and this is invalid.
// what we want is to check if the address continues being terra.com.br or changes. if changes, prints on page.
// if contains http, we will check if changes url or not.
// some servers, to redirect to a folder available on it, redirect only citting the folder. Example: net11.com.br redirect only to /heiden
// only execute if have http on location
if ( strpos(strtolower($header), "http") !== false) {
$address = explode("/", $header);
print_r($address);
// $address['0'] = http
// $address['1'] =
// $address['2'] = www.terra.com.br
// $address['3'] = portal
echo "url (address from array) = " . $url . "<br>";
echo "address[2] = " . $address['2'] . "<br><br>";
// url: terra.com.br
// address['2'] = www.terra.com.br
// check if string terra.com.br is still available in www.terra.com.br. It indicates that server did not redirect to some page away from here.
if(strpos(strtolower($address['2']), strtolower($url)) !== false) {
echo "URL NOT REDIRECT";
} else {
// not the same. (areiaebrita)
echo "SORRY, URL REDIRECT WAS FOUND: " . $url;
}
}
}
}
}
}
function unshorten_url($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
$out = curl_exec($ch);
$real_url = $url;//default.. (if no redirect)
if (preg_match("/location: (.*)/i", $out, $redirect))
$real_url = $redirect[1];
if (strstr($real_url, "bit.ly"))//the redirect is another shortened url
$real_url = unshorten_url($real_url);
return $real_url;
}
I have just made a function that checks if a URL exists or not
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
function url_exists($url, $ch) {
curl_setopt($ch, CURLOPT_URL, $url);
$out = curl_exec($ch);
// line endings is the wonkiest piece of this whole thing
$out = str_replace("\r", "", $out);
// only look at the headers
$headers_end = strpos($out, "\n\n");
if( $headers_end !== false ) {
$out = substr($out, 0, $headers_end);
}
//echo $out."====<br>";
$headers = explode("\n", $out);
//echo "<pre>";
//print_r($headers);
foreach($headers as $header) {
//echo $header."---<br>";
if( strpos($header, 'HTTP/1.1 200 OK') !== false ) {
return true;
break;
}
}
}
Now I have used an array of URLs to check if a URL exists as following:
$my_url_array = array('http://howtocode.pk/result', 'http://google.com/jobssss', 'https://howtocode.pk/javascript-tutorial/', 'https://www.google.com/');
for($j = 0; $j < count($my_url_array); $j++){
if(url_exists($my_url_array[$j], $ch)){
echo 'This URL "'.$my_url_array[$j].'" exists. <br>';
}
}
I can't understand your question.
You have an array with URLs and you want to know if user is from one of the listed URLs?
If I'm right in understanding your quest:
$urls = array('http://url1.com','http://url2.ru','http://url3.org');
if(in_array($_SERVER['HTTP_REFERER'],$urls))
{
echo 'FROM ARRAY';
} else {
echo 'NOT FROM ARR';
}