FOX - WooCommerce Currency Switcher Professional

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:

  1. MaxMind database inaccuracy – Free GeoLite2 database has limited accuracy
  2. Border proximity – IPs near borders often misidentified
  3. VPN/Proxy usage – Customer’s real location hidden
  4. Caching conflicts – Page cached with default currency
  5. 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

  1. Go to WooCommerce → Status
  2. Scroll to “Geolocation debug info” section
  3. Check for errors or warnings

Step 3: Verify MaxMind License

  1. Go to WooCommerce → Settings → Integration → MaxMind Geolocation
  2. Check if license key is present and valid
  3. Check database update date (should be recent)

If missing license:

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:

  1. Register at https://www.maxmind.com/en/geolite2/signup
  2. Generate license key
  3. Add to WooCommerce → Settings → Integration → MaxMind Geolocation
  4. 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_IP or HTTP_X_FORWARDED_FOR headers
  • 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:

  1. Enable WordPress debug logging in wp-config.php:
    define('WP_DEBUG', true);
    define('WP_DEBUG_LOG', true);
    define('WP_DEBUG_DISPLAY', false);
  2. Logs saved to /wp-content/debug.log
  3. 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

  1. Go to WooCommerce → Settings → General
  2. Find “Default customer location”
  3. 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

  1. Go to WOOCS settings → GeoIP tab
  2. Remove all geolocation rules
  3. Save settings

Step 3: Set Default Currency

  1. In WOOCS settings → Options
  2. Set your main market currency as default
  3. 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)

  1. Go to Appearance → Theme Editor
  2. Select Child Theme (important!)
  3. Open functions.php
  4. Add code at the end of the file
  5. Click Update File

Why child theme:

  • Parent theme updates won’t erase your code
  • Safer and more maintainable

Option 2: Code Snippets Plugin (Easier)

  1. Install Code Snippets plugin: https://wordpress.org/plugins/code-snippets/
  2. Go to Snippets → Add New
  3. Paste code
  4. Set to “Run everywhere”
  5. 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:

  1. Geolocation overriding billing address
    • Solution: Disable geolocation completely (Solution 3)
    • Geolocation runs first and may “stick” the wrong currency
  2. 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
  3. Currency storage method
    • Go to WOOCS → Options → Currency storage
    • Try switching to “transient” storage mode
    • This can help avoid conflicts with caching
  4. 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:

  1. Disable geolocation completely
  2. 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:

  1. Access site via FTP
  2. Rename functions.php to functions.php.backup
  3. Site recovers
  4. Review code for syntax errors
  5. Try adding code in smaller sections

Currency Map Not Working

Problem: Some countries show wrong currency

Check:

  1. Country code is correct (2-letter ISO code)
  2. Currency code exists in WOOCS
  3. 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

  1. Always use child theme – Prevents code loss on theme updates
  2. Test thoroughly – Use multiple countries in testing
  3. Monitor error logs – Check for issues after implementation
  4. Clear all caches – WordPress, server, browser, CDN
  5. Document your setup – Note which solution you implemented
  6. Keep WOOCS updated – Latest version has bug fixes
  7. 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:

  1. Add billing address code to functions.php or Code Snippets
  2. Optionally disable geolocation completely
  3. 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