DEV Community

Cover image for Going Global: Building Multi-Currency Apps
APIVerve
APIVerve

Posted on • Originally published at blog.apiverve.com

Going Global: Building Multi-Currency Apps

You built your pricing in USD. Everything works. Then you check your analytics and see traffic from 47 countries.

The German visitors see $99.00 and do mental math. The Japanese visitors see $99.00 and wonder if that's expensive or cheap. The British visitors just bounce because they can't be bothered to convert.

Stripe reports that showing prices in local currency can increase conversion rates by 30% or more. That's not a small optimization — it's the difference between a successful international launch and a failed one.

Going multi-currency isn't just about slapping a currency converter on your pricing page. It's about making international customers feel like you built the product for them.

The Psychology of Currency

When I see a price in dollars, I don't just see a number. I see a foreign product, made by a foreign company, priced in foreign money. Even if the price is reasonable, there's a psychological distance.

When I see a price in my local currency, that distance disappears. The product feels local. The company feels like they understand me. The price is directly comparable to other things I buy.

This isn't rational — a converted price should be equivalent. But shopping isn't rational. People make decisions based on comfort, familiarity, and trust. Showing local currency builds all three.

There's also the practical issue: mental math is hard. Quick, is $99 a good price in euros? You probably know the euro is roughly equivalent to the dollar, but is it 0.92 or 1.08 right now? Is that before or after the recent fluctuation? Most people won't bother to figure it out. They'll just leave.

Start With Display

Before you change anything about how payments work, change what users see.

The simplest implementation: detect the user's likely currency and convert your prices for display. The actual charge still happens in USD (or whatever your base currency is), but the displayed price is localized.

async function displayPrice(basePrice, userCurrency) {
  const response = await fetch(
    `https://api.apiverve.com/v1/currencyconverter?from=USD&to=${userCurrency}&value=${basePrice}`,
    { headers: { 'x-api-key': API_KEY } }
  );
  const { data } = await response.json();

  return new Intl.NumberFormat(undefined, {
    style: 'currency',
    currency: userCurrency
  }).format(data.convertedValue);
}
Enter fullscreen mode Exit fullscreen mode

Now your German visitor sees €91.23 instead of $99.00. No mental math required. The price feels native.

This approach has a catch: the final charge might differ slightly from the displayed price due to exchange rate fluctuations and payment processor conversions. You should show a disclaimer ("approximate price in EUR, final charge in USD") to set expectations. But even with that caveat, conversion rates improve significantly.

Detecting User Currency

Where does the user's preferred currency come from? You have several options, each with tradeoffs.

Explicit selection is the most accurate. Show a currency picker in your header or settings. The user tells you exactly what they want. The downside is friction — one more decision for the user to make.

Account country works if users have signed up. Their billing address implies a currency. German billing address means euros. This is reliable for existing customers, useless for prospects.

IP geolocation lets you guess based on location. A visitor from France probably wants euros. This is usually right, but fails for travelers, VPN users, and expats. Someone in Germany might be an American tourist who thinks in dollars.

Browser locale provides a weak signal. The Accept-Language header or JavaScript's navigator.language hints at preferences. A browser set to en-GB probably wants pounds. But people don't always configure this correctly.

The best approach combines these signals: detect automatically using IP or browser locale, but make it easy to override. Show "Prices in EUR" somewhere visible with a link to change it. Remember explicit choices.

The Pricing Decision

Display currency is the easy part. The harder question: should you charge in local currency, or just display in local currency?

Option A: Display local, charge base currency. You show €91.23 on the website, but when they check out, you charge $99.00 USD. Their bank handles the conversion. This is simpler to implement — you're just adding a display layer.

The downside: the final charge on their card statement might be €92.47 or €89.98, depending on exchange rates and card fees. Customers don't love seeing a different amount than they expected.

Option B: Actually charge in local currency. You show €89.00 and charge €89.00. The customer knows exactly what they're paying. You handle the currency complexity.

This requires:

  • Setting explicit prices per currency (not just converting USD prices)
  • A payment processor that handles multi-currency settlement
  • Understanding how you receive the funds (converted to USD? Held in EUR?)
  • Managing price changes across multiple currencies

Most companies start with Option A to test international demand. When specific markets prove valuable, they graduate to Option B for those markets.

Pricing Psychology Across Currencies

Exchange rates create awkward numbers. $99.00 at today's rate is €91.23. That looks calculated, not intentional.

Rounded prices (€89, €99, €90) feel deliberate. Fractional prices (€91.23) feel like an afterthought. Users notice the difference.

Consider the cultural conventions around pricing:

  • US: $99.99 pricing is standard, .99 endings everywhere
  • Europe: €89 or €89.90 more common than €89.99
  • Japan: Round numbers in thousands (¥9,800 not ¥9,843)
  • UK: Similar to US, but £79.99 instead of .95 endings

If you're serious about a market, set intentional local prices rather than converting mathematically.

There's also the anchor price effect. In some currencies, the nominal numbers are much larger. $99 converts to roughly ¥15,000 in Japanese yen. The large number doesn't mean expensive — Japanese users are calibrated to those magnitudes. But make sure your display handles them properly (no overflow, appropriate formatting).

The Formatting Details

Currency formatting varies more than you'd expect.

Symbol position: $100 (symbol before) vs 100€ (symbol after). Euros are typically shown with the symbol after the number in many European countries, though € before is also accepted.

Decimal separator: $100.00 (US) vs €100,00 (much of Europe). Using the wrong separator reads as "one hundred thousand" instead of "one hundred."

Thousands separator: $1,000 (US) vs €1.000 (Germany) vs €1 000 (France). This is literally the opposite of the decimal separator.

Currency codes vs symbols: $100 vs USD 100 vs 100 USD. Depends on context and regional preference.

Don't try to hand-code this. Use Intl.NumberFormat:

function formatCurrency(amount, currency, locale = undefined) {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency,
    minimumFractionDigits: currency === 'JPY' ? 0 : 2,
    maximumFractionDigits: currency === 'JPY' ? 0 : 2
  }).format(amount);
}
Enter fullscreen mode Exit fullscreen mode

The locale parameter controls formatting conventions. Pass undefined to use the user's browser locale, or specify explicitly (de-DE for German formatting, en-US for US formatting).

Exchange Rate Freshness

Currency rates change constantly. Every minute, every second. How fresh do your rates need to be?

For e-commerce display: Rates from the last hour are fine. Nobody notices if €91.23 should technically be €91.27. Update hourly, cache aggressively.

For invoicing: Use the rate at invoice time and document it clearly. "Amount calculated using EUR/USD rate of 1.0843 as of March 1, 2026." If payment comes days or weeks later, the rate may have changed, but the invoice amount is fixed.

For financial applications: Real-time or near-real-time rates matter. Stale rates in a forex app could cost users money. The freshness requirements drive your architecture.

For most applications, hourly rate updates are sufficient. Don't over-engineer this unless your use case demands it.

The VAT Complication

European customers expect VAT-inclusive pricing. American customers expect tax added at checkout. This creates a display problem.

€89.00 shown to a German customer should include VAT. That same €89.00 shown to an American customer should not include VAT (they'll see tax at checkout based on their location).

This means your €89.00 is actually:

  • €74.79 net + €14.21 VAT for German customers (VAT-inclusive)
  • €89.00 net + tax at checkout for US customers

And it gets more complex: different EU countries have different VAT rates. Some products are VAT-exempt. Digital services have special rules. B2B sales handle VAT differently than B2C.

Most developers don't implement VAT calculation themselves. Services like Stripe Tax, Paddle, or specialized tax APIs handle the complexity. You just need to know that "showing a price" means showing different things in different contexts.

Which Currencies to Support

You can't support every currency immediately. There are 180 currencies in the world; supporting all of them from day one is impractical.

Prioritize based on your actual traffic and revenue potential:

  1. USD — You're probably already here
  2. EUR — Single currency for 20 countries, massive market
  3. GBP — UK is often a top market for English-language products
  4. CAD, AUD — English-speaking, high purchasing power, easy markets
  5. JPY — Large economy, significant online spending

These six currencies likely cover 80%+ of your international revenue potential. Add more based on data — if your analytics show significant traffic from Brazil, add BRL.

Be aware of legal and practical considerations. Some currencies have capital controls or restrictions. Some payment processors don't support certain currencies. Do your research before promising support.

Testing Multi-Currency

Multi-currency code has edge cases that break in production:

Zero decimal currencies: Japanese yen (¥) doesn't use decimal places. ¥1000.00 looks wrong. So do Korean won (₩) and several others.

Right-to-left currencies: Saudi riyal (SAR) and UAE dirham (AED) are used in RTL-language contexts. Make sure your display doesn't break.

Very large numbers: Vietnamese dong (VND) trades at roughly 24,000 per USD. $100 is about 2,400,000 VND. Can your UI handle seven-digit prices?

Very small amounts: Some cryptocurrencies or currency subdivisions involve many decimal places. Probably not your problem, but worth considering.

Historic rates: If you show "your purchase from last month," do you use today's exchange rate or the rate at time of purchase? Different answers for different contexts.

Test with real currency data across your supported currencies before launching.

Building Trust

International customers are wary of foreign purchases. They've been burned by unexpected conversion fees, surprise customs charges, and companies that don't ship internationally.

Build trust by being explicit:

  • Show the currency clearly ("Price: €89.00 EUR")
  • If the charge happens in a different currency, say so
  • Explain any potential additional fees
  • Show local-feeling design (date formats, address fields, etc.)

Small details matter. Using "ZIP code" instead of "postal code" signals American-centricity. Expecting phone numbers in US format frustrates international users. These things add up.

The Infrastructure

If you're serious about multi-currency, your infrastructure needs to support it:

Database: Store amounts with their currency. amount DECIMAL(10,2) isn't enough; you need amount plus currency. Consider storing everything in a base currency for reporting.

Calculations: Never do math across currencies without explicit conversion. Adding $100 + €100 is meaningless unless you convert first.

Reporting: Decide whether your internal reporting uses a single currency (converted) or preserves original currencies. Both approaches have tradeoffs.

Price changes: When you raise prices, do you raise all currencies proportionally? Or independently? How do you handle customers who bought at the old price?

Multi-currency touches more of your system than you might expect. Plan accordingly.


Convert between 150+ currencies with the Currency Converter API. Get raw rate data with the Exchange Rate API. Make every international customer feel like the product was built for them.


Originally published at APIVerve Blog

Top comments (0)