SagePay Notification Blank - php

I have a problem that I'm hoping someone out there is going to be able to help me with, as SagePay support has been as helpful as a chocolate teapot. I have a bespoke e-commerce solution with SagePay Server Integration, some running v2.23 others v3.00. We have had a couple of websites suddenly stop working.
The Notification from SagePay is blank, completely and utterly empty. There is no post data available on either of the two websites. However we have two other websites where nothing else is wrong, everything is working fine.
The only difference between the 4 sites is the version, which necessitated some minor changes in what is sent to SagePay, and where they are hosted. The two working sites are hosted with HeartInternet, whereas the two that don't work are hosted with different hosts, one with 123reg and one with an unknown American host.
SagePay have sent me their logs for a couple of transactions, both show the Notification data present, so as far as they are concerned they are sending it to me fine and it's a server issue. I'm not even sure where to begin debuging this.
Not sure if it's relevant but I've added in the start of our Notification method:
public function Notify() {
$this->Load();
define('LOG_FILE', ROOT_LIB . 'sagepaylog-v2.23.txt');
error_log(date('c') . " NEW NOTIFICATION" . PHP_EOL, 3, LOG_FILE);
$input = file_get_contents('php://input');
$query = $response = array();
$exp = explode('&', $input);
foreach($exp as $keyVal) {
$e = explode('=', $keyVal, 2);
$response[$e[0]] = urldecode($e[1]);
}
/* Comment out the above code and uncomment below when testing using POSTMAN.
foreach($_POST as $key => $val) {
$response[$key] = $val;
}*/
error_log("Notification Post Data" . PHP_EOL . "-------------------------" . PHP_EOL, 3, LOG_FILE);
foreach($response as $key => $val) {
error_log("$key: $val" . PHP_EOL, 3, LOG_FILE);
}
//We record the transaction during the initial registration. We need to load the details of that transaction, and of the order for the specified order ID, and make sure they match the details sent to us by SagePay.
$valid = FALSE;
$orders = $this->glob->order->LoadOrderHistory("WHERE ordId = " . (int)$response['VendorTxCode'], TRUE);
if(count($orders)) {
$order = $orders[$response['VendorTxCode']];
} else {
$order = new Order($this->glob);
}
$transaction = new Transaction($this->glob, $order->txnId);

Turns out this was caused by an htaccess redirecting non-www to www, and the notification URL didn't include a www. This was fixed by checking the URL and adding a www in front if there wasn't already one.

Related

PHP - URL gets malformed during redirect

So, I have an image link that has this href:
http://www.app.com/link?target=www.target.com&param1=abc&param2=xyz
This is processed like so (I use laravel):
function out (Request $request) {
$url = $request->target;
$qs = $request->except('target');
if ( !empty($qs) ) {
$url .= strpos($url, '?') !== false ? '&' : '?';
$url .= http_build_query($qs);
}
return redirect($url);
}
Most of the time, this works. However, lately, we've been experiencing an issue where param1 and param2 are attached to the URL in a seemingly infinite loop causing us to hit a 414 Request URI too long Error.
The problem is that it happens so randomly that I really don't know where to check because I added a checker before the return statement.
if ( substr_count($url, 'param1') > 1 ) {
$file = storage_path() . '/logs/logger.log';
$log = "[ " . date("d-m-Y H:i:sa") . " ] [ {$request->ip()} ] - {$url} \n";
file_put_contents($file, $log, FILE_APPEND);
}
And it hasn't logged a single hit. Even after our testers experienced the bug.
Is it possible that the receiving application is breaking the URL somehow?
What information should I be looking out for? Have you seen an issue like this before?
Is it the http_build_query that could be causing this and that my checker just doesn't work as expected (though, I did test it and it logged my test URL).
Any help on the matter would be great.
Assuming and issue with http_build_query:
Well, one attempt you may try is to rewrite the code without $request->except and http_build_query.
If you don't have any special reason to use http_build_query i would suggest to use $request->input.
Example with $request->input:
function out (Request $request) {
$url = $request->target;
$param1 = $request->input('param1', '');
$param2 = $request->input('param2', '');
if (!empty($param1) || !empty($param2)) {
$url .= '?';
}
if (!empty($param1) && !empty($param2)) {
$url .= 'param1=' . $param1 . '&param2=' . $param2;
} else {
$url .= !empty($param1) 'param1=' . $param1 : '';
$url .= !empty($param2) 'param2=' . $param2 : '';
}
return redirect($url);
}
The solution is a little bit more verbose but with that, you should be sure 100% that is not the code to generate the redundancy.
Absurd, remote possibility:
The second thing I would try is to check you log system. For instance if you are running under apache you should have a file called access.log under /var/log/apache2/ (or under /var/log/nginx/ with nginx).
In there you should have the history of all your http requests.
Maybe there is a chance that some of the wired requests with multiple params are from a strange IP address.
If this is the case, it means that some company is monitoring and testing the website (potentially with the strange parameters) for security reasons.
If this is the case, I guess you are under http and you should switch to https.
Anyway, with the new code, you should be sure about the code and be able to investigate any other part of the system.

Laravel sessions keep regenerating after upload to live server

I have recently deployed my new website to a shared hosting server.
I had to change some paths in order for it to work but in the end I got it all working, except for sessions.
The website behaves as if sessions are completely disabled. Upon closer inspection, using the "file" driver for sessions it creates many sessions in the storage/sessions folder.
I changed to the "database" driver and there was similar behaviour, many new rows/sessions being created after every page operation.
Does anyone have any idea what could be causing this?
Here's an example of one function that sets session variables:
public function addItem($itemId) {
if (Session::get('cart.' . $itemId) !== null) {
$quantity = Session::get('cart.' . $itemId) + Input::get('quantity');
} else {
$quantity = Input::get('quantity');
}
if (!Input::get('quantity'))
$quantity = Session::get('cart.' . $itemId) + 1;
Session::set('cart.' . $itemId, $quantity);
$plural = (Input::get('quantity') > 1 ? Lang::get('messages.units') : Lang::get('messages.unit'));
$msg = Input::get('quantity') . ' ' . $plural . ' '.Lang::get('messages.addedtocart').'!';
if (Request::ajax()) {
return Response::json(['quantity' => $quantity, 'message' => $msg]);
} else {
return Redirect::back()->with('message', $msg);
}
}
I have found out what it is.
It appears that for some reason my ISP is stopping my sessions from working for whatever reason.
I switched to a VPN and all of a sudden all sessions work again. I noticed that other web apps like Google and SO were also behaving strangely.
Has anyone else encountered this before?

echo variable inside DEFINE in php?

I'm trying to figure out how to echo a variable inside a DEFINE function in my php page.
I get the variable like so:
$arr=file("myFile.txt");
foreach($arr as $str){
list($token)=explode("|",$str);
}
and then I echo it like so on the same page:
echo $token;
up to this point everything works fine.
but I need to echo the $token inside a DEFINE on the same page like so:
DEFINE("AUTH_TOKEN", "'".$token."'");
I don't get any error at all but this doesn't work.
however, if i use:
DEFINE("AUTH_TOKEN", 'dghsa7dasdbhas8hdasdasod9a999');
it works just fine. the dghsa7dasdbhas8hdasdasod9a999 is the value of $token stored in the database.
could someone please let me know if I'm missing something or doing anything wrong?
Thanks in advance
EDIT:
This is the entire code I am using:
// eBay site to use - 0 = United States
DEFINE("SITEID", 0);
// production vs. sandbox flag - true=production
DEFINE("FLAG_PRODUCTION", false);
// eBay Trading API version to use
DEFINE("API_COMPATIBILITY_LEVEL", 779);
/* Set the Dev, App and Cert IDs
Create these on developer.ebay.com
check if need to use production or sandbox keys */
$arr = file("myFile.txt");
foreach ($arr as $str) {
list($token) = explode("|", $str);
}
if (FLAG_PRODUCTION) {
// PRODUCTION
// Set the production URL for Trading API
DEFINE("API_URL", 'https://api.ebay.com/ws/api.dll');
// Set the auth token for the user profile used
DEFINE("AUTH_TOKEN", 'YOUR_PRODUCTION_TOKEN');
} else {
$arr = file("myFile.txt");
foreach ($arr as $str) {
list($token) = explode("|", $str);
}
echo $token;
// SANDBOX
// Set the sandbox URL for Trading API calls
DEFINE("API_URL", 'https://api.sandbox.ebay.com/ws/api.dll');
// Set production credentials (from developer.ebay.com)
// Set the auth token for the user profile used
DEFINE("AUTH_TOKEN", "'" . $token . "'");
}
the database is in a .txt file called myFile.txt and it looks like this:
111111111111111|222222222222222222|3333333333333333333|4444444444444444444444
You can try this:
DEFINE("AUTH_TOKEN", $token);
This code has a number of issues such as the repetition of $arr=file("myFile.txt"), however the crux of the question.
Move AUTH_TOKEN out of the if/else block and define it just after the foreach($arr as $str) loop (before the if/else)
This way you will be defining the AUTH_TOKEN once only and this will be far easier to debug.
$arr=file("myFile.txt");
foreach($arr as $str) {
list($devName,$appName,$certName,$token)=explode("|",$str);
}
DEFINE('AUTH_TOKEN', $token);

Yahoo Placefinder Geocoding randomly stopped working... Are they not supporting it now?

I have a web app that uses Yahoo Geocoding Placefinder API and it was working perfectly until earlier today. It just randomly quit working. Are they not supporting it anymore? I have looked all over the net and I can't find anything about them dropping support, but my code no longer works. Here's my code...
function geocode_yahoo($address,$city,$state,$country) {
$address = array($address, $city, $state, $country);
$address = array_filter($address);
$address = urlencode(implode(', ', $address));
$appid = 'CYxSRa64';
$url = 'http://where.yahooapis.com/geocode?location='.$address.'&flags=J&appid='.$appid;
$data = file_get_contents($url);
if ($data != '') {
$data = json_decode($data);
if ($data && $data->ResultSet && $data->ResultSet->Error == '0' && $data->ResultSet->Found) {
return (object) array('lat'=>$data->ResultSet->Results[0]->latitude, 'lng'=>$data->ResultSet->Results[0]->longitude);
}
}
return false;
}
Nothing wrong with your code, the service has simply been stopped.
Note that http://where.yahooapis.com is now returning a 404 page.
The service was set to retire at the end of November 2012 but was left running in order to provide time for developers to migrate to the replacement Yahoo BOSS services: http://developer.yahoo.com/boss/geo/
There is nothing here that states that the service will be stopped: https://developer.yahoo.com/boss/geo/docs/free_YQL.html#table_pf
In fact, there is a link on the bottom of this page: https://developer.yahoo.com/boss/geo/ to the free YQL version.

PHP 5.3 getallheaders

I'm developing a web application for which it is required to capture custom header data sent by clients. In my localhost PHP 5.4 was installed and I'm using getallheaders().
But in my hosting which has PHP 5.3 installed. I can't get the header. I already tried other ways such:
foreach ($_SERVER as $name => $value)
{
if (substr($name, 0, 5) == 'HTTP_')
{
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
$headers[$name] = $value;
} else if ($name == "CONTENT_TYPE") {
$headers["Content-Type"] = $value;
} else if ($name == "CONTENT_LENGTH") {
$headers["Content-Length"] = $value;
}
}
and
apache_request_headers(), apache_response_headers()
Is there any other way? My hosting is using PHP 5.3 (FastCGI).
UPDATE
Well, i got where the problem came from. so first(for proofing the concept), i switch the PHP to run as Apache module, and yes as expected i able to use getallheaders().
after that i tried other rest client/debuger to send custom header(now as FastCGI), and my code is able capture the header(inside $_SERVER). So the question is, why the first REST client able to send header only if the server run as Apache Module.
I'm afraid if one of my user got the same problem while the other is fine.

Categories