We already talked about currencies in Magento showing how to add the currency selector to the Onepage Checkout and how to convert from one currency to another.
In this tutorial, I will show you how you can easily set the store currency based on the geographic location of your visitors if you have the php-geoip extension installed.
Installing php-geoip
If you have full control over your server running Magento, then this task is really easy. Log in with a sudo account or root and just use your distro’s package manager to install php-geoip. On Ubuntu/Debian, all you need to do is:
sudo apt-get install php5-geoip
For other distros, you may need to google around a little. php-geoip is a PECL package so you may have some luck finding the right download there.
Now, the package comes with an old GeoIP.dat file so we really should update it to the latest available data file. There is a great little blog post about this so I’m going to just present the essentials here.
You can get the data file on the Maxmind site. You can choose the free “Lite” version or pay for the full, more accurate version. Either way, download the binary version of the .dat file, copy it to your server or from your server, directly type:
wget -q http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
Once downloaded, unpack and move the dat file to: /usr/share/GeoIP/. Make sure it’s called GeoIP.dat. You should also make a backup of the original file just in case.
Using php-geoip
The php.net manual for GeoIP tells us that we have the following functions at our disposal:
geoip_continent_code_by_name— Get the two letter continent codegeoip_country_code_by_name— Get the two letter country codegeoip_country_code3_by_name— Get the three letter country codegeoip_country_name_by_name— Get the full country namegeoip_database_info— Get GeoIP Database informationgeoip_db_avail— Determine if GeoIP Database is availablegeoip_db_filename— Returns the filename of the corresponding GeoIP Databasegeoip_db_get_all_info— Returns detailed information about all GeoIP database typesgeoip_id_by_name— Get the Internet connection speedgeoip_isp_by_name— Get the Internet Service Provider (ISP) namegeoip_org_by_name— Get the organization namegeoip_record_by_name— Returns the detailed City information found in the GeoIP Databasegeoip_region_by_name— Get the country code and regiongeoip_region_name_by_code— Returns the region name for some country and region code combogeoip_time_zone_by_country_and_region— Returns the time zone for some country and region code combo
We won’t need most of these functions but it’s good to know what we can do with this module. We will really only use: geoip_country_code_by_name and geoip_continent_code_by_name.
Putting it together in Magento
Note that the code shown here is from Magento 1.4.1.0 – the latest Magento release at the time of this writing. This code and principle will however work for previous Magento versions too.
Assuming we are using the default Magento currency selector, first we need to work out how the currency selector sets the default currency when visitors land on the site.
Looking into: /app/design/frontend/[package]/[theme]/template/directory/currency.phtml we notice the call to: $this->getCurrentCurrencyCode(). We check the Block class for this template in /app/code/core/Mage/Directory/Block/Currency.php and see:
public function getCurrentCurrencyCode()
{
if (is_null($this->_getData('current_currency_code'))) {
// do not use Mage::app()->getStore()->getCurrentCurrencyCode() because of probability
// to get an invalid (without base rate) currency from code saved in session
$this->setData('current_currency_code', Mage::app()->getStore()->getCurrentCurrency()->getCode());
}
return $this->_getData('current_currency_code');
}
The default currency handling in Magento 1.4. has changed slightly from 1.3.x as you can notice from the comment in the code above. Prior to 1.4, the code used a direct call to getCurrentCurrencyCode() whereas in 1.4 the getCurrentCurrency() method is used. This should not make much difference to us though.
So we investigate further: the Store class should contain a method called: getCurrentCurrency(). We see that the current currency will be set either to a value from a session or to the store’s base currency if no value is available. This process hinges on the getCurrentCurrencyCode() method which in turn retrieves its original default currency via the getDefaultCurrencyCode() method (in: /app/code/core/Mage/Core/Model/Store.php):
public function getDefaultCurrencyCode()
{
$result = $this->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_DEFAULT);
return $result;
}
It simply gets the system config value you set as the store’s default currency.
So, our task now becomes clear. If we want to influence the default currency depending on where the visitor comes from, then we need to inject our own code into the method. To do this, we’ll do an override of the Store class in our own namespace (/app/code/local/MageBase/Custom/etc/config.xml):
<config> <modules> <MageBase_Custom> <version>0.1.0</version> </MageBase_Custom> </modules> <global> <models> <core> <rewrite> <store>MageBase_Custom_Model_Store</store> </rewrite> </core> </models> </global> </config>
Then in /app/code/local/MageBase/Custom/Model/Store.php:
<?php
class MageBase_Custom_Model_Store extends Mage_Core_Model_Store
{
public function getDefaultCurrencyCode()
{
// by default we look for the configured currency code
$result = $this->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_DEFAULT);
// however, in our case we want to determine the default currency depending on the country/continent of the visitor
$geoCountryCode = null;
try {
// we'll try to get the visitor country
$geoCountryCode = geoip_country_code_by_name( $_SERVER['REMOTE_ADDR'] );
} catch (Exception $e) {
// prevent NOTICE error - for example we are running the code on a localhost
}
// first tier check is the specific countries to set the currency for
// NOTE: you can roll your own logic here depending on your enabled/supported countries/currencies
// this example assumes that AUD, GBP, JPY, USD, EUR and NZD are the supported currencies
switch ($geoCountryCode) {
case "AU":
$result = "AUD";
break;
case "GB":
$result = "GBP";
break;
case "JP":
$result = "JPY";
break;
default:
$geoContinentCode = null;
// now grab the continent code and set further by general regions
try {
$geoContinentCode = geoip_continent_code_by_name( $_SERVER['REMOTE_ADDR'] );
// You can debug here to check the continent codes...
Mage::log("GEOIP::CONTINENT::".$geoCountryCode);
} catch (Exception $e) {
Mage::log("EXCEPTION CAUGHT".$e->getMessage());
// prevent NOTICE error
}
// Now decide what currency to set depending on broad regions
switch ($geoContinentCode) {
case "EU": // we'll set EUR for European countries
$result = "EUR";
break;
case "NA": // North America
case "SA": // South America
case "AS": // Asia
case "AF": // Africa - all of them will see USD
$result = "USD";
break;
default: // everything else uses the default store currency as set in config
}
}
return $result;
}
}
To check your debug statements, you need to enable logging in the advanced configuration.
Then all we need is the module xml file in /app/etc/modules/MageBase_Custom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<MageBase_Custom>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_Core />
</depends>
</MageBase_Custom>
</modules>
</config>
Finally, time to test it all which can be a bit tricky since you’re in only one country. But, you can try to find some anonymous web proxies in the countries you want to do spot tests for and run your site through those.
A great byproduct of this approach is that if a visitor still changes their currency preference, that will be saved in their cookie and not overridden by our code here as they subsequently browse the site.
Let us know if you have any other cool ideas around GeoIP.
Originally published on magebase.com. Copyright © 2010 Magebase - All Rights Reserved.




Proud members of the 









Very nice article, Robert! I wish I could find it before.
We’ve been trying to implement the same feature for our client’s store but then found a nice solution, very similar to yours. Store and currency auto switcher extensions by mageworx http://www.mageworx.com/store-and-currency-auto-switcher-magento-extension.html
This is AWESOME.. I don’t need to pay for an extension now… man you are great.
I’ll make all my trademe related code public too soon when it’s done..
So everyone are free to upload to trademe programtically…
Cheers
nathan
[...] [...]
Thank you!
oh this is awesome…. awesome…
[...] up on Robert’s article How To Set Default Store Currency Based On Visitor Country In Magento I wanted to take the concept in a slightly different direction: Firstly I wanted to have an [...]
Hi Robert,
Thank you very much your useful article.
Thanks and Regards,
Kannan P
Hi Robert,
I tried your module. But am getting below fatal error
“Fatal error: Call to undefined function geoip_country_code_by_name()”.
Thanks
Palanikumar
Magento talks
Hi Palani,
Please check installation properly and also check php.ini file and php-geoip extension.
Please check below mention URL:
http://blog.thecodingmachine.com/content/installing-php-geolocalization-extension-centos
Thanks and regards,
Kannan
great module, and great explanation. Any idea how to switch between magento websites instead of currencies?
happy new year
When you say websites, do you mean stores? There is a built in store switcher block. It usually appears on the default template in the top right as the “Your Language” selector when you have multiple stores set up.
You can see this block in page.xml:
If you, however, want to have multiple websites (as set up under Manage Stores > Websites) and have a switcher between them, then you have to make your own block and phtml. I’ve done this for a project so I guess I could create a tutorial around it soon but basically, the block class has the following two methods:
public function getCurrentWebsiteId() { return Mage::app()->getStore()->getWebsiteId(); } public function getWebsites() { if (!$this->getData('websites')) { // get the websites with the website code as the array key $websites = Mage::app()->getWebsites(); foreach ($websites as $_idx => &$_website) { if ('admin' == $_website->getCode()) { unset($websites[$_idx]); } else { $_ws_url = $_website->getDefaultStore()->getUrl(); $_website->setStoreUrl($_ws_url); } } $this->setData('websites', $websites); } return $this->getData('websites'); }Then, in your phtml template file you can do the following:
<?php $_websites = $this->getWebsites(); $_currentId = $this->getCurrentWebsiteId(); if (count($_websites > 0)) : ?> <div class="website-switcher"> <select id="websites" class="styled" onchange="window.location.href=this.value"> <?php foreach($_websites as $_website) : $_isCurrent = ($_currentId == $_website->getWebsiteId()) ?> <option value="<?php echo str_replace('&', '&', $_website->getStoreUrl()) ?>" class="<?php echo $_website->getCode() ?>"<?php echo $_isCurrent?' selected="selected"':'' ?>><?php echo $_website->getName() ?></option> <?php endforeach; ?> </select> </div> <?php endif; ?>Hope this helps.
thanks for the really quick reply, I have a switcher already built a bit differently as below. Sorry for confusion but I am trying to create an automatic website switcher as you said like in Stores > websites. I have a few different “magento websites” which I want to switch between based on what IP it is, I tried to extend website.php in a similar way to you have extended store.php by using function getDefaultStore() but this function is looking to return a magento object Mage_Core_Model_Store so not sure if this is the right way to do it. many thanks for the help
<?php $websites = Mage::getModel('core/website')->getCollection(); $i = 0; foreach($websites as $website) { $default_store = $website->getDefaultStore(); $url_obj = new Mage_Core_Model_Url(); $default_store_path = $url_obj->getBaseUrl(array('_store'=> $default_store->getCode())); ?> <option name="<?php $i++; echo $i; ?>" <?php if(strstr($this->helper('core/url')->getCurrentUrl(), $default_store_path)): ?>selected="selected"<?php endif; ?> value="<?php echo $default_store_path ?>"><?php echo $website->getName(); ?></option> <?php } ?>What you’re doing seems OK. The
getDefaultStore()method indeed returns a Store object which has a method:getUrl()so you can replace your code with:<?php $websites = Mage::getModel('core/website')->getCollection(); $i = 0; foreach($websites as $website) { $default_store_path = $website->getDefaultStore()->getUrl(); ?> <option name="<?php $i++; echo $i; ?>" <?php if(strstr($this->helper('core/url')->getCurrentUrl(), $default_store_path)): ?>selected="selected"<?php endif; ?> value="<?php echo $default_store_path ?>"><?php echo $website->getName(); ?></option> <?php } ?>thanks again but it is not the template I’m trying to change rather the way the ip directs the user to the right site in you function
public function getDefaultCurrencyCode() { ...... }the currency code is pulled from the geoip data and returned back to the store to dislay the currency code.
I want to use the currency code from the geoip data to set what website the user is on. Maybe I am not doing this the right way, I had a search throught the code and couldn’t find any where any function like getDefaultWebsite so have been trying it with
OK I see. You want to redirect the customer to a specific website depending on which Geographical region they come from.
This is a tricky job. I’ve done this for another site without modifying any code, but using redirects in .htaccess. However, I had to consider various scenarios and it was quite complicated in the end.
In my, case, there were 4 sites for NZ, AU, EU and US & rest of the world for the same store. The US (.com) site was set as the default store. When I enabled the redirect, the issue was when somebody was redirected to their specific store (ie. NZ store) but then wanted to change to the US store. They would be redirected again to the NZ store because the geoip redirect kicks in after selecting another store. So, I had to take that into consideration for the redirect. So my final rules were to redirect only when there is no frontend store cookie set already.
Finally, with the redirects you may have issues with things like payment gateway IPNs and other processes that access the site from “outside” so you may need to make sure that these services end up on the correct store/website.
Essentially, the approach is to set the store code base on geoip. You can do that in .htaccess using the Magento environment variables: MAGE_RUN_CODE and MAGE_RUN_TYPE or you can add some code into your index.php in the root folder. You can get some info on this setting the store variables at: http://www.magentocommerce.com/wiki/4_-_themes_and_template_customization/navigation/multiple-website-setup
Just some food for thought.
thanks very much, thats what I do mean. I’ll have a think about it, it’s starting to become a very interesting project.