Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Integrate a Payment System for International Currencies in FlutterFlow

Implement international payments by combining Stripe's multi-currency support with exchange rates cached in Firestore. A daily Cloud Function fetches rates from an exchange rate API and stores them. The FlutterFlow UI includes a currency DropDown that converts displayed prices from your base currency using a Custom Function with proper locale formatting. The Stripe Checkout Session Cloud Function passes the selected currency so customers pay in their local currency.

What you'll learn

  • How to fetch and cache daily exchange rates in Firestore via a Cloud Function
  • How to build a currency selector DropDown and convert prices dynamically
  • How to format prices with locale-aware currency symbols and decimal rules
  • How to create Stripe Checkout Sessions in the selected currency
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read20-25 minFlutterFlow Pro+ (Cloud Functions required for Stripe)March 2026RapidDev Engineering Team
TL;DR

Implement international payments by combining Stripe's multi-currency support with exchange rates cached in Firestore. A daily Cloud Function fetches rates from an exchange rate API and stores them. The FlutterFlow UI includes a currency DropDown that converts displayed prices from your base currency using a Custom Function with proper locale formatting. The Stripe Checkout Session Cloud Function passes the selected currency so customers pay in their local currency.

Setting Up Multi-Currency Payments in FlutterFlow

If your app serves an international audience, showing prices and accepting payments in local currencies dramatically improves conversion rates. This tutorial builds a complete multi-currency payment flow: daily exchange rate syncing, a currency selector, proper price formatting (including zero-decimal currencies like JPY), and Stripe Checkout configured for the selected currency.

Prerequisites

  • A FlutterFlow project on the Pro plan or higher
  • Firebase project with Firestore and Cloud Functions enabled
  • A Stripe account with the API secret key stored in Cloud Functions config
  • An exchange rate API key (exchangeratesapi.io or similar, free tier available)

Step-by-step guide

1

Create a Cloud Function to fetch and cache exchange rates daily

Create a scheduled Cloud Function that runs once daily at midnight UTC. The function calls the ExchangeRatesAPI endpoint with your base currency (e.g., USD) and retrieves conversion rates for all supported currencies. Write the rates map, the base currency, and an updatedAt timestamp to a single Firestore document at `config/exchange_rates`. This ensures every client reads from the same cached data without hitting the API directly. Store the exchange rate API key in Cloud Functions environment config.

updateExchangeRates.js
1// Cloud Function: updateExchangeRates
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const fetch = require('node-fetch');
5admin.initializeApp();
6
7exports.updateExchangeRates = functions.pubsub
8 .schedule('every 24 hours')
9 .onRun(async () => {
10 const apiKey = functions.config().exchange.key;
11 const base = 'USD';
12 const url = `https://api.exchangeratesapi.io/v1/latest?access_key=${apiKey}&base=${base}`;
13 const res = await fetch(url);
14 const data = await res.json();
15
16 if (data.success && data.rates) {
17 await admin.firestore()
18 .doc('config/exchange_rates')
19 .set({
20 base,
21 rates: data.rates,
22 updatedAt: admin.firestore.FieldValue
23 .serverTimestamp(),
24 });
25 }
26 });

Expected result: The config/exchange_rates document updates daily with fresh conversion rates for all currencies relative to your base currency.

2

Build the currency selector DropDown and store the user preference

On your pricing or product page, add a DropDown widget bound to a list of supported currency codes (USD, EUR, GBP, JPY, CAD, AUD, INR, BRL, etc.) stored in an Option Set named SupportedCurrencies. Set the default value from App State `selectedCurrency` (persisted). When the user changes the selection, trigger an Update App State action to save the new currency code. Add a flag icon or currency symbol next to the DropDown using a Custom Function that maps currency codes to their symbols ($, EUR, GBP, JPY, etc.).

Expected result: Users can select their preferred currency from a DropDown, and the selection persists across sessions via App State.

3

Create a Custom Function to convert and format prices

Create a Custom Function named formatPrice that takes three parameters: baseAmount (double), targetCurrency (String), and rates (a JSON map from the exchange_rates document). The function multiplies the base amount by the target currency rate, then formats the result using Dart's NumberFormat.currency with the correct symbol and decimal places. Important: zero-decimal currencies like JPY, KRW, and VND use 0 decimal places while most others use 2. Include a map of zero-decimal currency codes and check against it. Return the formatted string (e.g., '$12.99', 'EUR10.87', 'JPY1,450').

format_price.dart
1// Custom Function: formatPrice
2import 'package:intl/intl.dart';
3
4String formatPrice(
5 double baseAmount,
6 String targetCurrency,
7 Map<String, dynamic> rates,
8) {
9 final rate = (rates[targetCurrency] ?? 1.0).toDouble();
10 final converted = baseAmount * rate;
11
12 const zeroDecimal = {
13 'JPY', 'KRW', 'VND', 'CLP', 'ISK',
14 'UGX', 'RWF', 'BIF', 'GNF',
15 };
16
17 final decimals = zeroDecimal.contains(targetCurrency)
18 ? 0 : 2;
19
20 final formatter = NumberFormat.currency(
21 symbol: _currencySymbol(targetCurrency),
22 decimalDigits: decimals,
23 );
24
25 return formatter.format(converted);
26}
27
28String _currencySymbol(String code) {
29 const symbols = {
30 'USD': '\$', 'EUR': '\u20ac', 'GBP': '\u00a3',
31 'JPY': '\u00a5', 'CAD': 'CA\$', 'AUD': 'A\$',
32 'INR': '\u20b9', 'BRL': 'R\$',
33 };
34 return symbols[code] ?? code;
35}

Expected result: The Custom Function correctly converts a USD base price to any target currency and formats it with the proper symbol and decimal places.

4

Display converted prices on product and pricing pages

On every page where prices are displayed, add a Backend Query to fetch the `config/exchange_rates` document. For each price Text widget, replace the static dollar amount with a call to the formatPrice Custom Function, passing the base USD price, the App State selectedCurrency, and the rates map from the Backend Query. This means a product priced at $10.00 automatically displays as EUR9.21, GBP7.89, or JPY1,498 depending on the user's currency selection. Add a small Text note below prices stating 'Prices converted at daily rates' for transparency.

Expected result: All prices throughout the app display in the user's selected currency with proper formatting, updating whenever the currency DropDown changes.

5

Create the Stripe Checkout Session with the selected currency

Create a Cloud Function named createCheckoutSession that accepts the product details, amount in the base currency, and the target currency code. The function converts the amount using the stored exchange rates, then creates a Stripe Checkout Session with the currency parameter set to the target currency code (lowercase). For zero-decimal currencies, pass the amount as an integer without multiplying by 100. For standard currencies, multiply by 100 as Stripe expects amounts in the smallest unit (cents). Return the session URL. In FlutterFlow, call this Cloud Function on the Checkout button tap and launch the returned URL.

createCheckoutSession.js
1// Cloud Function: createCheckoutSession
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const stripe = require('stripe')(
5 functions.config().stripe.secret
6);
7
8exports.createCheckoutSession = functions.https
9 .onCall(async (data) => {
10 const { productName, baseAmountUsd,
11 currency, successUrl, cancelUrl } = data;
12
13 const ratesDoc = await admin.firestore()
14 .doc('config/exchange_rates').get();
15 const rates = ratesDoc.data().rates;
16 const rate = rates[currency.toUpperCase()] || 1;
17 const converted = baseAmountUsd * rate;
18
19 const zeroDecimal = ['jpy','krw','vnd','clp'];
20 const amount = zeroDecimal.includes(currency)
21 ? Math.round(converted)
22 : Math.round(converted * 100);
23
24 const session = await stripe.checkout.sessions
25 .create({
26 payment_method_types: ['card'],
27 line_items: [{
28 price_data: {
29 currency: currency.toLowerCase(),
30 product_data: { name: productName },
31 unit_amount: amount,
32 },
33 quantity: 1,
34 }],
35 mode: 'payment',
36 success_url: successUrl,
37 cancel_url: cancelUrl,
38 });
39
40 return { url: session.url };
41 });

Expected result: The Stripe Checkout page opens in the user's selected currency with the correctly converted amount, and payment is processed in that currency.

Complete working example

formatPrice Custom Function + Cloud Functions
1// Custom Function: formatPrice
2// Converts a base USD amount to the target currency
3// and formats with proper symbol and decimals.
4
5import 'package:intl/intl.dart';
6
7String formatPrice(
8 double baseAmount,
9 String targetCurrency,
10 Map<String, dynamic> rates,
11) {
12 final rate = (rates[targetCurrency] ?? 1.0).toDouble();
13 final converted = baseAmount * rate;
14
15 const zeroDecimalCurrencies = {
16 'JPY', 'KRW', 'VND', 'CLP', 'ISK',
17 'UGX', 'RWF', 'BIF', 'GNF', 'PYG',
18 };
19
20 final decimalDigits =
21 zeroDecimalCurrencies.contains(targetCurrency)
22 ? 0
23 : 2;
24
25 final symbol = _currencySymbol(targetCurrency);
26
27 final formatter = NumberFormat.currency(
28 symbol: symbol,
29 decimalDigits: decimalDigits,
30 );
31
32 return formatter.format(converted);
33}
34
35String _currencySymbol(String code) {
36 const symbols = {
37 'USD': '\$',
38 'EUR': '\u20ac',
39 'GBP': '\u00a3',
40 'JPY': '\u00a5',
41 'CAD': 'CA\$',
42 'AUD': 'A\$',
43 'INR': '\u20b9',
44 'BRL': 'R\$',
45 'CHF': 'CHF',
46 'CNY': '\u00a5',
47 'SEK': 'kr',
48 'NZD': 'NZ\$',
49 'MXN': 'MX\$',
50 'SGD': 'S\$',
51 'HKD': 'HK\$',
52 'NOK': 'kr',
53 'KRW': '\u20a9',
54 'ZAR': 'R',
55 };
56 return symbols[code] ?? code;
57}
58
59// Usage in FlutterFlow Text widget binding:
60// formatPrice(
61// product.priceUsd,
62// appState.selectedCurrency,
63// exchangeRatesDoc.rates,
64// )

Common mistakes

Why it's a problem: Hardcoding exchange rates in the app instead of fetching them from an API

How to avoid: Fetch rates daily from an exchange rate API via a scheduled Cloud Function and cache them in Firestore. The app always reads the latest cached rates.

Why it's a problem: Treating all currencies as two-decimal currencies when creating Stripe charges

How to avoid: Maintain a list of zero-decimal currency codes. For those currencies, pass the converted amount directly as an integer. For standard currencies, multiply by 100.

Why it's a problem: Converting prices on the client only without server-side verification

How to avoid: Always recalculate the converted amount in the Cloud Function using server-side exchange rates before creating the Stripe Checkout Session. Never trust the amount sent from the client.

Best practices

  • Store the user's currency preference in persisted App State so it survives navigation and app restarts
  • Display a 'Prices converted at daily rates' disclaimer so users understand prices are approximate
  • Use Intl.NumberFormat or Dart's NumberFormat.currency for locale-aware formatting with correct symbols and separators
  • Limit your supported currencies list to the 10-15 most relevant to your audience rather than all 135+ Stripe currencies
  • Show the last exchange rate update time so users know how fresh the conversion is
  • Round converted prices to avoid displaying amounts like EUR9.213847 — use toStringAsFixed(2) or the formatter's decimal settings
  • Test the payment flow with Stripe test mode for at least 3 different currencies including one zero-decimal currency like JPY

Still stuck?

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

ChatGPT Prompt

I need to add multi-currency support to my FlutterFlow app. I want a daily Cloud Function to fetch exchange rates, a currency selector DropDown, a Dart function to convert and format prices with proper currency symbols, and a Stripe Checkout Cloud Function that charges in the selected currency. Walk me through the full implementation.

FlutterFlow Prompt

Add a currency selector DropDown at the top of my pricing page. When the user selects a currency, convert all displayed prices from USD to the selected currency using exchange rates stored in Firestore. The checkout button should create a Stripe session in the selected currency.

Frequently asked questions

Does Stripe handle currency conversion automatically?

Stripe processes charges in whichever currency you specify in the Checkout Session. However, you must calculate the converted amount yourself using exchange rates. Stripe does not convert prices for you — it charges exactly the amount you pass.

How do I handle refunds in a different currency than the original charge?

Stripe refunds are always processed in the original charge currency. If a customer paid in EUR, the refund is issued in EUR. You do not need to handle currency conversion for refunds.

What exchange rate API should I use?

ExchangeRatesAPI.io and Open Exchange Rates both have free tiers suitable for daily updates. For real-time trading apps, use a paid service like CurrencyLayer or Fixer.io with minute-level updates.

Can I let sellers set custom prices per currency instead of using conversion?

Yes. Store a prices map on each product document with explicit amounts per currency (e.g., {USD: 10, EUR: 9, GBP: 8}). This gives sellers control over regional pricing without relying on exchange rates.

How do I handle currencies with different decimal separators like commas?

Dart's NumberFormat.currency handles locale-specific formatting including decimal separators. For German locale, EUR 10.99 displays as 10,99 EUR. Set the locale parameter based on the user's device or currency selection.

Can RapidDev help implement a global payment infrastructure?

Yes. RapidDev can build multi-currency payment systems with Stripe Connect for marketplace payouts, subscription billing in local currencies, tax calculation per region, and automated invoice generation.

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.