Skip to main content

Multi-Currency Payments

Accept payments from customers worldwide in their local currency while receiving settlement in your preferred currency.

Overview

Multi-currency support allows you to charge customers in their local currency (presentment currency) while receiving funds in your settlement currency. This improves conversion rates and provides transparency to international customers.

Key Benefits:

  • 💰 Better conversion - Customers see prices in familiar currency
  • 🌍 Global reach - Accept payments worldwide
  • 💱 Automatic conversion - Real-time FX rates applied
  • 📊 Transparent pricing - No surprise exchange rates for customers
  • 🏦 Single settlement - Receive all funds in one currency

Supported Currencies

Presentment Currencies (Charge Customers In)

  • THB - Thai Baht
  • SGD - Singapore Dollar
  • MYR - Malaysian Ringgit
  • JPY - Japanese Yen
  • USD - US Dollar
  • EUR - Euro
  • GBP - British Pound
  • AUD - Australian Dollar
  • HKD - Hong Kong Dollar
  • KRW - South Korean Won
  • IDR - Indonesian Rupiah
  • PHP - Philippine Peso
  • CNY - Chinese Yuan

And 100+ more currencies for card payments.

Settlement Currencies (Receive Funds In)

Your settlement currency depends on your merchant agreement:

  • THB (Thailand merchants)
  • SGD (Singapore merchants)
  • MYR (Malaysia merchants)
  • JPY (Japan merchants)
  • USD (International merchants)

How It Works

Example Flow:

  1. Merchant shows prices in USD to US customers
  2. Customer pays $100 USD
  3. Omise converts at current FX rate (e.g., 1 USD = 35 THB)
  4. Merchant receives ฿3,500 THB in settlement

Implementation

Basic Multi-Currency Charge

const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});

// Charge customer in their local currency
const charge = await omise.charges.create({
amount: 10000, // $100.00 USD
currency: 'USD', // Presentment currency
card: cardToken,
description: 'Order #12345'
});

// You'll receive settlement in your currency (e.g., THB)
console.log('Customer charged:', charge.amount, charge.currency);
// Settlement will be in THB based on FX rate

Dynamic Currency Selection

// Detect customer location and show appropriate currency
const customerCurrency = detectCustomerCurrency(req.headers);

const prices = {
USD: { amount: 10000, symbol: '$', display: '$100.00' },
EUR: { amount: 9000, symbol: '€', display: '€90.00' },
GBP: { amount: 8000, symbol: '£', display: '£80.00' },
THB: { amount: 350000, symbol: '฿', display: '฿3,500' }
};

const price = prices[customerCurrency] || prices.USD;

// Display to customer
showPrice(price.display);

// Charge in customer's currency
const charge = await omise.charges.create({
amount: price.amount,
currency: customerCurrency,
card: cardToken
});

Currency Detection

By IP Geolocation

const geoip = require('geoip-lite');

function detectCustomerCurrency(ipAddress) {
const geo = geoip.lookup(ipAddress);

if (!geo) return 'USD'; // Default

const currencyMap = {
'TH': 'THB',
'SG': 'SGD',
'MY': 'MYR',
'JP': 'JPY',
'US': 'USD',
'GB': 'GBP',
'AU': 'AUD'
// Add more countries
};

return currencyMap[geo.country] || 'USD';
}

// Usage
const currency = detectCustomerCurrency(req.ip);

By Browser Language

function detectCurrencyByLanguage(acceptLanguage) {
const langCurrencyMap = {
'th': 'THB',
'en-SG': 'SGD',
'ms': 'MYR',
'ja': 'JPY',
'en-US': 'USD',
'en-GB': 'GBP'
};

// Parse Accept-Language header
const primaryLang = acceptLanguage.split(',')[0].split(';')[0];
return langCurrencyMap[primaryLang] || 'USD';
}

Manual Currency Selector

<div class="currency-selector">
<label>Currency:</label>
<select id="currency" onchange="updatePrices()">
<option value="USD" selected>USD ($)</option>
<option value="EUR">EUR (€)</option>
<option value="GBP">GBP (£)</option>
<option value="THB">THB (฿)</option>
<option value="SGD">SGD ($)</option>
<option value="MYR">MYR (RM)</option>
<option value="JPY">JPY (¥)</option>
</select>
</div>

<script>
function updatePrices() {
const currency = document.getElementById('currency').value;
const basePrice = 100; // USD

// Fetch current FX rates
fetch(`/api/convert?from=USD&to=${currency}&amount=${basePrice}`)
.then(r => r.json())
.then(data => {
document.querySelectorAll('.price').forEach(el => {
el.textContent = formatCurrency(data.amount, currency);
});
});
}
</script>

Exchange Rates

Current Rates

// Get current exchange rate
app.get('/api/exchange-rate', async (req, res) => {
const { from, to } = req.query;

// Omise provides FX rates through the API
const rate = await omise.forexRates.retrieve({
from: from,
to: to
});

res.json({
from: rate.from,
to: rate.to,
rate: rate.rate,
updated_at: rate.updated_at
});
});

Price Conversion

function convertPrice(amount, fromCurrency, toCurrency, fxRate) {
if (fromCurrency === toCurrency) {
return amount;
}

const converted = Math.round(amount * fxRate);
return converted;
}

// Usage
const usdAmount = 10000; // $100.00
const thbAmount = convertPrice(usdAmount, 'USD', 'THB', 35);
// Returns 350000 (฿3,500)

Complete Example

const express = require('express');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

const app = express();
app.use(express.json());

// Base prices in USD
const PRODUCTS = {
'product-1': { name: 'Product 1', price_usd: 10000 },
'product-2': { name: 'Product 2', price_usd: 25000 }
};

// FX rates (cache these, update hourly)
let fxRates = {
EUR: 0.92,
GBP: 0.79,
THB: 35.00,
SGD: 1.35,
JPY: 149.50
};

// Get product price in customer currency
app.get('/api/products/:id/price', (req, res) => {
const product = PRODUCTS[req.params.id];
const currency = req.query.currency || 'USD';

if (!product) {
return res.status(404).json({ error: 'Product not found' });
}

let amount = product.price_usd;

if (currency !== 'USD') {
const rate = fxRates[currency];
if (rate) {
amount = Math.round(amount * rate);
}
}

res.json({
product_id: req.params.id,
name: product.name,
amount: amount,
currency: currency,
formatted: formatCurrency(amount, currency)
});
});

// Create charge in customer currency
app.post('/checkout', async (req, res) => {
try {
const { product_id, currency, token } = req.body;
const product = PRODUCTS[product_id];

// Calculate amount in customer currency
let amount = product.price_usd;
if (currency !== 'USD' && fxRates[currency]) {
amount = Math.round(amount * fxRates[currency]);
}

// Create charge
const charge = await omise.charges.create({
amount: amount,
currency: currency || 'USD',
card: token,
description: `${product.name} - ${product_id}`,
metadata: {
product_id: product_id,
original_currency: 'USD',
original_amount: product.price_usd
}
});

res.json({
charge_id: charge.id,
status: charge.status,
amount: charge.amount,
currency: charge.currency
});

} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Update FX rates periodically
setInterval(async () => {
try {
// Fetch latest rates from Omise or external provider
const rates = await fetchLatestRates();
fxRates = rates;
} catch (error) {
console.error('Failed to update FX rates:', error);
}
}, 60 * 60 * 1000); // Every hour

app.listen(3000);

Currency Formatting

function formatCurrency(amount, currency) {
// Amount is in smallest currency unit
const divisors = {
JPY: 1, // Yen has no decimal
KRW: 1, // Won has no decimal
THB: 100, // 2 decimals
USD: 100, // 2 decimals
EUR: 100, // 2 decimals
GBP: 100 // 2 decimals
};

const divisor = divisors[currency] || 100;
const value = amount / divisor;

return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
minimumFractionDigits: divisor === 1 ? 0 : 2,
maximumFractionDigits: divisor === 1 ? 0 : 2
}).format(value);
}

// Usage
formatCurrency(10000, 'USD'); // "$100.00"
formatCurrency(350000, 'THB'); // "฿3,500.00"
formatCurrency(10000, 'JPY'); // "¥10,000"

Best Practices

1. Show Total Price Clearly

<div class="price-breakdown">
<div class="item-price">
<span>Item Price:</span>
<span id="item-price">$100.00 USD</span>
</div>
<div class="total-price">
<span>Total:</span>
<strong id="total-price">$100.00 USD</strong>
</div>
<p class="settlement-note">
<small>You will be charged in USD. Exchange rate applied at time of purchase.</small>
</p>
</div>

2. Cache Exchange Rates

// Don't fetch rates on every request
// Cache for 1 hour and update periodically
const cache = new Map();
const CACHE_TTL = 60 * 60 * 1000; // 1 hour

async function getCachedRate(from, to) {
const key = `${from}-${to}`;
const cached = cache.get(key);

if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.rate;
}

const rate = await fetchRateFromAPI(from, to);
cache.set(key, { rate, timestamp: Date.now() });
return rate;
}

3. Handle Zero-Decimal Currencies

// JPY, KRW have no decimal places
const zeroDecimalCurrencies = ['JPY', 'KRW', 'VND', 'CLP'];

function getAmountForCurrency(baseAmount, currency) {
if (zeroDecimalCurrencies.includes(currency)) {
return baseAmount; // No multiplication by 100
}
return baseAmount * 100; // For currencies with decimals
}

4. Display Currency Symbol

const currencySymbols = {
USD: '$',
EUR: '€',
GBP: '£',
THB: '฿',
SGD: 'S$',
MYR: 'RM',
JPY: '¥',
KRW: '₩'
};

function formatPrice(amount, currency) {
const symbol = currencySymbols[currency] || currency;
return `${symbol}${amount}`;
}

5. Test with Multiple Currencies

Test checkout flow with:

  • USD (standard)
  • EUR (common international)
  • JPY (zero decimal)
  • THB (your settlement currency if different)

FAQ

What exchange rate is used?

Omise uses real-time market exchange rates from major financial institutions. Rates are updated frequently throughout the day.

Who bears the FX risk?

The merchant bears FX risk. If rates change between charge and settlement, the settlement amount may vary slightly.

Can I set my own exchange rates?

No, Omise applies market rates automatically. You can add markup to your prices if desired, but not to the FX rate itself.

What currency do I receive?

You receive funds in your settlement currency (typically based on your merchant country - THB for Thailand, SGD for Singapore, etc.).

Can customers choose their currency?

Yes, you can implement a currency selector. However, presenting prices in the customer's local currency automatically (based on location) typically increases conversion.

Next Steps

  1. Determine which currencies to support
  2. Implement currency detection
  3. Add currency selector (optional)
  4. Test with multiple currencies
  5. Monitor FX rates and settlement
  6. Go live