less than a minute read • Updated 9 minutes ago
Update product totals with multicurrency support before add to cart
How to update product prices as options change, switch template sets, and convert prices to other currencies before add to cart.
For stores looking to support multiple different currencies, it's important to be able to show product prices in those different currencies. This helps customers to be able to know exactly what they'll be paying - rather than needing to do those calculations themselves.
Not all website builders and CMS's support setting multiple prices for a single product though, so you can be limited to just a single currency that you can display your prices in.
The following snippet will allow you to add the following functionality to your website:
Dynamically set the template set for the cart on initial page load, based on the location of the customer, which will set the locale, language and currency of the cart.
Convert the prices and price modifiers in all product add to cart forms to the cart currency
Convert all prices displayed to customers to the cart currency
Display a dynamic product total, taking into consideration a change in quantity or product options that include a price modifier.
Step 1: Include snippet
Paste the following code before the closing </body> tag of any page that you want to add this functionality to. It can also be included in an existing javascript file on your page if you prefer. This snippet relies on the loader.js file also being included on your page.
Tip: This is not required on the cart, checkout or receipt pages, so it should not be placed in those templates or in your store's admin settings. Just on your own website.
<script>
/* Foxy Dynamic Product Prices and Currency Conversion v0.8, https://foxy.io */
!function(t,e){var n=function(t){return new i(t)};n.version="0.1.3";var a=t.fxSetup||{rates:{},base:""};n.rates=a.rates,n.base=a.base,n.settings={from:a.from||n.base,to:a.to||n.base};var r=n.convert=function(t,e){if("object"==typeof t&&t.length){for(var a=0;a<t.length;a++)t[a]=r(t[a],e);return t}(e=e||{}).from||(e.from=n.settings.from),e.to||(e.to=n.settings.to);a=e.to;var i=e.from,s=n.rates;if(s[n.base]=1,!s[a]||!s[i])throw"fx error";return t*(a=i===n.base?s[a]:a===n.base?1/s[i]:s[a]*(1/s[i]))},i=function(t){"string"==typeof t?(this._v=parseFloat(t.replace(/[^0-9-.]/g,"")),this._fx=t.replace(/([^A-Za-z])/g,"")):this._v=t};(a=n.prototype=i.prototype).convert=function(){var t=Array.prototype.slice.call(arguments);return t.unshift(this._v),r.apply(n,t)},a.from=function(t){return(t=n(r(this._v,{from:t,to:n.base})))._fx=n.base,t},a.to=function(t){return r(this._v,{from:this._fx?this._fx:n.settings.from,to:t})},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=n),exports.fx=fx):"function"==typeof define&&define.amd?define([],(function(){return n})):(n.noConflict=function(e){return function(){return t.fx=e,n.noConflict=undefined,n}}(t.fx),t.fx=n)}(this),function(t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).numberCurrencyFormat=t()}((function(){return function t(e,n,a){function r(s,o){if(!n[s]){if(!e[s]){var c="function"==typeof require&&require;if(!o&&c)return c(s,!0);if(i)return i(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var f=n[s]={exports:{}};e[s][0].call(f.exports,(function(t){return r(e[s][1][t]||t)}),f,f.exports,t,e,n,a)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<a.length;s++)r(a[s]);return r}({1:[function(t,e,n){"use strict";const a="LEFT",r="ALWAYS",i="NEVER",s="IF_NEEDED",o="AS_IS",c={currency:"",showDecimals:r,thousandSeparator:",",decimalSeparator:".",currencyPosition:"RIGHT",decimalsDigits:2,spacing:!0,arithmeticalRounding:!1};function u(t,e){return void 0!==t?t:e}function f(t){return t?{currency:t.currency||c.currency,thousandSeparator:u(t.thousandSeparator,c.thousandSeparator),decimalSeparator:u(t.decimalSeparator,c.decimalSeparator),showDecimals:t.showDecimals||c.showDecimals,decimalsDigits:u(t.decimalsDigits,c.decimalsDigits),currencyPosition:t.currencyPosition||c.currencyPosition,spacing:u(t.spacing,c.spacing),arithmeticalRounding:u(t.arithmeticalRounding,c.arithmeticalRounding)}:c}function l(t){return/^[\.,]$/.test(t)}e.exports={format:function(t,e){const n=f(e),c=n.showDecimals===o?t.toString():parseFloat((n.arithmeticalRounding?function(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)}(t,n.decimalsDigits):+t).toString()).toFixed(n.showDecimals===i?0:n.decimalsDigits);if(isNaN(c))return t;const u=c.split("."),l=u[0],d=u[1]||"",p=n.showDecimals===r||n.showDecimals===s&&+d>0||n.showDecimals===o&&d.length>0?n.decimalSeparator+d:"",h=(m=l,g=n.thousandSeparator,m.split("").reduce((function(t,e,n){return t+e+(n<m.length-1&&(m.length-n-1)%3==0&&"-"!==e?g:"")}),"")+p);var m,g;if(n.currency){const t=n.spacing?" ":"";return n.currencyPosition===a?n.currency+t+h:h+t+n.currency}return h},unformat:function(t,e){const n=f(e),a=e&&e.decimalSeparator,r=t.replace(/\s/g,"").match(/[0-9]+|[^0-9]+/gi).map((function(t,e){return{value:t,isNumber:/[0-9]+/.test(t),index:e}})),i=r.filter((function(t){return t.isNumber})),s=r.filter((function(t){return!t.isNumber})),o=i[0],c=i[i.length-1],u=o.value>0?o:i.length>1&&i[1].value>0?i[1]:void 0;if(!u)return 0;const d=u.index>0?r[u.index-1]:void 0,p=d&&1===s.length&&l(d.value),h=c.index-1,m=h>=0&&r[h],g=s.filter((function(t){return t.index>o.index&&t.index<h})).map((function(t){return t.value})),_=g.length>0&&g.indexOf(m.value)<0,y=m&&m.value===a,v=c.value.length===n.decimalsDigits&&m&&l(m.value),x=(a?y:v||y||p||_)?c.value.length:0,b=d&&d.value,j=b&&"-"===b[b.length-1],w=i.map((function(t){return t.value})).join("");return parseFloat(w/Math.pow(10,x))*(j?-1:1)}}},{}]},{},[1])(1)}));var FC=FC||{};!function(t,e){var n={version:.8},r=/([{\|])p([+\-:])([\d\.]+)(?:\D{3})?([\|}])/,i=/^(\d+):/,s=!1,o={base:"USD",sets:[],max_rate_cache:12,rounding:"minimal",convert_display_only:!1,active_class:"active",use_openexchangerates:!1,adjust_price_display:null};function c(){for(var t=e.json.shipping_address.country,n=""==e.json.template_set?"DEFAULT":e.json.template_set,a="DEFAULT",r=0;r<settings.sets.length;r++)settings.sets[r].hasOwnProperty("countries")&&settings.sets[r].countries.indexOf(t)>-1&&(a=settings.sets[r].template_set);F()||(O(),a==n)?(m(),u()):h(a)}function u(n){var a=settings.sets.map((function(t,e){return t.currency})).filter((function(t,e,n){return n.indexOf(t)===e})),r=(n=void 0!==n&&n,F());r.hasOwnProperty("rates")&&(Date.now()-r.rates.timestamp)/60<36e4*parseFloat(settings.max_rate_cache)&&(n||r.fcsid==e.json.session_id)?(fx.rates=r.rates.rates,fx.base=r.rates.base,d()):n||(settings.use_openexchangerates?t.getJSON("https://openexchangerates.org/api/latest.json?app_id="+settings.use_openexchangerates+"&prettyprint=false&symbols="+a.join(","),(function(t){l(t.rates,t.base),d()})):t.getJSON("https://www.floatrates.com/daily/"+settings.base+".json",(function(t){for(var e={},n=0;n<a.length;n++)(t.hasOwnProperty(a[n].toLowerCase())||a[n]==settings.base)&&(e[a[n]]=a[n]==settings.base?1:t[a[n].toLowerCase()].rate);l(e,settings.base),d()})))}function l(t,e){fx.rates=t,fx.base=e,O({rates:t,base:e,timestamp:Date.now()})}function d(){var n=F();if(!n)return console.log("FC.currency: Unable to find locale information"),!1;var a=D(),i=e.json.item_count>0?e.json.locale_info.int_curr_symbol.trim():n.locale.int_curr_symbol.trim(),s=e.json.item_count>0?e.json.locale_info.mon_decimal_point:n.locale.mon_decimal_point;t(".fc-currency-price").each((function(){var a,r=settings.base;t(this).data("fc-currency")&&(r=t(this).data("fc-currency"),a=s);var o=S(t(this).html(),a),c=o;r!=i&&(c=g(c=fx.convert(o,{from:r,to:i}))),t(this).html(e.util.money_format(n.currency_format,_(c)).trim()).data("fc-currency",i).show()})),t('form[action*="'+a+'"]').each((function(){var e=t(this).find('[name="price"], [name$=":price"]');if(0==e.length)return!1;var n=settings.base;if(t(this).data("fc-currency"))n=t(this).data("fc-currency");else{var a=/([\d\.]+)([A-Z]{3})$/i,s=e.first().val().replace(" ","");s.match(a);a.test(s)&&(n=a.exec(s)[2].toUpperCase())}n!=i&&(t(this).find("[value*='{p'], [value*='|p']").each((function(){var t=this.value.match(r);if(t){var e=fx.convert(parseFloat(t[3]),{from:n,to:i});e=g(e),settings.convert_display_only||(this.value=this.value.replace(r,"$1p$2"+e.toFixed(2)+i.toLowerCase()+"$4"))}})),e.each((function(){var t=S(this.value),e=fx.convert(t,{from:n,to:i});e=g(e),settings.convert_display_only||(this.value=e.toFixed(2)+i.toLowerCase())})),t(this).data("fc-currency",i)),y()}))}function h(t){e.client.request("https://"+e.json.config.store_domain+"/cart?template_set="+t).done((function(){O(),c()}))}function m(){for(var n=F(),a=!1,r=e.json.item_count>0?""==e.json.template_set?"DEFAULT":e.json.template_set:n.template_set,i=0;i<settings.sets.length;i++)settings.sets[i].template_set==r&&(a=r);a&&(t("[data-fc-template-set]."+settings.active_class).removeClass(settings.active_class),t('[data-fc-template-set="'+a+'"]').addClass(settings.active_class))}function g(t){var e=["full","half","minimal","none",!1].indexOf(settings.rounding)>-1?settings.rounding:o.rounding;if("full"==e)return Math.ceil(t);if("half"==e){var n=t%1;return Math.floor(t)+(0==n?0:n<=.5?.5:1)}return"minimal"==e?Math.ceil(20*t)/20:t}function _(t){return"function"==typeof settings.adjust_price_display&&(adjusted_price=settings.adjust_price_display(t),void 0!==adjusted_price)?parseFloat(adjusted_price):t}function y(){ADJUST={},t("input,select").off("change.foxy-dynamic-price"),t('form[action*="'+D()+'"]').each((function(){var e=t(this),n="",a={products:{}};t(this).find("[name='name'],[name^='name||'],[name$=':name'],[name*=':name||']").each((function(){var t=b(this.name),r=t?t+":":"",i=S(e.find("[name='"+r+"price'],[name^='"+r+"price||']").first().val()),s={id:t,code:"",base_price:i||0,quantity:1,attributes:{},has_quantity:!1},o=e.find("[name='"+r+"quantity'],[name^='"+r+"quantity||']"),c=e.find("[name='"+r+"code'],[name^='"+r+"code||']");if(c.length>0&&(s.code=v(c.first().val()),""===n&&(n=s.code)),o.length>0){var u=0,f=j(o);["select","text","hidden"].indexOf(f)>-1?(s.has_quantity=!0,u=parseFloat(v(o.val()))):["radio","checkbox"].indexOf(f)>-1&&(s.has_quantity=!0,1==o.filter(":checked").length&&(u=parseFloat(v(o.filter(":checked").val())))),isNaN(u)&&(u=0),s.quantity=u}a.products[t]=s}));e.attr("data-fc-form-code")&&(n=e.attr("data-fc-form-code")),""!==n&&(t(this).find("input,select").each((function(){var e=b(this.name),i=function(t){return x(t)[2]}(this.name),s=j(t(this));if("quantity"!=i)if("price"!=i||"hidden"==s){if("SELECT"==this.tagName){var o=!1;t(this).children("option").each((function(){this.value.search(r)>-1&&(o=!0)})),o&&(t(this).data("fc-adjust-for",n),a.products[e].attributes[v(this.name)]=v(this.value),t(this).on("change.foxy-dynamic-price",(function(){ADJUST[t(this).data("fc-adjust-for")].products[e].attributes[v(this.name)]=v(this.value),w()})))}else if(this.value.search(r)>-1)switch(t(this).data("fc-adjust-for",n),t(this).attr("type")){case"checkbox":t(this).is(":checked")?a.products[e].attributes[v(this.name)]=v(this.value):a.products[e].attributes[v(this.name)]="",t(this).on("change.foxy-dynamic-price",(function(){t(this).is(":checked")?ADJUST[t(this).data("fc-adjust-for")].products[e].attributes[v(this.name)]=v(this.value):ADJUST[t(this).data("fc-adjust-for")].products[e].attributes[v(this.name)]="",w()}));break;case"radio":a.products[e].attributes.hasOwnProperty(v(this.name))||(a.products[e].attributes[v(this.name)]=""),t(this).is(":checked")&&(a.products[e].attributes[v(this.name)]=v(this.value)),t("[name='"+this.name+"']").data("fc-adjust-for",n).on("change.foxy-dynamic-price",(function(){ADJUST[t(this).data("fc-adjust-for")].products[e].attributes[v(this.name)]=v(this.value),w()}))}}else t(this).data("fc-adjust-for",n).on("change.foxy-dynamic-price",(function(){var n=0;(["select","text"].indexOf(s)>-1||["radio","checkbox"].indexOf(s)>-1&&t(this).is(":checked"))&&(n=parseFloat(v(this.value))),isNaN(n)&&(n=0),ADJUST[t(this).data("fc-adjust-for")].products[e].base_price=n,w()}));else t(this).data("fc-adjust-for",n).on("change.foxy-dynamic-price",(function(){var n=0;(["select","text"].indexOf(s)>-1||["radio","checkbox"].indexOf(s)>-1&&t(this).is(":checked"))&&(n=parseFloat(v(this.value))),isNaN(n)&&(n=0),ADJUST[t(this).data("fc-adjust-for")].products[e].quantity=n,w()}))})),ADJUST[n]=a)})),n._ADJUST=ADJUST,w()}function v(t){return t.replace(/\|\|[\d\w]+(?:\|\|open)?$/,"")}function x(t){return(t=v(t)).match(/(?:(\d+):)?(.*)/)}function b(t){var e=x(t);return i.test(this.name)&&(prefix=parseInt(this.name.match(i)[0])),void 0===e[1]?0:parseInt(e[1])}function j(t){if("SELECT"==t[0].tagName)return"select";if("INPUT"==t[0].tagName)switch(t.attr("type").toLowerCase()){case"text":case"number":case"tel":return"text";default:return t.attr("type").toLowerCase()}}function w(){for(f in ADJUST){var n=0,i=0;for(p in ADJUST[f].products){var s=ADJUST[f].products[p].base_price,o=0;for(a in ADJUST[f].products[p].attributes){var c=ADJUST[f].products[p].attributes[a].match(r);if(c)switch(c[2]){case":":s=parseFloat(c[3]);break;case"+":o+=parseFloat(c[3]);break;case"-":o-=parseFloat(c[3])}}s+=o,n+=s*=ADJUST[f].products[p].quantity,i+=ADJUST[f].products[p].quantity}"function"==typeof fcFormatPrice&&(n=fcFormatPrice(n,f)),"function"==typeof fcFormatQuantity&&(i=fcFormatQuantity(i,f));var u=F(),l=e.util.money_format(u.currency_format,_(n)).trim();t("."+f+"_total, ."+f+"-total").html(l),t("."+f+"_total_quantity, ."+f+"-total-quantity").html(i)}}function D(){var t=F();return e.json.hasOwnProperty("config")?e.json.config.store_domain:t?t.store_domain:".foxycart.com"}function S(t,e){try{var n={};return e&&(n.decimalSeparator=e),numberCurrencyFormat.unformat(t,n)}catch(t){return!1}}function O(t){var n=F(),a=Object.assign({},n);n&&""==e.json.template_set&&void 0!==t||Object.assign(a,{fcsid:e.json.session_id,store_domain:e.json.config.store_domain,template_set:""==e.json.template_set?"DEFAULT":e.json.template_set,locale:e.json.locale_info,currency_format:e.json.config.currency_format}),"object"==typeof t&&Object.assign(a,{rates:t}),localStorage.setItem("fc_currency_config",JSON.stringify(a))}function F(){var t=JSON.parse(localStorage.getItem("fc_currency_config"));return!(null===t||e.hasOwnProperty("json")&&e.json.hasOwnProperty("session_id")&&t.fcsid!==e.json.session_id)&&t}e.hasOwnProperty("util")||(e.util={},e.util.money_format=function(t,n){if("number"!=typeof n)return null;var a=e.json.locale_info;return t.replace(/%((=.|[+^(!-])*?)(\d*?)(#(\d+))?(\.(\d+))?([in%])/g,(function(t,r,i,s,o,c,u,f,l){if(t=u="","%"===l)return"%";u=r&&/=./.test(r)?r.match(/=(.)/)[1]:" ",o=!r||-1===r.indexOf("!"),s=parseInt(s,10)||0,(i=0>n)&&(n=-n),f=""===f||void 0===f?"i"===l?a.int_frac_digits:a.frac_digits:parseInt(f,10);var d=(n=(n=e.util.round(n,f)).toFixed(f)).indexOf(".");f=-1!==d?n.slice(0,d):n,d=-1!==d?n.slice(d+1):"";var p=function(t,e,n){return(t=t.split("")).splice(e,0,n),t.join("")},h=f.length,m=h<(c=parseInt(c,10));if(m){var g=c-h;f=Array(g+1).join(u)+f}if(-1===r.indexOf("^")){if(c=a.mon_thousands_sep,(h=a.mon_grouping)[0]<f.length)for(var _=0,y=f.length;_<h.length&&!(0>=(y-=h[_]));_++)m&&y<g&&(c=u),f=p(f,y,c);if(0<h[_-1])for(;y>h[_-1];)y-=h[_-1],m&&y<g&&(c=u),f=p(f,y,c)}if(u=d?f+a.mon_decimal_point+d:f,g="",o&&(g="i"===l?a.int_curr_symbol:a.currency_symbol),l=i?a.n_sign_posn:a.p_sign_posn,_=i?a.n_sep_by_space:a.p_sep_by_space,y=i?a.n_cs_precedes:a.p_cs_precedes,-1!==r.indexOf("("))t=(y?g+(1===_?" ":""):"")+u+(y?"":(1===_?" ":"")+g),t=i?"("+t+")":" "+t+" ";else switch(c=a.positive_sign,o=a.negative_sign,f=i?o:c,d="",l&&(d=Array((i?c:o).length-f.length+1).join(" ")),l){case 0:t="("+(y?g+(1===_?" ":"")+u:u+(1===_?" ":"")+g)+")";break;case 1:t=d+f+(2===_?" ":"")+(y?g+(1===_?" ":"")+u:u+(1===_?" ":"")+g);break;case 2:t=(y?g+(1===_?" ":"")+u:u+(1===_?" ":"")+g)+(2===_?" ":"")+f+d;break;case 3:t=y?d+f+(2===_?" ":"")+g+(1===_?" ":"")+u:u+(1===_?" ":"")+f+d+(2===_?" ":"")+g;break;case 4:t=y?g+(2===_?" ":"")+d+f+(1===_?" ":"")+u:u+(1===_?" ":"")+g+(2===_?" ":"")+f+d}return 0<(s-=t.length)&&(s=Array(s+1).join(" "),t=-1!==r.indexOf("-")?t+s:s+t),t}))},e.util.round=function(t,e){var n=Math.pow(10,e);return 5<=10*t*n%10&&(t+=1/(10*n)),Math.round(t*n)/n}),n.update=function(){settings=e.hasOwnProperty("currency_config")?Object.assign({},o,e.currency_config):Object.assign({},o),s=e.hasOwnProperty("session")&&!0===e.session.initialized;var t=F();!t||e.hasOwnProperty("json")&&0!=e.json.item_count||(e.hasOwnProperty("json")||(e.json={}),e.json.currency_format=t.currency_format,e.json.locale_info=t.locale,e.json.item_count=0),(t&&!s||s)&&(settings.sets.length>1?s?c():(m(),u(!0)):(t||O(),y()))};var A="function"==typeof e.onLoad?e.onLoad:function(){};e.onLoad=function(){e.client.on("ready.done",n.update),A()},t((function(){n.update(),t("body").on("click","[data-fc-template-set]",(function(e){var n=function(t){for(var e=!1,n=0;n<settings.sets.length;n++)settings.sets[n].template_set==t&&(e=settings.sets[n]);return e}(t(this).attr("data-fc-template-set"));n&&(e.preventDefault(),s&&h(n.template_set))}))})),e.currency=n}(jQuery,FC);
</script>Step 2: Set up forms
For the dynamic product total calculation to work, you need to set up your product add to cart forms as follows:
Product Code
Every Foxy based product that you want to dynamically calculate the total for needs to have a unique product code set like this:
<input type="hidden" name="code" value="mycode" />If you have multiple products within a single form, the code from the first product in your form will be used as the code for the form. If you're not able to know what product code is first, or you just want to set a specific code to use for the price modifier without relying on the product's code, you can also add a data attribute to your form like this:
<form ... data-fc-form-code="mycode">Total Price Element
Next, set an element for where the total product cost is shown and updated, by combining the code from above and -total as its class, like this:
<h4 class="mycode-total">$20.00</h4>If you'd like to show the total quantity, you can also do that using an element with a class that is a combination of the code and -total-quantity as its class, like this:
<p class="mycode-total-quantity">0</p>You can also use classes with underscores instead if needed, such as mycode_total and mycode_total_quantity.
Product Price Element
As with a normal Foxy product form, the name and price inputs are required. The snippet will use the price inputs in the form to determine the base prices for the products present.
If you wanted to specify a different currency for that product than your store's default currency as the starting base price, you can append the currency code to the price (like 29.95eur for €29,95). It will use that currency as the base currency for all products within that form, so ensure that if you take this approach, all prices (including product option modifiers) are in that currency.
Step 3: Configuration for currency conversion
By default, the snippet will just handle calculating the dynamic product price. It can also support dynamically converting your product prices to different currencies, as well as automatically assigning a currency based on the location of a customer.
Note: The dynamic currency conversion feature relies on your store having multiple template sets configured. If you haven't set up multiple template sets on your Foxy store yet, you can see information on creating those here.
If you'd like to also enable multiple currencies for your product prices, the snippet includes a number of options for configuring this functionality. The configuration is applied with javascript through an object variable of FC.currency_config. These are the different configuration options available:
baseAccepts: String, 3 character currency code Description: Set to the currency that your product prices are in by default. Required for dynamic currency conversion.setsAccepts: Array of different currencies available for your store Description: This array will contain the details for each template set configured on Settings > Templates that you want to use on your store. Each object in the array includes three variables,currency,template_setandcountries. Required when using dynamic currency conversion.sets.currencyAccepts: String, 3 character currency code Description: Set to the currency that this particular template set uses. Required when includingsets.sets.template_setAccepts: String, Template set code Description: Set to the code of the template set, as set on Settings > Templates. Required when includingsets.sets.countriesAccepts: Array of 2 character country codes Description: A list of countries that will automatically be set to this template set on initial page load. If a customer visits that doesn't match any country in a set, then it will switch to theDEFAULTtemplate set. If not set, this template set will not be set automatically, but could be selected manually.max_rate_cacheAccepts: Number Description: The number of hours that the rates should be cached before being refreshed. Defaults to 12 hours.roundingAccepts:full,half,minimal,noneDescription: Configure how the prices should be rounded when being converted.fullwill round up to the nearest whole number,halfwill round up to the nearest.50,minimalwill round up to the nearest0.05andnonewill not round at all. Default isminimalactive_classAccepts: String Description: Customise what class is added to the template set selectors for the current active template set. Default isactiveuse_openexchangeratesAccepts: String Description: Set to the API key for your OpenExchangeRates account if you would prefer to use it instead of the default https://floatrates.com source for currency rates.adjust_price_displayAccepts: Function Description: Create a function to modify only the price displayed on the page, for example to make a tax exclusive price in your add to cart to be displayed as tax inclusive to the customer. The function is passed a single argument for the price to be modified, and the function must return the modified price as well.
Tip: All of the configuration options are optional, but to offer dynamic currency conversion
baseandsetsare required.
As an example, assume you had four template sets on your store. The store's DEFAULT template set is configured for USD, as are the product prices on the website by default, but there are also template sets for CAD, as well as Euro in both French and German languages. Your configuration could look like this, included above the snippet from step 1:
<script>
var FC = FC || {};
FC.currency_config = {
'base': 'USD',
'sets': [
{
'currency': 'USD',
'template_set': 'DEFAULT',
'countries': ['US']
},
{
'currency': 'CAD',
'template_set': 'Canada',
'countries': ['CA']
},
{
'currency': 'EUR',
'template_set': 'Germany',
'countries': ['DE', 'BE']
},
{
'currency': 'EUR',
'template_set': 'France',
'countries': ['FR']
},
]
};
</script>If a customer from Germany or Belgium visits your website, it will automatically apply the template set from your store with a code of Germany, and then convert the prices to all be in Euro. If someone visits from Canada, then it will update to the Canada template set, and change the prices to Canadian dollars.
If curreny conversion is enabled for the snippet, then any price and p product option modifiers will be dynamically converted to the current currency of the cart, rounding as specified by the rounding configuration option.
Here is an example with all configuration options for the snippet:
<script>
var FC = FC || {};
FC.currency_config = {
'base': 'USD',
'sets': [
{
'currency': 'USD',
'template_set': 'DEFAULT',
'countries': ['US']
},
{
'currency': 'CAD',
'template_set': 'Canada',
'countries': ['CA']
}
],
'max_rate_cache': 12,
'rounding': 'half',
'active_class': 'currency-option-active',
'use_openexchangerates': 'abc123abc123abc123',
'adjust_price_display': function(price) { return price * 1.25; }
};
</script>Important: Note: The above is simply an example of what it looks like with all attributes for the configuration option. You will not want to copy this to use directly in your set up, but rather review each configuration option and only add them if you actually need them.
Note: By default this snippet will pull the currency conversion rates from floatrates.com. It does also support Open Exchange Rates (which is also what we use for our own default currency conversion in the cart), but you'll need to create your own account and specify your API key using the
use_openexchangeratesconfiguration option.
Step 4: Set up a currency switcher
Although the snippet will automatically set a customers template set based on their location, you'll probably also want to allow customers to switch template sets on the fly, or perhaps have a modal window that displays to allow them to select an option.
You can set up your currency switcher however you want - it just requires a data attribute of data-fc-template-set be added to each link that designates what currency set it is for from your configuration from step 3.
For example, to have a link use the German Euro template set from the earlier example, you would add a data attribute of data-fc-template-set="Germany" to the link element.
Here's an example of what the currency switcher could look like:
<p>Select a locale: <a data-fc-template-set="DEFAULT" href="#">USD</a> | <a data-fc-template-set="Canada" href="#">CAD</a> | <a data-fc-template-set="Germany" href="#">EUR (DE)</a> | <a data-fc-template-set="France" href="#">EUR (FR)</a></p>The data-fc-template-set value needs to match the template_set from the FC.currency_config.sets configuration option.
For styling - note that whichever template set is active will have a class added. By default the added class will be active, but you can customise that using the active_class configuration option.
Step 5: Allowing currency conversion for other prices
You may also have some prices listed on your site outside of your add to cart forms. For example, on a category listing page. To have these prices be converted to the currency of the cart, you just need to add a class to the element of .fc-currency-price, and the snippet will take care of the rest. For example:
<h4 class="fc-currency-price">$15.99</h4>That's it!
That should be it. Give your site a test and confirm that everything is working as you expect it to.
Advanced: Dynamically triggering recalculation
If for whatever reason you need to trigger the dynamic price calculations to be recreated - for example if you dynamically add in a new input that includes a price modifier, you can execute this function in your javascript:
FC.currency.update();
Notes and Limitations
On the first time this snippet is run, it is run after the Foxy javascript has been initialized. As such - there may be a small time while the page is loading before the dynamic total is calculated. The snippet then uses your browser's
localStorageto store key information to execute on page load for subsequent pages to minimise any delay on converting.Currently only product
priceinputs and price product option modifiers are converted. Product level discounts are not currently converted.Product links are also not currently converted.