Multi-currency and FX quotes
Quick Reference
Why offer multi-currency
Your customers abandon checkout when they see prices in a foreign currency. Multi-currency solves this.
| Without multi-currency | With A55 multi-currency |
|---|---|
| Customer sees R$ 500.00 but pays in USD | Customer sees US$ 87.26 — the exact amount they pay |
| Customer does not know the real exchange rate | You show the mid-market rate with full transparency |
| Customer calls their bank to dispute the amount | No surprises — the price matches the charge |
| You lose cross-border sales | You convert international visitors into paying customers |
The business impact is measurable:
- Higher conversion: Customers buy when they understand the price in their own currency.
- Fewer chargebacks: Transparent pricing reduces "I did not recognize this amount" disputes.
- Competitive edge: You offer the same experience as global platforms like Amazon, Shopify, and Stripe.
- LATAM coverage: 8 currencies across 7 countries — USD, BRL, EUR, MXN, ARS, COP, CLP, PEN.
A merchant in Brazil can display prices in USD, EUR, or MXN to international buyers. A SaaS platform can show subscription prices in each customer's local currency. An e-commerce site can let buyers switch currencies at checkout.
How it works — three steps
The multi-currency flow has three steps. You call the FX endpoint, display the converted price, and create the charge.
Step 1 — Get the exchange rate
Call the FX endpoint to get the current mid-market rate between two currencies.
Endpoint: POST /api/v1/bank/wallet/fx/rate/
Request:
| Field | Type | Required | Description |
|---|---|---|---|
from_currency | string | Yes | Source currency (ISO 4217). Example: USD |
to_currency | string | Yes | Target currency (ISO 4217). Example: BRL |
Response:
| Field | Type | Description |
|---|---|---|
price | float | The exchange rate, rounded to 2 decimal places. When converting to a zero-decimal currency (CLP, COP), round the final amount to an integer |
- cURL
- Python
- JavaScript
curl -X POST 'https://core-manager.a55.tech/api/v1/bank/wallet/fx/rate/' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"from_currency": "USD",
"to_currency": "BRL"
}'
import requests
url = "https://core-manager.a55.tech/api/v1/bank/wallet/fx/rate/"
headers = {
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
}
payload = {
"from_currency": "USD",
"to_currency": "BRL",
}
response = requests.post(url, json=payload, headers=headers)
rate = response.json()["price"]
print(f"1 USD = {rate} BRL")
const response = await fetch(
"https://core-manager.a55.tech/api/v1/bank/wallet/fx/rate/",
{
method: "POST",
headers: {
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
from_currency: "USD",
to_currency: "BRL",
}),
}
);
const { price } = await response.json();
console.log(`1 USD = ${price} BRL`);
Response:
{
"price": 5.73
}
This means 1 USD = 5.73 BRL at this moment.
Step 2 — Display the converted price to your customer
Use the rate to show your customer the price in their currency. This happens in your application — no A55 API call is needed.
Example (BRL — 2 decimals): Your product costs US$ 100.00. The rate is 5.73.
Price in BRL = US$ 100.00 × 5.73 = R$ 573.00
| What the customer sees | Value |
|---|---|
| Product price | US$ 100.00 |
| Exchange rate | 1 USD = 5.73 BRL |
| Amount to pay | R$ 573.00 |
Example (CLP — zero-decimal): Your product costs US$ 100.00. The rate is 950.73.
Price in CLP = US$ 100.00 × 950.73 = CLP 95,073 (round to integer)
| What the customer sees | Value |
|---|---|
| Product price | US$ 100.00 |
| Exchange rate | 1 USD = 950.73 CLP |
| Amount to pay | CLP $95,073 |
For zero-decimal currencies, the amount you send in the charge request must be an integer. 95073, not 95073.00. The backend truncates decimal places for CLP and COP.
Display the original price AND the converted price. This builds trust. Your customer knows the exact rate and the exact amount their card or bank account will be charged.
Step 3 — Create the charge
Create the charge in the settlement currency (the currency of the wallet). The charge amount is the converted value from Step 2.
curl -X POST 'https://core-manager.a55.tech/api/v1/bank/wallet/charge/' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"wallet_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "11111111-1111-1111-1111-111111111111",
"payer_name": "John Smith",
"payer_email": "john@example.com",
"payer_tax_id": "12345678909",
"payer_cell_phone": "+5511999999999",
"installment_value": 573.00,
"currency": "BRL",
"due_date": "2026-04-30",
"description": "Order #12345 (US$ 100.00 at 5.73)",
"type_charge": "credit_card",
"installment_count": 1,
"payer_address": {
"street": "Av. Paulista",
"address_number": "1000",
"complement": "Sala 101",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postal_code": "01310-100",
"country": "BR"
}
}'
Store the rate in the charge description field (for example: "Order #12345 (US$ 100.00 at 5.73)"). This creates an audit trail for reconciliation and dispute resolution.
The charge response includes automatic multi-currency conversion:
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"local_currency": 573.00,
"currency": "BRL",
"usd_currency": 100.00,
"eur_currency": 91.45,
"type": "credit_card",
"status": "confirmed",
"installment_count": 1,
"installments": [
{
"local_currency": 573.00,
"currency": "BRL",
"usd_currency": 100.00,
"eur_currency": 91.45,
"due_date": "2026-04-30",
"status": "confirmed",
"installment_number": 1
}
]
}
Every charge response includes three currency values:
| Field | Description | Example |
|---|---|---|
local_currency | Amount in the wallet's settlement currency | 573.00 (BRL) |
usd_currency | Equivalent amount in US dollars | 100.00 (USD) |
eur_currency | Equivalent amount in euros | 91.45 (EUR) |
You can use these values for multi-currency reporting, reconciliation, and analytics dashboards without calling the FX endpoint again.
Complete integration example
This example shows the full flow — authenticate, get the FX rate, calculate the price, and create the charge.
- Python
- JavaScript
import requests
BASE = "https://core-manager.a55.tech/api/v1"
AUTH_URL = "https://smart-capital.auth.us-east-1.amazoncognito.com/oauth2/token"
# Authenticate
token_resp = requests.post(AUTH_URL, data={
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
}, headers={"Content-Type": "application/x-www-form-urlencoded"})
token = token_resp.json()["access_token"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# Step 1: Get the exchange rate
fx_resp = requests.post(f"{BASE}/bank/wallet/fx/rate/", json={
"from_currency": "USD",
"to_currency": "BRL",
}, headers=headers)
rate = fx_resp.json()["price"]
print(f"Exchange rate: 1 USD = {rate} BRL")
# Step 2: Calculate the converted price
product_price_usd = 100.00
ZERO_DECIMAL_CURRENCIES = {"CLP", "COP"}
target_currency = "BRL"
if target_currency in ZERO_DECIMAL_CURRENCIES:
charge_amount = round(product_price_usd * rate)
else:
charge_amount = round(product_price_usd * rate, 2)
print(f"Charge amount: {charge_amount}")
# Step 3: Create the charge in BRL
charge_resp = requests.post(f"{BASE}/bank/wallet/charge/", json={
"wallet_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "11111111-1111-1111-1111-111111111111",
"payer_name": "John Smith",
"payer_email": "john@example.com",
"payer_tax_id": "12345678909",
"payer_cell_phone": "+5511999999999",
"installment_value": charge_amount,
"currency": "BRL",
"due_date": "2026-04-30",
"description": f"Order #12345 (US$ {product_price_usd} at {rate})",
"type_charge": "credit_card",
"payer_address": {
"street": "Av. Paulista", "address_number": "1000",
"complement": "Sala 101", "neighborhood": "Bela Vista",
"city": "São Paulo", "state": "SP",
"postal_code": "01310-100", "country": "BR",
},
}, headers=headers)
charge = charge_resp.json()
print(f"Charge UUID: {charge['charge_uuid']}")
print(f"Local (BRL): {charge['local_currency']}")
print(f"USD: {charge['usd_currency']}")
print(f"EUR: {charge['eur_currency']}")
const BASE = "https://core-manager.a55.tech/api/v1";
const AUTH_URL = "https://smart-capital.auth.us-east-1.amazoncognito.com/oauth2/token";
// Authenticate
const tokenResp = await fetch(AUTH_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "YOUR_CLIENT_ID",
client_secret: "YOUR_CLIENT_SECRET",
}),
});
const { access_token } = await tokenResp.json();
const headers = {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
};
// Step 1: Get the exchange rate
const fxResp = await fetch(`${BASE}/bank/wallet/fx/rate/`, {
method: "POST",
headers,
body: JSON.stringify({ from_currency: "USD", to_currency: "BRL" }),
});
const { price: rate } = await fxResp.json();
console.log(`Exchange rate: 1 USD = ${rate} BRL`);
// Step 2: Calculate the converted price
const productPriceUsd = 100.0;
const ZERO_DECIMAL = new Set(["CLP", "COP"]);
const targetCurrency = "BRL";
const chargeAmount = ZERO_DECIMAL.has(targetCurrency)
? Math.round(productPriceUsd * rate)
: Math.round(productPriceUsd * rate * 100) / 100;
console.log(`Charge amount: ${chargeAmount}`);
// Step 3: Create the charge in BRL
const chargeResp = await fetch(`${BASE}/bank/wallet/charge/`, {
method: "POST",
headers,
body: JSON.stringify({
wallet_uuid: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
merchant_id: "11111111-1111-1111-1111-111111111111",
payer_name: "John Smith",
payer_email: "john@example.com",
payer_tax_id: "12345678909",
payer_cell_phone: "+5511999999999",
installment_value: chargeAmount,
currency: "BRL",
due_date: "2026-04-30",
description: `Order #12345 (US$ ${productPriceUsd} at ${rate})`,
type_charge: "credit_card",
payer_address: {
street: "Av. Paulista", address_number: "1000",
complement: "Sala 101", neighborhood: "Bela Vista",
city: "São Paulo", state: "SP",
postal_code: "01310-100", country: "BR",
},
}),
});
const charge = await chargeResp.json();
console.log(`Charge UUID: ${charge.charge_uuid}`);
console.log(`Local (BRL): ${charge.local_currency}`);
console.log(`USD: ${charge.usd_currency}`);
console.log(`EUR: ${charge.eur_currency}`);
Supported currencies
The FX API supports 8 currencies across 7 LATAM countries plus USD and EUR, producing 56 conversion pairs.
| Code | Currency | Country / Region | Decimals | Wallet supported |
|---|---|---|---|---|
USD | US Dollar | United States | 2 | — |
BRL | Brazilian Real | Brazil | 2 | Yes |
EUR | Euro | Eurozone | 2 | — |
MXN | Mexican Peso | Mexico | 2 | Yes |
ARS | Argentine Peso | Argentina | 2 | Yes |
COP | Colombian Peso | Colombia | 0 | — |
CLP | Chilean Peso | Chile | 0 | Yes |
PEN | Peruvian Sol | Peru | 2 | — |
When converting to CLP or COP, the final charge amount must be an integer. After multiplying by the FX rate, round to the nearest whole number using round() (not floor()). Example: USD 100.00 × 950.73 = CLP 95,073 (not 95073.00).
Key cross-border corridors
| Pair | Use case |
|---|---|
| USD → BRL | US companies selling to Brazilian customers |
| USD → MXN | US companies selling to Mexican customers |
| EUR → BRL | European companies entering Brazil |
| BRL → USD | Brazilian SaaS, exports, remittances |
| USD → CLP | US companies selling to Chilean customers |
| USD → ARS | US companies selling to Argentine customers |
| USD → COP | US companies selling to Colombian customers |
| USD → PEN | US companies selling to Peruvian customers |
Rate source and freshness
| Property | Value |
|---|---|
| Rate source | Mid-market (interbank) — the Bid/Ask midpoint as reported by third-party data sources, with zero A55 markup |
| Cache window | ~17 minutes (1,000 seconds) |
| Data sources | Cascading fallback — the system queries multiple external providers in sequence to ensure rate availability |
| Precision | Rate rounded to 2 decimal places; final charge amount follows currency decimals (0 for CLP/COP, 2 for all others) |
| API availability | 24/7 — the endpoint responds at all times, including weekends and holidays |
| Weekend rates | Outside forex market hours (weekends, holidays), data sources return the last available trading rate |
The mid-market rate is the midpoint between the buy and sell price in the interbank market. It has zero markup. Central banks, regulators, and fintech companies use it as the fairest reference for currency conversion. The Forex market trades over US$ 9.6 trillion per day (BIS 2025) — no other financial market offers this level of liquidity and price accuracy.
How card networks and issuers add fees to the exchange rate
The A55 FX endpoint returns the mid-market rate with zero markup. But between A55 and the cardholder's credit card statement, two additional layers add fees. Understanding this chain helps you distinguish expected bank fees from integration errors.
The conversion chain
When a US cardholder pays on a BRL merchant site, five steps determine the final statement amount:
| Step | Actor | What happens |
|---|---|---|
| 1 | A55 FX endpoint | Returns mid-market rate (for example, 5.50 BRL/USD) |
| 2 | Merchant | Charges R$ 550.00 in BRL (settlement currency) |
| 3 | Card network (Visa or Mastercard) | Converts R$ 550.00 to USD at their published daily rate — mid-market plus network markup (~1%) |
| 4 | Issuing bank | Adds foreign transaction fee (0–3%, most cards charge 1–2%) |
| 5 | Cardholder statement | Shows the final amount: approximately US$ 101–104 |
Fee breakdown by layer
| Layer | What it is | Who pays | Typical range |
|---|---|---|---|
| A55 FX quote | Mid-market interbank rate, no markup | — | 0% |
| Card network assessment | Visa ISA or Mastercard Cross-Border Assessment | Merchant (passed through processor) | 0.6–1.4% |
| Network currency conversion | Embedded in the card network's published exchange rate | Cardholder | ~1% |
| Issuer foreign transaction fee | Fee charged by the cardholder's bank for foreign currency transactions | Cardholder | 0–3% |
| Total impact on cardholder | Difference between mid-market rate and statement amount | Cardholder | ~1.6–4.4% |
Visa vs Mastercard fee comparison
| Visa | Mastercard | |
|---|---|---|
| Cross-border assessment (merchant side) | ISA: 1.00% (USD settlement) / 1.40% (non-USD) | 0.60% (USD settlement) / 1.00% (non-USD) |
| Conversion markup (cardholder side) | ~1% over mid-market | ~1% over mid-market |
| Rate application timing | Typically at settlement (1–2 business days after authorization) | At authorization (point of sale) |
| Published rate checker | Visa exchange rate calculator | Mastercard currency converter |
What you control vs what you cannot control
| You control | You cannot control |
|---|---|
| When to fetch the FX rate (freshness) | The card network's published exchange rate |
| The BRL amount submitted in the charge | The issuer's foreign transaction fee (0–3%) |
| The price and rate displayed to the customer | Whether the cardholder's card charges 0% or 3% FX fee |
| The audit trail stored for dispute defense | The conversion date the network applies |
Real-world scenarios
| Scenario | Expected statement amount | Risk |
|---|---|---|
| Fresh rate + card with 0% foreign fee | ~US$ 101.00 | None |
| Fresh rate + card with 2% foreign fee | ~US$ 103.00 | None |
| Fresh rate + card with 3% foreign fee | ~US$ 104.00 | None — within expected range |
| Stale rate (45 min old) + 2% foreign fee | ~US$ 106–108 | High — Mastercard 4834 chargeback |
The first three rows show expected, non-disputable variance caused by bank fees. The last row shows the stale-rate problem compounded by fees — this combination produces the chargebacks.
Conversion timing and weekends
Mastercard applies the exchange rate at the time of authorization (when the cardholder pays). Visa typically applies the rate at the time of settlement (1–2 business days later). This means:
- Mastercard charges: The rate applied is very close to the rate at the time of purchase.
- Visa charges: The rate may shift between authorization and settlement. In volatile markets, this creates additional variance.
For weekend transactions, both networks use the last available Friday rate (New York 5:00 PM EST) until Forex markets reopen Sunday evening. Weekend transactions carry higher risk of rate divergence if the market moves significantly over the 48-hour closure.
Rate freshness best practices
Leading payment platforms (Stripe, Wise, Adyen) distinguish between two uses of exchange rates. The A55 FX endpoint serves both, but the refresh strategy differs.
Display pricing — product pages and catalogs
When showing multi-currency prices on product listing pages or pricing pages, you can cache the rate server-side and refresh periodically.
| Recommendation | Details |
|---|---|
| Refresh frequency | Call the FX endpoint every 10–15 minutes |
| Caching | Cache the rate server-side to avoid an API call on every page load |
| Price disclaimer | Label prices as "approximate — final amount determined at checkout" |
Transactional checkout — creating actual charges
When the customer confirms payment and you create the charge, use the freshest rate possible to ensure the displayed amount matches the charged amount.
| Recommendation | Details |
|---|---|
| Refresh timing | Re-fetch the rate when the customer clicks "Confirm payment" |
| Display the rate | Show the original amount, exchange rate, and converted amount on the payment confirmation page |
| Record the rate | Store the rate in the charge description field and in your database |
| Amount consistency | Ensure the amount shown to the customer matches the installment_value exactly |
The exchange rate remains constant within the cache window (~17 minutes). If your customer spends time on the checkout page, re-fetch the rate before creating the charge. Using a stale rate may cause the charged amount to differ from the displayed amount.
How a stale rate causes chargebacks — a real scenario
If you are new to payments, this section explains why rate freshness matters. Here is a real chargeback scenario:
Scenario: Your product costs US$ 100.00. The customer pays with a USD credit card. Your wallet settlement currency is BRL.
| Time | Event | Rate | Amount |
|---|---|---|---|
| 10:00 AM | You fetch the rate and display the price | 1 USD = 5.73 BRL | Page shows "R$ 573.00" |
| 10:45 AM | Customer clicks "Confirm" — you create the charge using the 10:00 AM rate | Real rate is now 5.50 | You submit R$ 573.00 |
| 10:45 AM | Issuing bank converts R$ 573.00 back to USD at current rate | Issuer uses 5.50 | Statement shows US$ 104.18 |
| Next day | Cardholder checks statement | — | "I agreed to pay US$ 100.00, why was I charged US$ 104.18?" |
| 3 days later | Cardholder files dispute with issuer | — | Mastercard reason code 4834: "Transaction Amount Differs" |
What happened: You calculated the BRL amount using a 45-minute-old rate (5.73). But the cardholder's issuing bank converted BRL back to USD at the current rate (5.50), resulting in US$ 104.18 instead of US$ 100.00. The US$ 4.18 difference triggered the chargeback.
The fix: At the moment the customer clicks "Confirm" (10:45 AM), re-call the FX endpoint to get the current rate (5.50), calculate R$ 550.00, and submit R$ 550.00. The issuer converts back to approximately US$ 100.00 — the statement matches the displayed price, no dispute.
Even with a perfectly timed rate, the cardholder's issuing bank adds its own FX markup (typically 1–3% over mid-market). This is standard across Visa and Mastercard networks — the cardholder's bank agreement covers this markup. A charge that converts to exactly US$ 100.00 at mid-market may appear as US$ 101.00–103.00 on the statement. This small, expected variance rarely triggers disputes. The critical issue — and the one that causes Mastercard 4834 chargebacks — is the large discrepancy caused by stale rates, as shown in the scenario above.
Fetch the rate at the last possible moment before creating the charge. The shorter the gap between fetching the rate and creating the charge, the smaller the discrepancy on the cardholder's statement.
Why re-fetching at confirm is the best approach
Three strategies exist for preventing FX-related chargebacks in cross-border payments:
| Strategy | How it works | Available in A55 |
|---|---|---|
| Re-fetch at confirm | Get the freshest mid-market rate before creating the charge | Yes — call the FX endpoint at the last moment |
| DCC (Dynamic Currency Conversion) | Charge the cardholder in their own currency so the issuer does not convert | No — requires DCC certification and multi-currency settlement |
| Rate lock | Lock the quoted rate for a guaranteed period | No — the FX endpoint returns a real-time quote, not a locked rate |
Re-fetching at confirm minimizes rate drift (the largest source of chargebacks). Combined with transparent disclosure and record-keeping, this is the industry-standard approach used by Stripe, Adyen, and Wise for merchants who settle in a single currency.
Chargeback prevention checklist
For every cross-border charge, verify:
- You fetched a fresh rate immediately before creating the charge
- The payment confirmation page shows the original amount, exchange rate, and converted amount together
- The payment confirmation page includes a note: "Final amount on your card statement may vary slightly due to your bank's exchange rate"
- The rate and original amount are stored in the charge
descriptionfield - The
installment_valueexactly matches the amount displayed to the customer - Your database records: rate value, fetch timestamp, displayed amount, charged amount
- For zero-decimal currencies (CLP, COP), the amount is an integer
Use cases
| Scenario | How to use the FX endpoint |
|---|---|
| Cross-border checkout | Get the rate, display the converted price to the buyer before they confirm payment |
| Dynamic pricing | Update product prices on your website based on current exchange rates |
| Multi-currency dashboard | Convert all transaction values to USD or EUR for consolidated reporting |
| Financial reconciliation | Compare the rate at transaction time with the rate at settlement time |
| Subscription pricing | Show subscribers their monthly charge in their local currency |
| Invoice generation | Include the exchange rate and both currency amounts on invoices |
Important notes
For currencies with capital controls — particularly the Argentine Peso (ARS) — the mid-market rate may differ from official or parallel rates used locally. The rate returned reflects the international interbank market.
The FX endpoint returns the current interbank market rate for display and calculation. It does not lock the rate. The actual conversion applied to a charge depends on the acquirer at the time the transaction is processed. To minimize discrepancy, fetch a fresh quote immediately before creating the charge.
For every cross-border charge, record: the time you fetched the rate, the rate value, the amount displayed to the customer, and the amount charged. Include the rate in the charge description field (for example: "Order #12345 (US$ 100.00 at 5.73)") and persist the full record in your system. This data is essential for reconciliation and dispute resolution.