FCHubFCHub.co

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)

HookArgumentsDescription
fchub_mc/context_switchedstring $currencyCode, int $userIdFired when a user selects a new currency. $userId is 0 for guests.
fchub_mc/rates_refreshedstring $baseCurrency, int $countFired after a successful rate refresh.
fchub_mc/community_currency_updatedstring $currencyCode, int $userIdFired after FluentCommunity user meta is written.

Filters (fired by the plugin)

HookArgumentsDescription
fchub_mc/modulesarray $modulesModify the list of modules before registration. Add or remove module classes.
fchub_mc/contextCurrencyContext $contextOverride the resolved currency context. Runs after the resolver chain.

Hooks consumed (FluentCart)

HookTypeDescription
fluent_cart/integration/addonsfilterRegisters the plugin as a FluentCart addon
fluent_cart/before_payment_methodsactionAdds the validated display-currency field inside regular and modal checkout forms
fluent_cart/checkout/prepare_other_dataactionCaptures that checkout field before the order is finalised
fluent_cart/order_paid_doneactionTriggers order snapshot and FluentCRM sync
fluent_cart/store_settings/valuesfilterAdds multi-currency fields to store settings API
fluent_cart/loading_appactionEnqueues 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

EventTargetDescription
fchub_mc:context_changedwindowFired once the new currency is on screen. Carries currency; there is no reload to fire before
fchub_mc:context_switch_failedwindowFired when the preference was not confirmed; includes currency, message, HTTP status and stable code
fchub_mc:prices_projecteddocumentFired after each projection pass

Currency Switcher Widget

Block Family

The plugin now exposes a small set of currency-related blocks:

  • fchub-multi-currency/switcher
  • fchub-multi-currency/current-currency
  • fchub-multi-currency/exchange-rate
  • fchub-multi-currency/context-notice
  • fchub-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" with aria-expanded, aria-haspopup="listbox", aria-controls
  • Option list: role="listbox" with aria-label="Select currency"
  • Each option: role="option" with aria-selected on the active currency
  • aria-activedescendant tracks 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:

KeyTypeDescription
currencyTableobjectEvery 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
presentationTemplatesobjectThe translated sentences the browser fills to render a currency surface, including the one error a visitor can see
baseCurrencystringBase currency code. A table key equal to this needs no conversion
defaultCurrencystringConfigured default display currency, if any
accountCurrencystringThe signed-in visitor's saved preference. Empty for guests, and only trustworthy when isLoggedIn is true
roundingModestringRounding mode slug
restUrlstringREST API base URL
noncestringWP REST nonce. Empty for guests: a nonce is per visitor, and a cached page would hand one visitor's to everybody after them
cookieNamestringCookie and local-storage key, currently fchub_mc_currency
cookiePersistenceEnabledboolWhether guest browser persistence is enabled
cookieLifetimeDaysintShared cookie and local-storage lifetime
accountPersistenceEnabledboolWhether signed-in preferences use user meta
isLoggedInboolWhether the rendered request belongs to an authenticated visitor
urlParamEnabledboolWhether storefront links may resolve a currency query parameter
urlParamKeystringConfigured storefront currency query key
baseCurrencySignstringBase currency symbol
baseCurrencyPositionstringBase currency symbol position
baseCurrencyCodestringBase currency ISO code
baseDecimalSepstringBase currency decimal separator
baseThousandSepstringBase currency thousand separator
baseDecimalsintBase currency decimal places
disclosureEnabledboolWhether checkout disclosure is active
disclosureTextstring|nullRendered 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:

  1. ?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.
  2. The local-storage record.
  3. The signed-in account preference, believed only on a page that cannot have been cached for somebody else.
  4. The fchub_mc_currency cookie.
  5. 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.

FlagDefaultControls
js_projectiontrueWhether currency-projection.js is enqueued. Disable to turn off client-side price rewriting.
geo_resolverfalseWhether 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 KeyTypeDescription
_fchub_mc_display_currencystringISO 4217 display currency code at time of order
_fchub_mc_base_currencystringISO 4217 base currency code
_fchub_mc_ratestringExchange rate (8 decimal places)
_fchub_mc_disclosure_versionstringPlugin version string

Database Schema

fchub_mc_rate_history

ColumnTypeNotes
idBIGINT UNSIGNED AUTO_INCREMENTPrimary key
base_currencyCHAR(3)ISO 4217
quote_currencyCHAR(3)ISO 4217
rateDECIMAL(18,8)Exchange rate
providerVARCHAR(64)Provider slug, default manual
fetched_atDATETIMEWordPress 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

ColumnTypeNotes
idBIGINT UNSIGNED AUTO_INCREMENTPrimary key
eventVARCHAR(128)Event name
user_idBIGINT UNSIGNED NULLWordPress user ID
ip_hashVARCHAR(64) NULLHashed IP address
payloadLONGTEXT NULLJSON payload
created_atDATETIMETimestamp

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_log rows for the user (event name, timestamp; paginated at 50 per page)

Personal Data Eraser

Deletes for a given email address:

  • _fchub_mc_currency user meta
  • All fchub_mc_event_log rows 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:

ConstantValueUsage
REST_NAMESPACEfchub-mc/v1REST API namespace
OPTION_SETTINGSfchub_mc_settingsMain settings option key
OPTION_DB_VERSIONfchub_mc_db_versionDB migration tracking
OPTION_FEATURE_FLAGSfchub_mc_feature_flagsFeature flag storage
HOOK_PREFIXfchub_mc/Internal action/filter prefix
COOKIE_KEYfchub_mc_currencyBrowser cookie name
COOKIE_DAYS90Default cookie lifetime
USER_META_KEY_fchub_mc_currencyWP user meta key
CHECKOUT_CURRENCY_FIELD_fchub_mc_display_currencyValidated display choice posted with FluentCart checkout
TABLE_RATE_HISTORYfchub_mc_rate_historyRate history table (without prefix)
TABLE_EVENT_LOGfchub_mc_event_logEvent log table (without prefix)
FC_ADDON_SLUGfchub-multi-currencyFluentCart addon registration slug
CRON_REFRESH_RATESfchub_mc_refresh_ratesWP cron event name

On this page