WPCS - WordPress Currency Switcher Professional

Add markup to exchange rates in WordPress, or keep a fixed manual rate

Converted prices on a WordPress site can differ from Google or XE by a few percent, and many site owners want a difference on purpose: they add markup to the exchange rate to cover conversion fees, or keep a fixed rate such as 1 EUR = 1 CHF that automatic updates must not overwrite. In WPCS – WordPress Currency Switcher all three come down to the same question: where the rate comes from and when it changes. This article explains that, then shows how to add a margin per currency without code, a percentage markup in code, and fixed manual rates.

Why converted prices differ from Google or XE

WPCS does not calculate exchange rates. For every currency it asks the service selected in Currency aggregator (Options tab of Settings → Currency Switcher) for the rate against your base currency, stores that number in the currency row, and multiplies your prices by it: [wpcs_price value=100] shows 100 × the rate. A difference from Google or XE has one of these causes:

  • A different source. Google and XE show live market rates. Central bank aggregators (the ECB, Narodowy Bank Polski, the Romanian, Hungarian, Moldovan and Ukrainian national banks) publish one official reference rate per working day. Commercial services (Open exchange rates, Currencylayer, Fixer, Currencyapi) refresh at an interval that depends on your plan. For major currencies the sources agree closely; for currencies such as KES, GHS or NGN they can differ by several percent.
  • An old rate. The site uses the rate stored at the last update. Rate auto update on the Options tab refreshes it hourly, twice a day (the default), daily, weekly or monthly, through WP-Cron. WP-Cron runs only when someone visits the site, so on a quiet site an update can come late.
  • Your own changes. A margin in the interest field or in code (both below), a rate typed by hand, or rounding of the converted price.

To get closer to market rates, switch to a commercial aggregator: choose it in Currency aggregator, paste the key from that service into Aggregator API key, save, and press Update all rates on the Currencies tab followed by Save options. Error messages and API request limits are explained in exchange rate errors. You can also plug in any other rate source as a custom aggregator.

Check the rate your site really uses

The rate field on the Currencies tab shows the stored rate from the aggregator. The front end uses that rate plus any margin. To see the final rate, put this on a draft page:

[wpcs_code_rate code=GBP]
[wpcs_price value=100]

The first line prints the rate of GBP that prices are converted with, interest and code changes included; the second must equal 100 times that rate when GBP is selected. Compare the first line with your source, not the converted price with a converter website: that comparison mixes rate, rounding and decimals.

Add markup to the exchange rate without code: the interest field

Each currency row has a second field after the rate, with a plus sign between them and the placeholder interest. On the front end the plugin uses rate + interest. The interest is an amount added to the rate, not a percentage:

Base USDRateInterestRate usedMarkup
EUR0.860.02580.88583%
JPY151.304.539155.8393%
INR88.20-0.88287.318-1%

To get a percentage, multiply the current rate by it (3% of 0.86 is 0.0258) and enter the result. Because the amount stays the same while the rate moves, the percentage drifts a little over time; recalculate it now and then, or use the code below. A negative interest lowers the rate. The settings page always shows the rate without the interest, so the aggregator can keep updating the rate while your margin stays separate. The converter, the rates table and the price info icon use the same rate plus interest as your prices.

Use the interest field only for currencies whose rate updates without errors. When an automatic update gets no rate for a currency, that currency keeps the rate the site is using at that moment, which already contains the interest, and it is saved as the new stored rate; every failed update adds the margin again. Check those rows after an aggregator error, or use the code version, which does not have this effect. For the same reason, after Update all rates always press Save options before leaving the page.

A percentage markup in code

The wpcs_currency_data_manipulation filter changes the currency data every time the plugin reads it, so a percentage stays a percentage whatever the rate does. The plugin also reads that data on its settings page and when it updates rates, and writes it back. A markup added there would be saved into the stored rate and then added again, so the snippet skips those moments:

/*
 * WPCS: add a percentage markup to exchange rates on the front end.
 * Child theme functions.php or a code snippets plugin.
 */
add_filter('wpcs_currency_data_manipulation', function ($currencies) {
    // Keep stored rates clean: no markup on the plugin settings page,
    // during automatic rate updates and in the "Update all rates" request.
    if ((is_admin() && !wp_doing_ajax())
        || wp_doing_cron()
        || (isset($_REQUEST['action']) && $_REQUEST['action'] === 'wpcs_save_etalon')) {
        return $currencies;
    }

    $markup = array(
        'EUR' => 3,    // +3 %
        'GBP' => 2.5,  // +2.5 %
    );

    foreach ($markup as $code => $percent) {
        if (isset($currencies[$code]['rate'])) {
            $currencies[$code]['rate'] = (float) $currencies[$code]['rate'] * (1 + $percent / 100);
        }
    }

    return $currencies;
});

Do not list the base currency. The filter runs after the interest field is added, so use one or the other for a currency, not both. The short example in the FAQ on setting rates manually has no such check: on its own it adds the markup again each time the settings are saved.

Keep a fixed manual rate, such as 1:1

Some sites want the same number in two currencies (a book at 20 EUR and 20 CHF), or a rate agreed with a partner. The rate field accepts any number you type; the work is to stop the plugin from replacing it.

All rates fixed by hand

  1. On the Currencies tab, type the rate into the row, for example 1 for CHF when the base currency is EUR.
  2. On the Options tab, set Rate auto update to no auto update.
  3. Press Save options.

From now on rates change only when you change them. Avoid Update all rates and the update button in the row: they fetch fresh rates from the aggregator into the form, and saving would replace your fixed rate. If you press one by mistake, retype the rate before saving.

One fixed rate while the others update

Rate auto update works for all currencies at once; there is no per-currency lock. To keep CHF at 1 while the other currencies follow the market, pin it in code. The aggregator may keep updating the stored CHF rate, but the site always uses 1:

/*
 * WPCS: always use 1 EUR = 1 CHF (the base currency is EUR).
 * Child theme functions.php or a code snippets plugin.
 */
add_filter('wpcs_currency_data_manipulation', function ($currencies) {
    if (isset($currencies['CHF'])) {
        $currencies['CHF']['rate'] = 1;
    }
    return $currencies;
});

A pinned rate gives the same result every time, so this snippet needs no admin check. It overrides the interest field of that currency. If you want your own amount per price rather than one rate, for example 20 EUR but 19 CHF, use fixed prices per currency instead.

When the update interval does not change

The plugin schedules its update job once, with the interval selected at that time. Choosing another interval later does not replace the existing schedule, so the old interval keeps running. To apply the new one, delete the scheduled event wpcs_currencies_rate_auto_update (with a cron manager plugin, or with WP-CLI: wp cron event delete wpcs_currencies_rate_auto_update); the plugin schedules it again with the current setting on the next page view. If rates never update at all, read why the rate auto update is not working.

Checklist

  • [wpcs_code_rate code=XXX] shows the rate you expect, and [wpcs_price value=100] equals 100 times it.
  • The aggregator and its API key fit the accuracy you need.
  • Each currency has a margin in the interest field or in code, never both, and the base currency has none.
  • Fixed rates: Rate auto update is no auto update, or the rate is pinned in code.
  • Page caches are cleared after changing rates; cached pages show the old prices until then.

If the rate used still differs from the source by more than you can explain, post the aggregator, the currency and the output of [wpcs_code_rate] on the WPCS support forum.