FCHubFCHub.co

Exchange Rates

How FCHub Multi-Currency fetches, stores, caches, and manages exchange rates from multiple providers with automatic refresh and staleness detection.

Exchange rates are the engine behind display-layer multi-currency. The plugin fetches rates from an external provider, stores them with full history, caches the latest values, and automatically detects when rates go stale.

Rate Providers

Four providers are available. You choose one in the settings — the plugin fetches from that provider exclusively.

Slug: exchange_rate_api

The default provider. Reliable, wide currency coverage, and a free tier that's generous enough for most stores.

  • API Key: Required — get one at exchangerate-api.com
  • Endpoint: https://v6.exchangerate-api.com/v6/{key}/latest/{base}
  • Base currency: Uses your configured base currency directly

Refresh Cycle

Remote-provider refreshes run on a WordPress cron schedule. Manual rates use their explicit save action instead.

Cron Fires

The fchub_mc_refresh_rates event fires at the configured interval (default: every 6 hours). Changing the interval in settings reschedules the cron immediately.

Lock Acquired

A 120-second option-based lock prevents concurrent refreshes. If another refresh is already running, the new one exits silently.

Provider Fetched

The configured provider's fetchRates() method is called with the base currency. The provider returns an associative array of code => rate pairs.

Validation

The configured display-currency set is the contract. Extra provider rates are ignored, but every configured quote must be present, numeric, and greater than zero. One missing or invalid quote rejects the whole snapshot — because a mixed-age rate table is not a successful refresh wearing a convincing hat.

Storage

The complete snapshot is inserted into fchub_mc_rate_history with one multi-row write and one timestamp. Only after that write succeeds are all corresponding entries replaced in the WordPress object cache (group fchub_mc_rates, 1-hour TTL). A failed write leaves the previous cache untouched.

Pruning

Rate history rows older than 90 days are deleted. This keeps the table from growing unboundedly.

Hook Fired

fchub_mc/rates_refreshed action fires with the base currency and rate count.

You can also trigger an immediate remote refresh from the admin: FluentCart > Settings > Multi-Currency > Exchange Rates > Refresh Now. This calls the same action as the cron job. Manual mode uses Save Manual Rates instead; the generic refresh endpoint rejects it rather than laundering an old rate through a shiny new timestamp.

Storage Architecture

Rates live in three layers:

1. WordPress Object Cache (Hot)

The latest rate per currency pair is cached in WordPress object cache with group fchub_mc_rates and a 1-hour TTL. This is the first thing the plugin checks when it needs a rate.

On hosts with persistent object cache (Redis, Memcached), this means rate lookups are sub-millisecond. Without persistent cache, the cache only lasts for the current PHP request — but the database fallback is still fast.

2. Database Table (Warm)

The fchub_mc_rate_history table stores every rate ever fetched. When the cache misses, the plugin queries the most recent row for the requested currency pair.

ColumnTypeDescription
idBIGINT UNSIGNEDAuto-increment primary key
base_currencyCHAR(3)ISO 4217 base code
quote_currencyCHAR(3)ISO 4217 quote code
rateDECIMAL(18,8)Exchange rate (8 decimal places)
providerVARCHAR(64)Provider slug that fetched this rate
fetched_atDATETIMETimestamp in WordPress site timezone

Indexed on (base_currency, quote_currency, fetched_at) for efficient latest-rate lookups.

3. Synthetic Identity Rate

When the display currency equals the base currency, a synthetic 1.00000000 rate is returned. No database query, no cache lookup.

Staleness Detection

A rate is considered stale when its fetched_at timestamp is older than the configured stale threshold (default: 24 hours). The comparison uses WordPress's site timezone (current_time('timestamp')) to avoid UTC offset mismatches.

When staleness is detected:

BehaviourWhere
Admin warning noticeWordPress dashboard (for manage_options users)
Red dot on rate badgeCurrency switcher dropdown footer
Stale fallback appliesPrice display (base currency or last known rate)

Stale Fallback Options

SettingBehaviour
baseDisplay reverts to the base currency. The visitor sees all prices in the base currency until fresh rates arrive.
last_knownKeep using the stale rate. Prices continue to display in the selected currency, but the rate badge warns it's outdated.

Rate History and Pruning

Every rate refresh inserts new rows into the history table — existing rows are never updated. This gives you a complete audit trail of rate changes over time.

The cron job automatically deletes rows older than 90 days after each refresh. This is a hard-coded retention period designed to balance history depth with table size.

The RateHistoryQuery::forPair() method returns the last 30 rows for any currency pair, ordered newest first. This is used internally but also available for custom integrations.

Rounding

After the exchange rate multiplication, the resulting amount is rounded according to your settings. These examples use a two-decimal display currency:

ModeBehaviourExample (input: 12.345)
NoneTruncate (no rounding)12.34
Half UpStandard rounding12.35
Half DownRound half towards zero12.34
CeilAlways round up12.35
FloorAlways round down12.34

The display currency's Decimals value controls the granularity:

DecimalsRounds ToExample (input: 12.3456, Half Up)
0Nearest whole unit12
2Nearest hundredth12.35
3Nearest thousandth12.346

Rounding is applied identically in both the PHP fchub_mc_format_price() function and the JavaScript projection engine.

On this page