Developer Reference
REST API endpoints, WordPress hooks, PHP API, feature flags, order metadata, and database schema for FCHub Multi-Currency.
Everything a developer needs to extend, integrate with, or debug FCHub Multi-Currency.
REST API
All endpoints use the fchub-mc/v1 namespace. Admin endpoints require manage_options capability and a valid X-WP-Nonce header.
Public Endpoints
Returns the current resolved currency context. An optional currency=EUR
query resolves that allowed browser preference for cached-page recovery without
depending on a cookie reaching WordPress, independently of the public
URL-parameter setting. Every response uses
Cache-Control: no-store because visitor context makes an excellent cache key
and a dreadful shared response.
Response:
{
"data": {
"display_currency": "EUR",
"base_currency": "USD",
"rate": "0.92140000",
"source": "cookie",
"is_base_display": false,
"context": {
"rate": 0.9214,
"displayCurrency": "EUR",
"displayCurrencyName": "Euro",
"baseCurrency": "USD",
"decimals": 2,
"symbol": "€",
"position": "right_space",
"isBaseDisplay": false,
"resolverSource": "cookie",
"displayDecSep": ",",
"displayThousandSep": ".",
"disclosureEnabled": true,
"disclosureText": "Prices shown in EUR are approximate. Your payment will be processed in USD.",
"presentation": {}
}
}
}Admin Endpoints
All require manage_options and nonce verification.
Returns all plugin settings with defaults merged.
WordPress Hooks
Actions (fired by the plugin)
| Hook | Arguments | Description |
|---|---|---|
fchub_mc/context_switched | string $currencyCode, int $userId | Fired when a user selects a new currency. $userId is 0 for guests. |
fchub_mc/rates_refreshed | string $baseCurrency, int $count | Fired after a successful rate refresh. |
fchub_mc/community_currency_updated | string $currencyCode, int $userId | Fired after FluentCommunity user meta is written. |
Filters (fired by the plugin)
| Hook | Arguments | Description |
|---|---|---|
fchub_mc/modules | array $modules | Modify the list of modules before registration. Add or remove module classes. |
fchub_mc/context | CurrencyContext $context | Override the resolved currency context. Runs after the resolver chain. |
Hooks consumed (FluentCart)
| Hook | Type | Description |
|---|---|---|
fluent_cart/integration/addons | filter | Registers the plugin as a FluentCart addon |
fluent_cart/before_payment_methods | action | Adds the validated display-currency field inside regular and modal checkout forms |
fluent_cart/checkout/prepare_other_data | action | Captures that checkout field before the order is finalised |
fluent_cart/order_paid_done | action | Triggers order snapshot and FluentCRM sync |
fluent_cart/store_settings/values | filter | Adds multi-currency fields to store settings API |
fluent_cart/loading_app | action | Enqueues admin JS bundle |
Extending FluentCart's currency catalogue
The Currencies tab reads FluentCart's filtered catalogue rather than a private
copy. A currency added through fluent_cart/accepted_currencies therefore appears
in Multi-Currency as well. Supply its symbol through
fluent_cart/global_currency_symbols; add it to
fluent_cart/zero_decimal_currencies when appropriate. A custom currency still
needs a complete Manual rate unless the selected remote provider actually returns
it. The provider cannot quote a currency merely because we asked it nicely.
PHP API
fchub_mc_format_price(float $basePrice): string
Converts a FluentCart base-currency amount in minor units to the visitor's display currency and returns a formatted price string.
echo fchub_mc_format_price(10000); // FluentCart stores 100.00 as 10000
// Output: "€92.14" (when display currency is EUR with rate 0.9214)Falls back to FluentCart's native formatter when the plugin is disabled or the
visitor is in the base currency. Converted output uses the selected display
currency's own decimals, separators, symbol and position rather than inheriting
the base currency's decimal rules. Uses bcmul for precision when bcmath is
available.
fchub_mc_get_order_display_currency(int $orderId): ?string
Returns the display currency captured for an order, or null when no
Multi-Currency snapshot exists.
fchub_mc_format_order_price(float $basePrice, int $orderId): string
Formats FluentCart minor units with the rate captured on that order. It falls back to FluentCart's base formatter when the order has no display-currency snapshot.
JavaScript Globals
// Which currency this visitor is seeing
window.fchubMc.currentCurrency(); // 'EUR'
// Record a preference without touching the page
window.fchubMc.setCurrency('EUR'); // false if the store does not offer it
// Show a currency now: re-projects prices and repaints every currency surface.
// No request; the page already carries every rate it offers.
window.fchubMcApplyCurrency('EUR'); // false if the store does not offer it
// Apply a currency and tell the server, so the choice outlives this page
window.fchubMcSwitchCurrency('EUR');
// Re-initialise all switcher widgets after dynamic content load
window.fchubMcInitSwitchers();
// Re-project prices on a specific DOM subtree
window.fchubMcProjectPrices(document.getElementById('my-container'));fchubMcApplyCurrency is the whole switch. fchubMcSwitchCurrency adds one
fire-and-forget POST /context so the preference reaches the places the browser
cannot — order metadata, emails, CRM. Nothing waits on it.
JavaScript Events
| Event | Target | Description |
|---|---|---|
fchub_mc:context_changed | window | Fired once the new currency is on screen. Carries currency; there is no reload to fire before |
fchub_mc:context_switch_failed | window | Fired when the preference was not confirmed; includes currency, message, HTTP status and stable code |
fchub_mc:prices_projected | document | Fired after each projection pass |
Currency Switcher Widget
Block Family
The plugin now exposes a small set of currency-related blocks:
fchub-multi-currency/switcherfchub-multi-currency/current-currencyfchub-multi-currency/exchange-ratefchub-multi-currency/context-noticefchub-multi-currency/selector-buttons
CSS Custom Properties
Override these on .fchub-mc-switcher to theme the widget:
.fchub-mc-switcher {
--fchub-mc-radius: 6px;
--fchub-mc-border-color: #d0d5dd;
--fchub-mc-bg: #fff;
--fchub-mc-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
--fchub-mc-transition: 150ms ease;
--fchub-mc-badge-ok: #12b76a;
--fchub-mc-badge-stale: #f04438;
}ARIA Roles
The widget implements the combobox/listbox pattern:
- Trigger:
role="combobox"witharia-expanded,aria-haspopup="listbox",aria-controls - Option list:
role="listbox"witharia-label="Select currency" - Each option:
role="option"witharia-selectedon the active currency aria-activedescendanttracks the currently focused option- Unique ARIA IDs per instance for multiple widgets on one page
JavaScript Configuration Object
The fchubMcConfig object is emitted as typed JSON inline in the document head
and is available globally:
| Key | Type | Description |
|---|---|---|
currencyTable | object | Every selectable currency, keyed by code. Each entry carries rate, symbol, displayCurrencyName, decimals, position, displayDecSep, displayThousandSep, disclosureText, flag and rateBadge. Currencies without a usable rate are absent |
presentationTemplates | object | The translated sentences the browser fills to render a currency surface, including the one error a visitor can see |
baseCurrency | string | Base currency code. A table key equal to this needs no conversion |
defaultCurrency | string | Configured default display currency, if any |
accountCurrency | string | The signed-in visitor's saved preference. Empty for guests, and only trustworthy when isLoggedIn is true |
roundingMode | string | Rounding mode slug |
restUrl | string | REST API base URL |
nonce | string | WP REST nonce. Empty for guests: a nonce is per visitor, and a cached page would hand one visitor's to everybody after them |
cookieName | string | Cookie and local-storage key, currently fchub_mc_currency |
cookiePersistenceEnabled | bool | Whether guest browser persistence is enabled |
cookieLifetimeDays | int | Shared cookie and local-storage lifetime |
accountPersistenceEnabled | bool | Whether signed-in preferences use user meta |
isLoggedIn | bool | Whether the rendered request belongs to an authenticated visitor |
urlParamEnabled | bool | Whether storefront links may resolve a currency query parameter |
urlParamKey | string | Configured storefront currency query key |
baseCurrencySign | string | Base currency symbol |
baseCurrencyPosition | string | Base currency symbol position |
baseCurrencyCode | string | Base currency ISO code |
baseDecimalSep | string | Base currency decimal separator |
baseThousandSep | string | Base currency thousand separator |
baseDecimals | int | Base currency decimal places |
disclosureEnabled | bool | Whether checkout disclosure is active |
disclosureText | string|null | Rendered disclosure text with tokens replaced |
Currency on Cached Pages
Every storefront document this plugin renders is byte-identical for every visitor. Prices are base-currency markup, the switcher names the store base, and the configuration carries the whole currency table rather than one resolved choice. A shared cache can therefore serve the same bytes to anyone, and there is nothing per-visitor in them to be wrong.
The visitor's currency is decided in their own browser, by an inline block in the document head, before anything paints:
?currency=<code>, for the page it was followed to only. A shared link shows the recipient that currency without overwriting the preference they already had.- The local-storage record.
- The signed-in account preference, believed only on a page that cannot have been cached for somebody else.
- The
fchub_mc_currencycookie. - The configured default, then the store base.
Switching applies the choice immediately and posts it afterwards. A failed post means the preference will not outlive the session; it does not mean the visitor is looking at the wrong prices, so nothing is rolled back.
The local-storage record contains the currency code and an expiry timestamp. Legacy plain-code values are migrated, while expired, malformed and no-longer-configured currencies are removed. Disabling cookie persistence removes the record as well; it is not a back door around the setting.
Currencies without a usable exchange rate are absent from the table, so the switcher cannot offer a currency it would then fail to show.
Feature Flags
Internal flags stored under the fchub_mc_feature_flags option. Not exposed in the settings UI — visible in the Diagnostics tab.
| Flag | Default | Controls |
|---|---|---|
js_projection | true | Whether currency-projection.js is enqueued. Disable to turn off client-side price rewriting. |
geo_resolver | false | Whether the geolocation resolver is included in the chain. Not yet production-ready. |
Order Metadata
Written by SaveOrderSnapshotAction on fluent_cart/order_paid_done. Only written when the display currency differs from base.
| Meta Key | Type | Description |
|---|---|---|
_fchub_mc_display_currency | string | ISO 4217 display currency code at time of order |
_fchub_mc_base_currency | string | ISO 4217 base currency code |
_fchub_mc_rate | string | Exchange rate (8 decimal places) |
_fchub_mc_disclosure_version | string | Plugin version string |
Database Schema
fchub_mc_rate_history
| Column | Type | Notes |
|---|---|---|
id | BIGINT UNSIGNED AUTO_INCREMENT | Primary key |
base_currency | CHAR(3) | ISO 4217 |
quote_currency | CHAR(3) | ISO 4217 |
rate | DECIMAL(18,8) | Exchange rate |
provider | VARCHAR(64) | Provider slug, default manual |
fetched_at | DATETIME | WordPress site timezone |
Indexes: base_quote_fetched (base_currency, quote_currency, fetched_at), fetched_at
Pruned automatically: rows older than 90 days are deleted by the daily
maintenance cron (fchub_mc_daily_maintenance), which runs on every store —
the refresh cron only exists when a remote rate provider is configured.
fchub_mc_event_log
| Column | Type | Notes |
|---|---|---|
id | BIGINT UNSIGNED AUTO_INCREMENT | Primary key |
event | VARCHAR(128) | Event name |
user_id | BIGINT UNSIGNED NULL | WordPress user ID |
ip_hash | VARCHAR(64) NULL | Hashed IP address |
payload | LONGTEXT NULL | JSON payload |
created_at | DATETIME | Timestamp |
Indexes: event_created (event, created_at), created_at, user_id
Used by the GDPR personal data exporter/eraser. Pruned automatically: rows older than 90 days are deleted by the daily maintenance cron.
GDPR / Privacy
The plugin registers with WordPress's privacy tools:
Personal Data Exporter
Exports for a given email address:
- User meta
_fchub_mc_currency(preferred currency preference) - All
fchub_mc_event_logrows for the user (event name, timestamp; paginated at 50 per page)
Personal Data Eraser
Deletes for a given email address:
_fchub_mc_currencyuser meta- All
fchub_mc_event_logrows for the user
Browser cookie and local-storage values are held by the visitor's browser rather than WordPress, so the server-side eraser cannot reach them. They expire on the configured cookie lifetime and can be removed immediately by clearing this site's browser data or disabling cookie persistence and loading the storefront once.
Constants Reference
All constants are defined in FChubMultiCurrency\Support\Constants:
| Constant | Value | Usage |
|---|---|---|
REST_NAMESPACE | fchub-mc/v1 | REST API namespace |
OPTION_SETTINGS | fchub_mc_settings | Main settings option key |
OPTION_DB_VERSION | fchub_mc_db_version | DB migration tracking |
OPTION_FEATURE_FLAGS | fchub_mc_feature_flags | Feature flag storage |
HOOK_PREFIX | fchub_mc/ | Internal action/filter prefix |
COOKIE_KEY | fchub_mc_currency | Browser cookie name |
COOKIE_DAYS | 90 | Default cookie lifetime |
USER_META_KEY | _fchub_mc_currency | WP user meta key |
CHECKOUT_CURRENCY_FIELD | _fchub_mc_display_currency | Validated display choice posted with FluentCart checkout |
TABLE_RATE_HISTORY | fchub_mc_rate_history | Rate history table (without prefix) |
TABLE_EVENT_LOG | fchub_mc_event_log | Event log table (without prefix) |
FC_ADDON_SLUG | fchub-multi-currency | FluentCart addon registration slug |
CRON_REFRESH_RATES | fchub_mc_refresh_rates | WP cron event name |
Wishlist Integration
FCHub Wishlist prices convert automatically when a visitor switches currency. Zero setup, zero configuration.
Troubleshooting
Common issues with FCHub Multi-Currency and how to resolve them. Covers price projection problems, exchange rate failures, switcher issues, checkout disclosure, and FluentCRM sync.