How to Switch Currency Based on Billing Address Instead of Geolocation
If you’re experiencing geolocation issues where customers are being charged in the wrong currency, this guide will help you switch to billing address-based currency detection.
The Problem
Common symptoms (example):
- Canadian customers being charged in USD instead of CAD (or vice versa)
- Customers near country borders getting wrong currency
- 5-10% failure rate with IP-based geolocation
- Currency doesn’t match customer’s actual location
Example scenario: A Canadian customer visits your store. Geolocation detects their IP as US (common near borders or with VPNs), so they see USD prices. At checkout, they select Canada as billing country, but still get charged in USD.
Root cause: IP-based geolocation is never 100% accurate, especially:
- Near country borders (US/Canada, EU countries)
- Using VPN or proxy services
- Corporate networks with centralized IPs
- Mobile networks routing through different countries
Understanding the Issue
Why Geolocation Fails
Common causes:
- MaxMind database inaccuracy – Free GeoLite2 database has limited accuracy
- Border proximity – IPs near borders often misidentified
- VPN/Proxy usage – Customer’s real location hidden
- Caching conflicts – Page cached with default currency
- Server proxy issues – Hosting provider sends server IP instead of visitor IP
Geolocation vs. Billing Country
| Method | Accuracy | User Control | Reliability |
|---|---|---|---|
| Geolocation (IP) | 85-95% | None | Inconsistent |
| Billing Address | 100% | Full | Always accurate |
Recommendation: Use billing address for currency detection – customers explicitly choose their country at checkout.
Diagnostic Steps (Before Fixing)
Step 1: Test Current Geolocation
Add this shortcode to any page:
[woocs_geo_hello]
Expected output:
Your country is: Canada (defined by WooCommerce GeoIP functionality)
What this tells you:
- If country is correct → WOOCS is fine, issue is with WooCommerce geolocation
- If country is wrong → Need to fix WooCommerce MaxMind integration
- If no output → Geolocation not working at all
Step 2: Check WooCommerce Geolocation Status
- Go to WooCommerce → Status
- Scroll to “Geolocation debug info” section
- Check for errors or warnings
Step 3: Verify MaxMind License
- Go to WooCommerce → Settings → Integration → MaxMind Geolocation
- Check if license key is present and valid
- Check database update date (should be recent)
If missing license:
- Register for free at https://www.maxmind.com/en/geolite2/signup
- Add license key in WooCommerce settings
- Database updates automatically monthly
Solution 1: Fix Geolocation Issues (If You Want to Keep Using It)
Fix #1: MaxMind License Key
Problem: WooCommerce 3.9+ requires MaxMind license
Solution:
- Register at https://www.maxmind.com/en/geolite2/signup
- Generate license key
- Add to WooCommerce → Settings → Integration → MaxMind Geolocation
- Database downloads automatically
Fix #2: Caching Conflicts
Problem: Cached pages show default currency before geolocation runs
Solution:
In WooCommerce:
- Go to WooCommerce → Settings → General
- Set “Default customer location” to “Geolocate (with page caching support)”
For WP Rocket:
- Go to Cache → User Cache
- Enable “Disable caching for logged-in users”
- Consider excluding shop/checkout pages from cache
For LiteSpeed Cache:
- Exclude
/shop/,/product/,/checkout/from cache - Enable ESI for dynamic content
Note: WOOCS can work with caching by redrawing prices via AJAX after page load.
Fix #3: Server/Proxy IP Issues
Problem: Server sends its own IP instead of visitor’s IP
Affected hosts: Bluehost, servers with Varnish, Cloudflare
Solution:
- Ask hosting support to configure
HTTP_X_REAL_IPorHTTP_X_FORWARDED_FORheaders - For Cloudflare: Dashboard → Caching → Configuration → set “Browser Cache TTL” to “Respect Existing Headers”
Fix #4: Browser Caching
Problem: Geolocation only works on first visit, then remembers choice
Testing solution:
- Use incognito/private mode for testing
- Clear cookies between tests
- Use VPN or https://www.locabrowser.com to test different locations
Solution 2: Switch to Billing Address Method (Recommended)
This solution switches currency automatically when customer selects their billing country at checkout.
Basic Implementation (All Countries)
Add this code to your child theme’s functions.php or Code Snippets plugin:
/**
* Switch currency based on billing country selection
* Works during checkout when customer selects billing address
*/
add_action('woocommerce_checkout_update_order_review', function ($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
// Map billing country to currency
$currency_map = array(
'US' => 'USD',
'CA' => 'CAD',
'GB' => 'GBP',
'AU' => 'AUD',
'EU' => 'EUR', // For Euro zone countries
'DE' => 'EUR',
'FR' => 'EUR',
'ES' => 'EUR',
'IT' => 'EUR',
);
// Get currency for selected country
if (isset($currency_map[$country])) {
$new_currency = $currency_map[$country];
// Switch currency if different from current
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}
}, 10);
How it works:
- Customer arrives at checkout page
- Selects billing country from dropdown
- WooCommerce triggers AJAX to recalculate order
- Code detects country selection and switches currency
- Order totals update immediately with correct currency
Add more countries: Simply add lines to $currency_map array:
'MX' => 'MXN', // Mexico 'JP' => 'JPY', // Japan 'BR' => 'BRL', // Brazil
Canada-Specific Implementation
Use case: Only Canada uses CAD, all other countries use USD
/**
* Canada = CAD, all other countries = USD
*/
add_action('woocommerce_checkout_update_order_review', function ($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
// Canada = CAD, everything else = USD
$new_currency = ($country === 'CA') ? 'CAD' : 'USD';
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
Result:
- Billing country = Canada → CAD
- Billing country = United States → USD
- Billing country = United Kingdom → USD
- Billing country = Germany → USD
- Any other country → USD
Euro Zone Implementation
Use case: Multiple Euro zone countries
/**
* Euro zone countries = EUR, others = USD
*/
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
// Define Euro zone countries
$euro_countries = ['AT', 'BE', 'CY', 'EE', 'FI', 'FR', 'DE', 'GR',
'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PT',
'SK', 'SI', 'ES'];
// Determine currency
if (in_array($country, $euro_countries)) {
$new_currency = 'EUR';
} else {
$new_currency = 'USD';
}
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
With Debug Logging (For Troubleshooting)
If you need to debug currency switching issues, use this version with logging:
/**
* Billing address currency switching with debug logging
*/
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
// Canada = CAD, all others = USD
$new_currency = ($country === 'CA') ? 'CAD' : 'USD';
// Debug logging
error_log('WOOCS billing country received: ' . $country . ' → currency: ' . $new_currency);
error_log('WOOCS current currency: ' . $WOOCS->current_currency . ' → switching to: ' . $new_currency);
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
To view logs:
- Enable WordPress debug logging in
wp-config.php:define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); - Logs saved to
/wp-content/debug.log - Download via FTP to review
Remove logging after debugging: Simply delete the two error_log() lines from the code.
Solution 3: Completely Disable Geolocation
If geolocation causes too many issues, disable it entirely and rely only on manual currency selection + billing address.
Step 1: Disable WooCommerce Geolocation
- Go to WooCommerce → Settings → General
- Find “Default customer location”
- Select “Shop base address” or “No location by default”
What this does:
- ✅ Currency switcher still works – customers can manually change
- ✅ All currencies remain active
- ✅ Billing address code (Solution 2) still works
- ❌ No automatic currency detection on first visit
Step 2: Remove WOOCS GeoIP Rules
- Go to WOOCS settings → GeoIP tab
- Remove all geolocation rules
- Save settings
Step 3: Set Default Currency
- In WOOCS settings → Options
- Set your main market currency as default
- Example: If primarily US market, set USD as default
Result:
- All visitors see default currency on arrival
- Customers can manually switch using currency selector
- At checkout, currency switches automatically based on billing country (if you added Solution 2 code)
Where to Add the Code
Option 1: Child Theme functions.php (Recommended)
- Go to Appearance → Theme Editor
- Select Child Theme (important!)
- Open
functions.php - Add code at the end of the file
- Click Update File
Why child theme:
- Parent theme updates won’t erase your code
- Safer and more maintainable
Option 2: Code Snippets Plugin (Easier)
- Install Code Snippets plugin: https://wordpress.org/plugins/code-snippets/
- Go to Snippets → Add New
- Paste code
- Set to “Run everywhere”
- Activate snippet
Advantages:
- No theme file editing
- Easy to enable/disable
- Survives theme changes
- Safer for non-developers
Important for Code Snippets: Code Snippets loads via plugins_loaded hook, which should work fine for checkout AJAX. However, if for some reason the code doesn’t trigger (very rare), use functions.php instead for 100% reliability.
Troubleshooting
Currency Still Wrong After Billing Country Selection
Problem: Customer selects Canada, but still sees USD
Possible causes:
- Geolocation overriding billing address
- Solution: Disable geolocation completely (Solution 3)
- Geolocation runs first and may “stick” the wrong currency
- Code not running
- Check if code is in correct location
- Verify no PHP errors (check error log)
- Try switching from Code Snippets to functions.php
- Currency storage method
- Go to WOOCS → Options → Currency storage
- Try switching to “transient” storage mode
- This can help avoid conflicts with caching
- Caching interference
- Clear all caches (WordPress, server, browser)
- Exclude checkout page from caching
- Test in incognito mode
Geolocation Conflicts with Billing Address
Problem: Even with billing address code, geolocation overrides it
Diagnosis:
// Add debug code
error_log('Geo detected: ' . $WOOCS->current_currency);
error_log('Billing country: ' . $country);
Solution: If logs show geolocation currency appearing AFTER billing country switch, you need to:
- Disable geolocation completely
- OR increase priority of billing address hook:
add_action('woocommerce_checkout_update_order_review', function($post_data) { // ... your code ... }, 999); // Higher priority = runs later
Code Causes White Screen / Errors
Problem: Adding code breaks site
Causes:
- PHP syntax error (missing bracket, semicolon)
- Wrong file edited (edited parent theme instead of child)
- Plugin conflict
Solution:
- Access site via FTP
- Rename
functions.phptofunctions.php.backup - Site recovers
- Review code for syntax errors
- Try adding code in smaller sections
Currency Map Not Working
Problem: Some countries show wrong currency
Check:
- Country code is correct (2-letter ISO code)
- Currency code exists in WOOCS
- Case sensitivity: Use ‘CA’ not ‘ca’
Find country codes:
- WooCommerce uses ISO 3166-1 alpha-2 codes
- Full list: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
Billing Address Shows “United States (US)” But Code Expects “US”
Problem: Country string is “United States (US)” but code checks for “US”
Solution: Code already uses trim() but WooCommerce sends just “US”, so this shouldn’t be an issue. If it is:
// Extract country code
$country = trim($data['billing_country']);
if (strlen($country) > 2) {
$country = substr($country, -2); // Get last 2 characters
}
Best Practices
- Always use child theme – Prevents code loss on theme updates
- Test thoroughly – Use multiple countries in testing
- Monitor error logs – Check for issues after implementation
- Clear all caches – WordPress, server, browser, CDN
- Document your setup – Note which solution you implemented
- Keep WOOCS updated – Latest version has bug fixes
- Backup before changes – Always backup functions.php before editing
Performance Considerations
AJAX Impact
What happens at checkout:
- Customer changes billing country
- WooCommerce triggers AJAX call
- Your code runs and switches currency
- Order totals recalculate
- Page updates with new currency
Performance: Minimal impact – AJAX call already happening, code just adds currency switch.
Geolocation vs. Billing Address
| Aspect | Geolocation | Billing Address |
|---|---|---|
| Server Load | Higher (IP lookup) | Lower (no lookup) |
| Accuracy | 85-95% | 100% |
| Speed | Slower (external API) | Instant |
| Caching Friendly | No | Yes |
Recommendation: Billing address is faster and more cache-friendly.
Real-World Examples
Example 1: US/Canada Store (Most Common)
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
$new_currency = ($country === 'CA') ? 'CAD' : 'USD';
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
Setup:
- Default currency: USD
- Geolocation: Disabled
- Manual switcher: Enabled for customer choice
Example 2: Global Store with Multiple Currencies
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
$currency_map = [
'US' => 'USD',
'CA' => 'CAD',
'GB' => 'GBP',
'AU' => 'AUD',
'NZ' => 'NZD',
'JP' => 'JPY',
'CN' => 'CNY',
// Euro zone
'DE' => 'EUR', 'FR' => 'EUR', 'ES' => 'EUR', 'IT' => 'EUR',
'NL' => 'EUR', 'BE' => 'EUR', 'AT' => 'EUR', 'PT' => 'EUR',
];
// Default to USD if country not mapped
$new_currency = isset($currency_map[$country]) ? $currency_map[$country] : 'USD';
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
Example 3: Regional Grouping
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
// Define regional currencies
$euro_zone = ['AT', 'BE', 'CY', 'EE', 'FI', 'FR', 'DE', 'GR', 'IE',
'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PT', 'SK', 'SI', 'ES'];
$gbp_zone = ['GB', 'UK'];
$aud_zone = ['AU', 'NZ'];
// Determine currency
if (in_array($country, $euro_zone)) {
$new_currency = 'EUR';
} elseif (in_array($country, $gbp_zone)) {
$new_currency = 'GBP';
} elseif (in_array($country, $aud_zone)) {
$new_currency = 'AUD';
} elseif ($country === 'CA') {
$new_currency = 'CAD';
} else {
$new_currency = 'USD'; // Default
}
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
Summary
The Problem: Geolocation by IP is 85-95% accurate, causing wrong currency charges
Best Solution: Use billing address for 100% accurate currency detection
Implementation:
- Add billing address code to functions.php or Code Snippets
- Optionally disable geolocation completely
- Keep manual currency switcher enabled for flexibility
Key Code (Canada/US example):
add_action('woocommerce_checkout_update_order_review', function($post_data) {
global $WOOCS;
parse_str($post_data, $data);
if (isset($data['billing_country']) AND !empty($data['billing_country'])) {
$country = trim($data['billing_country']);
$new_currency = ($country === 'CA') ? 'CAD' : 'USD';
if ($WOOCS->current_currency !== $new_currency) {
$WOOCS->set_currency($new_currency);
}
}
}, 10);
Benefits:
- ✅ 100% accurate currency detection
- ✅ No geolocation API calls
- ✅ Better performance
- ✅ Cache-friendly
- ✅ Customer controls their currency
- ✅ No border proximity issues
- ✅ Works with VPNs
This approach eliminates geolocation uncertainty and gives customers full control over their currency at checkout.
Source: https://pluginus.net/support/topic/geotagging-issues
