Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to handle multiple currencies in Stripe

Handle multiple currencies in Stripe by specifying the currency code (lowercase ISO 4217) when creating PaymentIntents or Checkout Sessions. Stripe settles to your default currency at the current exchange rate. Use Adaptive Pricing for automatic localized pricing in Checkout, or create separate Price objects per currency for fixed international pricing.

What you'll learn

  • How to create charges in different currencies
  • The difference between presentment currency and settlement currency
  • How to use Stripe Adaptive Pricing for automatic currency conversion
  • How to handle zero-decimal currencies like JPY
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate6 min read15 minutesStripe API v2024-12+, Node.js 18+March 2026RapidDev Engineering Team
TL;DR

Handle multiple currencies in Stripe by specifying the currency code (lowercase ISO 4217) when creating PaymentIntents or Checkout Sessions. Stripe settles to your default currency at the current exchange rate. Use Adaptive Pricing for automatic localized pricing in Checkout, or create separate Price objects per currency for fixed international pricing.

Multi-Currency Payments with Stripe

Stripe supports 135+ currencies. When you create a PaymentIntent, you specify the presentment currency — what the customer sees and pays. Stripe converts to your settlement currency (the currency of your bank account) at the current exchange rate, charging a 1% conversion fee on top of standard processing fees. For Stripe Checkout, Adaptive Pricing can auto-convert prices to the customer's local currency. Alternatively, create separate Price objects for each currency for fixed rates.

Prerequisites

  • A Stripe account with your settlement currency configured
  • Node.js 18+ with the stripe npm package
  • Understanding of ISO 4217 currency codes (usd, eur, gbp, jpy, etc.)

Step-by-step guide

1

Create a PaymentIntent in a specific currency

Specify the currency parameter as a lowercase ISO 4217 code. The amount is in the smallest unit of that currency.

typescript
1// Charge €25.00 (EUR)
2const paymentIntentEUR = await stripe.paymentIntents.create({
3 amount: 2500, // 2500 cents = €25.00
4 currency: 'eur',
5 automatic_payment_methods: { enabled: true },
6});
7
8// Charge £15.00 (GBP)
9const paymentIntentGBP = await stripe.paymentIntents.create({
10 amount: 1500, // 1500 pence = £15.00
11 currency: 'gbp',
12 automatic_payment_methods: { enabled: true },
13});
14
15// Charge ¥3000 (JPY — zero-decimal currency)
16const paymentIntentJPY = await stripe.paymentIntents.create({
17 amount: 3000, // 3000 yen (no cents)
18 currency: 'jpy',
19 automatic_payment_methods: { enabled: true },
20});

Expected result: PaymentIntents are created in EUR, GBP, and JPY. Stripe converts to your settlement currency at the current exchange rate.

2

Use dynamic currency in Checkout Sessions

Pass the customer's preferred currency to your Checkout Session endpoint. Stripe displays the price in that currency.

typescript
1app.post('/create-checkout-session', async (req, res) => {
2 const { currency = 'usd', amount } = req.body;
3
4 const session = await stripe.checkout.sessions.create({
5 mode: 'payment',
6 line_items: [
7 {
8 price_data: {
9 currency: currency.toLowerCase(),
10 product_data: { name: 'Premium Widget' },
11 unit_amount: amount, // In smallest unit of currency
12 },
13 quantity: 1,
14 },
15 ],
16 success_url: 'https://yoursite.com/success',
17 cancel_url: 'https://yoursite.com/cancel',
18 });
19
20 res.json({ url: session.url });
21});

Expected result: The Checkout page displays the price in the specified currency with the correct symbol and format.

3

Enable Adaptive Pricing for automatic conversion

Stripe's Adaptive Pricing (for Checkout and Payment Links) automatically shows prices in the customer's local currency based on their location. Enable it in your Dashboard or when creating Prices.

typescript
1// Create a Price with Adaptive Pricing enabled
2const price = await stripe.prices.create({
3 product: 'prod_xxx',
4 unit_amount: 2000, // Base price: $20.00 USD
5 currency: 'usd',
6 currency_options: {
7 eur: { unit_amount: 1850 }, // Fixed: €18.50
8 gbp: { unit_amount: 1600 }, // Fixed: £16.00
9 },
10});
11
12// Or use the Price in a Checkout Session
13const session = await stripe.checkout.sessions.create({
14 mode: 'payment',
15 line_items: [{ price: price.id, quantity: 1 }],
16 // Stripe auto-selects currency based on customer location
17 success_url: 'https://yoursite.com/success',
18 cancel_url: 'https://yoursite.com/cancel',
19});

Expected result: Customers in Europe see EUR pricing, UK customers see GBP, and others see USD or their local equivalent.

4

Handle zero-decimal currencies correctly

Some currencies like JPY, KRW, VND don't have sub-units (cents). The amount value IS the full amount. Build a helper to handle this.

typescript
1const ZERO_DECIMAL_CURRENCIES = [
2 'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga',
3 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf',
4];
5
6function toStripeAmount(dollarAmount, currency) {
7 if (ZERO_DECIMAL_CURRENCIES.includes(currency.toLowerCase())) {
8 return Math.round(dollarAmount); // No multiplication needed
9 }
10 return Math.round(dollarAmount * 100); // Convert to cents
11}
12
13// Usage:
14toStripeAmount(20, 'usd'); // 2000 (cents)
15toStripeAmount(3000, 'jpy'); // 3000 (yen)

Expected result: The helper correctly converts display amounts to Stripe amounts for both regular and zero-decimal currencies.

Complete working example

multi-currency-server.js
1const express = require('express');
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3
4const app = express();
5app.use(express.json());
6
7const ZERO_DECIMAL_CURRENCIES = [
8 'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga',
9 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf',
10];
11
12function toSmallestUnit(amount, currency) {
13 const cur = currency.toLowerCase();
14 if (ZERO_DECIMAL_CURRENCIES.includes(cur)) {
15 return Math.round(amount);
16 }
17 return Math.round(amount * 100);
18}
19
20function fromSmallestUnit(amount, currency) {
21 const cur = currency.toLowerCase();
22 if (ZERO_DECIMAL_CURRENCIES.includes(cur)) {
23 return amount;
24 }
25 return amount / 100;
26}
27
28// Multi-currency checkout
29app.post('/create-checkout-session', async (req, res) => {
30 const { displayAmount, currency = 'usd', productName = 'Widget' } = req.body;
31
32 try {
33 const session = await stripe.checkout.sessions.create({
34 mode: 'payment',
35 line_items: [
36 {
37 price_data: {
38 currency: currency.toLowerCase(),
39 product_data: { name: productName },
40 unit_amount: toSmallestUnit(displayAmount, currency),
41 },
42 quantity: 1,
43 },
44 ],
45 success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
46 cancel_url: `${req.headers.origin}/cancel`,
47 });
48
49 res.json({ url: session.url });
50 } catch (err) {
51 res.status(500).json({ error: err.message });
52 }
53});
54
55// Multi-currency PaymentIntent
56app.post('/create-payment-intent', async (req, res) => {
57 const { displayAmount, currency = 'usd' } = req.body;
58
59 try {
60 const paymentIntent = await stripe.paymentIntents.create({
61 amount: toSmallestUnit(displayAmount, currency),
62 currency: currency.toLowerCase(),
63 automatic_payment_methods: { enabled: true },
64 });
65
66 res.json({ clientSecret: paymentIntent.client_secret });
67 } catch (err) {
68 res.status(500).json({ error: err.message });
69 }
70});
71
72app.listen(4000, () => console.log('Multi-currency server on port 4000'));

Common mistakes when handling multiple currencies in Stripe

Why it's a problem: Treating zero-decimal currencies like regular currencies

How to avoid: For JPY, KRW, VND, and other zero-decimal currencies, the amount IS the full amount. Passing 2000 for JPY means ¥2,000 — not ¥20.00. Use a helper function to handle conversion.

Why it's a problem: Assuming settlement happens in the presentment currency

How to avoid: Stripe converts to your settlement currency (your bank account's currency) at the current rate. A €25 charge settles in USD if your bank is USD. Stripe charges 1% for currency conversion.

Why it's a problem: Hardcoding prices without considering exchange rates

How to avoid: If you set EUR prices as a simple conversion of USD, exchange rates will drift. Either use Adaptive Pricing or regularly update your currency_options with current rates.

Why it's a problem: Using uppercase currency codes

How to avoid: Stripe requires lowercase ISO 4217 codes: 'usd', not 'USD'. Always .toLowerCase() the currency before passing to Stripe.

Best practices

  • Always use lowercase ISO 4217 currency codes (usd, eur, gbp, jpy)
  • Build a helper function that handles zero-decimal currency conversion
  • Use Adaptive Pricing in Checkout for automatic localized pricing
  • Create fixed currency_options on Prices for markets where you want specific pricing
  • Account for Stripe's 1% currency conversion fee in your pricing
  • Test with multiple currencies using card 4242 4242 4242 4242 in test mode
  • Display the correct currency symbol on your frontend using Intl.NumberFormat

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a Node.js Express server that creates Stripe PaymentIntents in multiple currencies. Include a helper function for zero-decimal currencies (JPY, KRW). Accept the currency and display amount from the request body and convert to Stripe's smallest unit format.

Stripe Prompt

Add multi-currency support to my Stripe checkout. Accept a currency parameter (usd, eur, gbp, jpy) from the frontend, correctly handle zero-decimal currencies, and create the Checkout Session in the specified currency.

Frequently asked questions

How many currencies does Stripe support?

Stripe supports 135+ currencies for payment processing. The exact list depends on your country and payment methods. Check your Dashboard → Settings → Payment methods to see which currencies are enabled.

What is the currency conversion fee?

Stripe charges a 1% fee on top of the standard processing fee when the presentment currency differs from your settlement currency. For example, if you charge in EUR but settle in USD, there's an extra 1% fee.

Can I settle in multiple currencies?

Yes, if you add bank accounts in different currencies. For example, add a EUR bank account to settle EUR charges directly without conversion. Go to Settings → Bank accounts and scheduling.

What are zero-decimal currencies?

Currencies like JPY, KRW, and VND don't have sub-units (cents). For JPY, amount: 5000 means ¥5,000. For USD, amount: 5000 means $50.00. Always check if a currency is zero-decimal before converting.

Can RapidDev help with international payment setup?

Yes. RapidDev helps businesses set up multi-currency payment systems including localized pricing tables, automatic currency detection, exchange rate management, and multi-region settlement optimization.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.