June 17, 2026 · 8 min read · Industry

Multi-currency for travel SaaS: the four problems nobody warns you about.

Stripe and Paddle handle the checkout. They do not handle the in-app price tag. Travel SaaS sits squarely in the gap, and the gap has four distinct shapes. Here they are, with examples in USD, EUR, GBP, JPY, BRL, and KRW.

1. Display vs settlement vs storage

Three different currencies, three different reasons. The currency you store a price in is the canonical record — usually USD or EUR for ease of accounting. The currency you settle in is what your payment processor moves to your bank — controlled by the customer's card and your merchant agreement. The currency you display is what the user reads on the page.

These are independent. A user in Tokyo seeing JPY 14,800 on a hotel listing has a price displayed in JPY, settled in USD by the processor, and stored in EUR in your database. Confusing the three is how you end up with reconciliation off by 0.4% every month and nobody knowing why.

2. FX rate freshness

Most teams pull a free FX feed once a day at 00:00 UTC and call it done. This is fine until a user in São Paulo sees a price quoted at the BRL rate from 17 hours ago, books, and the processor applies a different rate at the moment of charge. The user complains, the support team doesn't know which rate is right, the dispute escalates.

The defensible posture: refresh FX every 60 minutes for major pairs (USD/EUR/GBP/JPY), every 15 minutes for volatile pairs (TRY, ARS, BRL), and stamp every quote with the rate timestamp and rate value. When the customer asks, you show them the exact rate they were quoted at. When they book, you re-fetch and show them the current rate before they confirm. Stripe's built-in conversion does this for checkout. Your in-app catalog displays do not, unless you wrote the code.

3. Decimal precision is not universal

USD, EUR, GBP, BRL: 2 decimal places. JPY, KRW, CLP, ISK: 0 decimal places. BHD, JOD, KWD, OMR, TND: 3 decimal places. CLF (a Chilean accounting unit): 4. Every payments engineer learns this once, painfully.

A price stored as 14800 means JPY 14,800 — not 148.00. A price stored as 14800 in BHD means 14.800 — fourteen and eight hundred fils. Get this wrong and you charge a Bahraini customer a thousand times what they expected, or charge a Japanese customer a hundredth.

The right shape: store everything as integer minor units (yen, fils, cents, won), and store the currency code alongside. Render through a library that knows the precision rules — Intl.NumberFormat in JS, Babel in Python. Never divide by 100 unconditionally.

4. Prices that don't divide cleanly

A SaaS plan priced at $9.99/mo converts to GBP 7.84 at today's rate. GBP 7.84 looks wrong. GBP 7.99 looks right. Users perceive currency- converted prices as cheap or expensive based on how "clean" the number is, not the actual value.

The fix is anchored pricing per currency: don't auto-convert your primary tier into 30 currencies. Set 5-7 primary currencies manually at psychologically clean numbers (USD 9.99, EUR 9.99, GBP 7.99, JPY 1,500, BRL 49, KRW 13,000, INR 799), and let the rest auto-convert with a footnote. Stripe and Paddle both support this; most teams don't enable it because the docs bury the feature.

What this looks like in code

// stored canonical
{ "amount_minor": 999, "currency": "USD" }

// for display in JP locale
{
  "amount_minor": 1500,
  "currency": "JPY",
  "display": "¥1,500",
  "fx_source": "manual_anchor"
}

// for display in BR locale
{
  "amount_minor": 4900,
  "currency": "BRL",
  "display": "R$ 49,00",
  "fx_source": "manual_anchor"
}

// for display in MX locale
{
  "amount_minor": 17500,
  "currency": "MXN",
  "display": "MX$ 175",
  "fx_source": "auto_fx",
  "fx_timestamp": "2026-06-17T11:00:00Z"
}

Storage is integer minor units. Display includes the formatted string pre-rendered server-side, the source of the rate, and the timestamp. Reconciliation later becomes easy.

Where TravelMindsAI fits

Our /v1/cities response includes the country code. From the country code your client picks the local display currency (ISO 4217, easy lookup). We do not return prices — pricing belongs to your booking surface — but we make the localisation step a single field lookup instead of a heuristic.

Sign up — free See India coverage →