Status: DraftCreated: 2025-11-07Last Updated: 2025-11-10
Multi-Currency Support in Polar
Problem Statement
Historically, Polar has operated using a single base currency (USD) for all transactions and accounting. However, as our user base expands globally, there is a growing need to support multiple currencies natively within the platform. This design document will explore the requirements, challenges, and proposed solutions for implementing multi-currency support in Polar.Background
Key terms
- Presentment Currency: The currency in which a payment/transaction is done, i.e. the price shown on the checkout page and customer’s invoice.
- Settlement Currency: The currency in which funds are shown on the merchant’s side.
- Payout Currency: The currency in which funds are paid out to the merchant’s bank account.
Current state
Currently, we only support USD as presentment and settlement currency. We support multiple payout currencies: merchants can connect any bank account supported by Stripe Connect and receive payouts in that currency. Stripe handles the currency conversion internally.Comparison with Stripe
Stripe has both the concepts of Presentment and Settlement currencies, with the following capabilities, given that Polar runs a Stripe US account.Presentment Currency: 135+ currencies supported
In the current context, if we trigger a payment in a currency different than USD, Stripe will implicitly convert the funds to USD before settling them to our Stripe account. That said, they still show it up in the transactions list as the original currency. However, balance and analytics are all in USD. Ref: https://docs.stripe.com/currencies#presentment-currenciesSettlement Currency
For US-based Stripe accounts, Stripe allows us to wire bank account for the following settlement currencies:- CAD
- EUR
- GBP
Proposed Solutions
Depending on how far we want to go, given the constraints we have with Stripe, we can consider several scenarios detailed below.Scenario 1: Multiple Presentment Currencies, USD Settlement currency
In this scenario, we would allow merchants to set prices in multiple currencies, and customers would be able to pay in their local currency. However, all funds would still be settled in USD.Implementation details
- Allow to set other presentment currencies than
usdwhen creating products (only a validation constraint to change).
- To allow to display and track the presentment currency in the dashboard, add the following columns to Payment, Order, OrderItem, Refund and Subscription models:
presentment_currency:strpresentment_amount:int
- When receiving a payment or refund from Stripe, we’ll need to get the converted amount in
usdStripe calculated (from the balance transaction), to fill our original amount fields. - Prefer
presentment_amountandpresentment_currencywhen displaying amounts in the dashboard.
usd, analytics and account transactions are working unchanged.
Implementation B
- Store presentment amount and currency in the current amount fields.
- When receiving a payment or refund from Stripe, we’ll need to get the converted amount in
usdStripe calculated (from the balance transaction), to impact the merchant’s Account balance. - Revamp metrics so amount are always computed in
usd, directly from the Account transactions amount.
Scenario 2: Multiple Presentment Currencies, One Settlement Currency
In this scenario, we would allow merchants to set prices in multiple currencies, and customers would be able to pay in their local currency. However, merchants would choose a single settlement currency for their account.Implementation details
Similar to Scenario 1, but with the ability to set an Organization’s settlement currency.- Add
currencyfield to Organization model.- Only currencies supported by Stripe for settlement can be chosen here (for US-based accounts: USD, CAD, EUR, GBP); provided Polar has enabled the settlement currency in Stripe account (with a proper bank account attached).
- Allow to set other presentment currencies than
usdwhen creating products (only a validation constraint to change).
- To allow to display and track the presentment currency in the dashboard, add the following columns to Payment, Order, OrderItem, Refund and Subscription models:
presentment_currency:strpresentment_amount:int
- When receiving a payment or refund from Stripe, we’ll need to get the converted amount in
Organization.currencyStripe calculated (from the balance transaction), to fill our original amount fields. - Prefer
presentment_amountandpresentment_currencywhen displaying amounts in the dashboard.
Organization.currency, analytics and account transactions are working unchanged.
Implementation B
- Store presentment amount and currency in the current amount fields.
- When receiving a payment or refund from Stripe, we’ll need to get the converted amount in
usdStripe calculated (from the balance transaction), to impact the merchant’s Account balance. - Revamp metrics so amount are always computed in
Organization.currency, directly from the Account transactions amount.
Scenario 3: Single Presentment and Settlement Currency per Merchant
In this scenario, we would allow merchants to choose a single presentment and settlement currency for their account. All prices would be shown in that currency, and all funds would be settled in that currency. For example, if they choose EUR, all prices would be shown in EUR, and all funds would be settled in EUR.Implementation details
This is somehow a generalization of our current single-currency model.- Add
currencyfield to Organization model.- Only currencies supported by Stripe for settlement can be chosen here (for US-based accounts: USD, CAD, EUR, GBP); provided Polar has enabled the settlement currency in Stripe account (with a proper bank account attached).
Scenario 4: Multiple Presentment and Settlement Currencies
In this scenario, we would allow merchants to set prices in multiple currencies, and customers would be able to pay in their local currency. Merchants would also be able to choose multiple settlement currencies for their account. Implementation details This is the most complex scenario, as it requires significant changes to our data model and business logic. In particular, an Organization should be able to have several Account (one per settlement currency). It means then we’ll need to extract the payout logic from that (since merchants will still likely want to have a single bank account for payouts, regardless of settlement currency).- Allow to create multiple Account in different settlement currencies for an Organization.
- Only currencies supported by Stripe for settlement can be chosen here (for US-based accounts: USD, CAD, EUR, GBP); provided Polar has enabled the settlement currency in Stripe account (with a proper bank account attached).
- Create a new PayoutAccount entity that’ll hold all the payout information (Stripe Connect). Payout entity will be linked to this new PayoutAccount entity as well.
- Add a mandatory
currencyparameter when computing metrics or generate metrics in every enabled settlement currency. - Allow to set other presentment currencies than
usdwhen creating products (only a validation constraint to change).
- When receiving a payment or refund from Stripe, match the currency with one of the Organization’s settlement currencies, and impact it on the corresponding Account.
- To allow to display and track the presentment currency in the dashboard, add the following columns to Payment, Order, OrderItem, Refund and Subscription models:
presentment_currency:strpresentment_amount:int
- When receiving a payment or refund from Stripe: if it matches one of the Organization’s settlement currencies, impact it on the corresponding Account; otherwise, impact it on the currency which Stripe converted it to (most likely
usd).- It means Organization will probably always have a
usdAccount by default.
- It means Organization will probably always have a
- Prefer
presentment_amountandpresentment_currencywhen displaying amounts in the dashboard.
- Store presentment amount and currency in the current amount fields.
- When receiving a payment or refund from Stripe, if it matches one of the Organization’s settlement currencies, impact it on the corresponding Account; otherwise, impact it on the currency which Stripe converted it to (most likely
usd). - Revamp metrics so amount are always computed using the amounts from Account’s transaction.
Scenario Comparison
When we look at those scenarios, one interesting thing to note is that Scenario 1, 2 and 4 Variant 2 are relatively similar from an implementation standpoint. We can almost already see the iterations there:- Scenario 1: multiple presentment currencies, single settlement currency (usd)
- Scenario 2: multiple presentment currencies, single settlement currency (configurable)
- Scenario 4 Variant 2: multiple presentment currencies, multiple settlement currencies
Implementation A or B
In those three scenarios, I’ve described two possible implementation strategies: Implementation A (adding presentment fields) and Implementation B (metrics from Account transactions). Implementation A has the advantage of being simpler to implement, as we don’t need to revamp all the metrics calculations. The drawback is that we’ll need to introduce a new set of fields in our models, and use them a bit everywhere in the dashboard and in lot of places in the codebase (e.g. refunds creation or invoice generation). It feels a bit weird to have those fields as “second-class citizens”. Implementation B feels cleaner from a data model standpoint and consistency: amounts are shown in the currency the customer paid it. Most of the code we have today should naturally work out-of-the-box (except a few currency formatting quirks). However, it requires to revamp all the metrics calculations to compute them from Account transactions, which is a significant amount of work and might lead to poorer performance as we need to do more joins.Recommendation
Given our current time constraints, I would recommend to go with Scenario 1 as a first step. It would allow us to deliver multi-currency support relatively quickly, while still providing significant value to our merchants. Then, we can quite quickly iterate to Scenario 2. Longer-term, we still have the possibility to expand easily towards Scenario 4 Variant 2 if we see a strong demand for it. From a technical standpoint, I would recommend to go with Implementation B. It feels cleaner and more consistent, and will save us from having to maintain two sets of amount fields in our models; even if it requires more work upfront.Technical Considerations and Open Questions
Polar fees
Our fees is a percentage + fixed amount per payment. We should define if we want to compute it:- On the presentment amount
- On the settlement amount
Comparison with Stripe
Here is what Stripe does:- If the presentment currency is not supported as settlement currency, Stripe first converts to the settlement currency (usd for us), then computes the fee on that basis, and add a conversion fee percentage.
- Conversion: 12 EUR -> 13.89 USD
- Stripe fee: 13.89 * 2.9% + 0.30 = 0.70 USD
- Conversion fee: 13.89 * 1% = 0.14 USD
- Total fee: 0.84 USD
- If the presentment currency is supported as settlement currency, Stripe computes the fee directly in that currency.
- Amount: 60 EUR
- Stripe fee: 60 * 2.9% + 0.30 = 2.04 EUR

