What is Currency Switcher for WooCommerce?
Currency Switcher lets your international customers browse and purchase in their local currency. It automatically fetches live exchange rates, detects the visitor's country via GeoIP, and can optionally convert the full WooCommerce checkout — cart, order totals, and payment — into the selected currency. Everything runs from a single clean admin panel with zero external dependencies.
storzen-cs/v1 for headless & decoupled WooCommerce setups — switch currency and list enabled currencies.Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| WordPress | 6.3 | 7.1+ |
| WooCommerce | 7.0 | 11.0+ |
| PHP | 8.0 | 8.2+ |
| MySQL | 5.6 | 8.0+ |
| HTTPS | Required for GeoIP API requests | |
Installation
storzen-multi-currency-switcher-for-woocommerce.zip file and click Install Now.General Settings
The General tab controls the conversion mode, base currency, and which currencies are available to visitors. This is the primary configuration screen.
Conversion Mode
Full Checkout — the selected currency applies to the cart, order total, and the amount charged by the payment gateway. Your payment gateway must support multiple currencies (Stripe, PayPal, etc.).
Visual Only — prices are displayed in the chosen currency, but WooCommerce charges the base store currency at checkout. Useful when your gateway only supports one currency.
Display Settings
The Display tab controls how the currency switcher looks on the frontend — the label format, rounding, decimals, and a live preview of the actual switcher widget.
Exchange Rates
The Exchange Rates tab selects the rate source, sets the refresh schedule, and shows the last fetch time. A Manual Refresh button lets you force an immediate rate update.
Conversion Modes
Visual Only mode
In this mode, prices are converted and displayed in the visitor's selected currency everywhere on the frontend — product pages, shop archives, cart, and checkout. However, WooCommerce internally processes the order and charges the payment gateway in the base store currency. The converted price is for display only.
Use this mode if your payment gateway does not support multi-currency, or if you only want to give customers a price indication without actually charging in local currency.
Full Checkout mode
In Full Checkout mode, the conversion goes all the way through: cart totals, tax calculations, shipping, and the final charge sent to the payment gateway are all in the selected currency. Orders are stored in WooCommerce with the currency code the customer used.
GeoIP Auto-Detection
When GeoIP is enabled, the plugin sends a lightweight request to api.country.is (free, no API key, no account required) to detect the visitor's country on their first page load. The visitor's IP address is sent in that request. The country code is matched against a built-in country→currency map and the result is stored in a first-party cookie (storzen_cs_country) for 30 days — no per-IP data is ever written to the database.
If GeoIP detects a country that maps to a currency not in your enabled list, the plugin falls back to the base store currency. Role Access rules still apply: a detected currency that is not allowed for the visitor is ignored.
Smart Rounding
After converting a price, raw floating-point results (e.g. 24.571...) are rarely attractive to shoppers. The rounding system applies one of four modes to every converted price. The examples below use 2 decimal places:
| Mode | Example Input | Output | Best for |
|---|---|---|---|
| None | $24.571 | $24.57 | Exact pricing, B2B |
| Round | $24.571 | $24.57 | Rounds to the decimal places set in Decimals (use 0 for whole numbers) |
| Charm (.95) | $24.571 | $24.95 | Retail — rounds up to the next whole number, then subtracts .05 |
| .99 Pricing | $24.571 | $24.99 | Retail — rounds up to the next whole number, then subtracts .01 |
$25.00) becomes $24.95 or $24.99.Per-Product Price Override
Sometimes the auto-converted rate is not the right price for a specific market. The per-product override lets you set an exact fixed price for each currency directly on the WooCommerce product edit screen.
Go to Product → Product Data → Storzen Currency Prices. Enter a fixed price for each enabled currency. Leave a field empty to fall back to the auto-converted rate. Per-product fixed prices are a Pro feature. In the free version the tab shows a locked preview.
Widget & Shortcode
Shortcode
Place the currency switcher anywhere using the shortcode:
| Attribute | Values | Default |
|---|---|---|
| style | dropdown, buttons, select | dropdown |
| show_flag | true, false | plugin setting |
| class | Any CSS class string | — |
PHP function
WordPress Widget
The plugin registers a Currency Switcher widget in Appearance → Widgets. Drag it into any widget area. Configure the style and flag display from the widget settings panel.
Gutenberg Block v1.1.3
The plugin registers a native WordPress block — storzen-cs/switcher — that works in the Block Editor and Full Site Editor (FSE). No shortcode or PHP needed.
All Settings
All settings are stored in a single WordPress option: storzen_cs_settings (serialised array).
| Key | Type | Default | Description |
|---|---|---|---|
| mode | string | full | Conversion mode: full or visual |
| currencies | array | [] | Enabled currency codes (ISO 4217) |
| base_currency | string | WC setting | Store base currency — mirrors WooCommerce |
| rate_source | string | ecb | Rate provider: ecb, erapi, open_exchange_rates, manual |
| api_key | string | "" | OpenExchangeRates App ID (only if source = oxr) |
| refresh_interval | int | 12 | Hours between automatic rate refreshes |
| geoip_enabled | bool | true | Enable GeoIP auto-detection on first visit |
| rounding | string | none | Rounding mode: none, charm, round, ninety_nine |
| decimals | int | 2 | Decimal places for converted prices |
| show_flag | bool | true | Show country flag emoji in the switcher |
| switcher_display | string | symbol_code | Label format: symbol, code, symbol_code, name |
| switcher_style | string | dropdown | Switcher UI style: dropdown, buttons, select |
| manual_rates | array | {} | Manual rate overrides: {"EUR": 0.92, "GBP": 0.79} |
| auto_placement_enabled | bool | false | Enable one-click theme header placement (Storefront, Astra, OceanWP, etc.) |
| storzen_cs_lang_map | array | {} | Language → currency pairs (separate option key): {"fr": "EUR", "bn": "BDT"} — set from Language Map tab |
| storzen_cs_role_access | array | {} | Allowed currencies per role (separate option key, v1.1.5): {"__guest": ["USD","EUR"], "wholesale_customer": ["USD"]} — set from Role Access tab. __guest is the logged-out visitor. A role with no entry is unrestricted. |
Hooks & Filters
Filters
| Hook | Arguments | Description |
|---|---|---|
| storzen_cs_rate | $rate, $base, $currency, $product |
Override the exchange rate for a specific currency pair. Return a float. |
| storzen_cs_convert_price | $converted, $base, $currency, $product |
Modify the final converted price after rate & rounding are applied. Return a float. |
| storzen_cs_enabled_currencies | $currencies |
Programmatically add or remove currencies from the enabled list. Return a string[]. |
| storzen_cs_geoip_country | $country |
Override the detected country code before it is mapped to a currency. Return an ISO 3166-1 alpha-2 string. |
| storzen_cs_switcher_html | $html, $atts |
Filter the fully-rendered switcher HTML string before output. Used in shortcode and block render. |
Filters (v1.1.4)
| Hook | Arguments | Description |
|---|---|---|
| storzen_cs_coupon_currency_error | $message, $currency, $allowed |
Customise the error notice shown when a coupon is rejected due to currency mismatch. |
Filters (v1.1.5)
| Hook | Arguments | Description |
|---|---|---|
| storzen_cs_role_access_user_allowed | $allowed, $user_id, $roles |
Filter the currencies a user may use. $allowed is a string[] or null (unrestricted). $user_id is 0 for guests; $roles holds the role slugs, or ["__guest"]. |
| storzen_cs_role_access_exempt | $exempt |
Return true to skip role rules for the current request. Default false. Admin screens, cron, WP-CLI and the plugin's own admin AJAX are always exempt. |
Actions
| Hook | Description |
|---|---|
| storzen_cs_rates_refreshed | Fires after exchange rates are successfully fetched and cached. |
| storzen_cs_currency_switched | Fires when a visitor switches their active currency. |
| storzen_cs_lang_currency_switched | Fires after the currency is switched due to a WPML / Polylang language change. Args: $currency, $lang. |
| storzen_cs_role_access_saved | Fires after the Role Access rules are saved (v1.1.5). Arg: $rules. Hook here to purge page caches that show the switcher. |
| storzen_cs_role_access_advanced_panel | Admin only (v1.1.5). Renders at the bottom of the Role Access tab when Pro is active. Pro uses it to show advanced role rules. |
REST API v1.1.3
The plugin registers two REST API endpoints under the storzen-cs/v1 namespace. Both require a valid WP nonce (X-WP-Nonce header or _wpnonce query param).
Switch currency
POST /wp-json/storzen-cs/v1/switch
| Parameter | Type | Description |
|---|---|---|
| currency | string | ISO 4217 code to switch to (e.g. EUR) |
On success returns 200 with { "currency": "EUR", "rate": 0.93, "symbol": "€", … }. Returns 400 if the currency is not in the enabled list.
List enabled currencies
GET /wp-json/storzen-cs/v1/currencies
Returns an array of enabled currency objects: { "code": "EUR", "symbol": "€", "name": "Euro", "flag": "🇪🇺" }. Public endpoint — no authentication required.
wp_ajax_storzen_cs_switch) continues to work and requires no changes.
Currency-Specific Coupons v1.1.4
Restrict any WooCommerce coupon so it is only valid when the customer's active currency matches. Create loyalty codes for specific markets — BANGLA20 for BDT shoppers, SUMMER10 for USD, EURO15 for EUR — without any code.
How to set a currency restriction
Developer hook
| Hook | Arguments | Description |
|---|---|---|
| storzen_cs_coupon_currency_error | $message, $currency, $allowed |
Filter the error notice shown when a coupon is rejected due to currency mismatch. Return a custom string. |
WPML / Polylang Language Map v1.1.4
Automatically switch the active currency whenever a visitor changes language. Requires WPML or Polylang to be installed and active. Configure language → currency pairs from the new Language Map admin tab.
Setup
How it works
The plugin hooks into:
| Event | Plugin | What happens |
|---|---|---|
| wpml_language_has_switched | WPML | Currency switches immediately when the user changes language. |
| init (priority 20) | WPML & Polylang | Currency syncs on every page load from the active language cookie/URL. |
| pll_language_defined | Polylang | Currency syncs once Polylang resolves the current language. |
| pll_set_language | Polylang | Currency switches when visitor uses the Polylang language switcher. |
Developer action
| Hook | Arguments | Description |
|---|---|---|
| storzen_cs_lang_currency_switched | $currency, $lang |
Fires after the currency is switched due to a language change. Use this to sync other systems. |
Role-Based Currency Access v1.1.5
Control which currencies each customer group can use. For example, show wholesale buyers only USD, let logged-out guests choose between USD and EUR, and keep every currency for retail customers. Rules are set per WordPress user role, plus one row for logged-out visitors.
Setup
Example
| Role | USD | EUR | GBP | Result |
|---|---|---|---|---|
| Guest (logged out) | ✓ | ✓ | Sees USD and EUR | |
| Customer | ✓ | ✓ | ✓ | No restriction |
| Wholesale Customer | ✓ | USD only — the switcher is not shown |
How the rules work
| Situation | What happens |
|---|---|
| Role has no rule | The role can use every enabled currency (safe default for existing stores). |
| User has several roles | The user can use the currencies of all their roles. If any one role has no rule, the user is unrestricted. |
| Current currency is not allowed | The store base currency is used. If the base currency is not allowed either, the first allowed currency is used. |
| Only one currency allowed | The switcher is not shown and the frontend script is not loaded for that visitor. |
| Allowed currency was disabled later | If a rule would leave a visitor with no usable currency, the rule is ignored so checkout never breaks. |
| GeoIP, language map, cookie | All follow the same rules. A currency chosen by GeoIP or the language map that is not allowed is ignored. |
| Direct switch request | A request for a hidden currency is rejected with 403 and the message “This currency is not available for your account.” |
| Admin screens, cron, WP-CLI | Not restricted. Exchange rates are always fetched for every enabled currency. |
Developer hooks
| Hook | Type | Description |
|---|---|---|
| storzen_cs_role_access_user_allowed | Filter | Change the allowed currencies for a user. Args: $allowed, $user_id, $roles. |
| storzen_cs_role_access_exempt | Filter | Return true to skip role rules for the current request. |
| storzen_cs_role_access_saved | Action | Fires after the rules are saved. Arg: $rules. |
Frequently Asked Questions
Does this work with my payment gateway?
get_woocommerce_currency() — including Stripe, PayPal, Square, and Mollie — will work. Test with a small transaction first. In Visual Only mode, the gateway always receives the base store currency so compatibility is guaranteed.Are exchange rates real-time?
Is this GDPR compliant?
api.country.is to determine their country. The detected country code is stored in a first-party cookie for 30 days — the raw IP address is never written to the database. The active currency is also stored in a first-party cookie (storzen_cs_currency) containing only the ISO currency code — no personal data. Exchange-rate requests contain no visitor data.Can I set different prices per country rather than using the exchange rate?
Will it slow down my site?
Where does the currency selection get stored?
storzen_cs_currency for 30 days. The selection persists across browser sessions. If GeoIP is enabled, the first-visit detection populates the cookie automatically.