# Multi-Currency Support

The POS system supports **31 fiat currencies**. Merchants can configure which currencies their terminals accept, and cashiers can enter amounts in any enabled currency. The system automatically converts the fiat amount to crypto at the current exchange rate.

***

## How It Works

When a customer wants to pay in a local currency (e.g., Euro, Japanese Yen, British Pound), the cashier enters the amount in that currency. The system converts it through this chain:

```
Cashier enters: 50 EUR
    |
    v
Exchange rate (cached): 1 USD = 0.92 EUR
    |
    v
USD equivalent: 50 / 0.92 = $54.35
    |
    v
Oracle price: $54.35 / $3,500 per ETH = 0.01553 ETH
    |
    v
Payment created for 0.01553 ETH
```

The customer only sees the fiat amount and the crypto amount. The USD intermediate step is invisible.

***

## Supported Currencies

31 currencies from the European Central Bank (ECB):

| Code | Currency           | Zero-Decimal |
| ---- | ------------------ | :----------: |
| AUD  | Australian Dollar  |              |
| BGN  | Bulgarian Lev      |              |
| BRL  | Brazilian Real     |              |
| CAD  | Canadian Dollar    |              |
| CHF  | Swiss Franc        |              |
| CNY  | Chinese Yuan       |              |
| CZK  | Czech Koruna       |              |
| DKK  | Danish Krone       |              |
| EUR  | Euro               |              |
| GBP  | British Pound      |              |
| HKD  | Hong Kong Dollar   |              |
| HUF  | Hungarian Forint   |      Yes     |
| IDR  | Indonesian Rupiah  |              |
| ILS  | Israeli Shekel     |              |
| INR  | Indian Rupee       |              |
| ISK  | Icelandic Krona    |      Yes     |
| JPY  | Japanese Yen       |      Yes     |
| KRW  | South Korean Won   |      Yes     |
| MXN  | Mexican Peso       |              |
| MYR  | Malaysian Ringgit  |              |
| NOK  | Norwegian Krone    |              |
| NZD  | New Zealand Dollar |              |
| PHP  | Philippine Peso    |              |
| PLN  | Polish Zloty       |              |
| RON  | Romanian Leu       |              |
| SEK  | Swedish Krona      |              |
| SGD  | Singapore Dollar   |              |
| THB  | Thai Baht          |              |
| TRY  | Turkish Lira       |              |
| USD  | US Dollar          |              |
| ZAR  | South African Rand |              |

**Zero-decimal currencies** (JPY, KRW, ISK, HUF) have no fractional units. When a cashier enters `1500` on a JPY terminal, it means 1,500 yen -- not 15.00.

***

## Exchange Rates

* **Source:** European Central Bank via the [Frankfurter API](https://frankfurter.dev)
* **Fallback:** [Fawaz Ahmed Currency API](https://github.com/fawazahmed0/exchange-api) (CDN-backed)
* **Update frequency:** Every 30 minutes (automated via Convex cron job)
* **Staleness protection:** If rates are older than 48 hours (both APIs down for extended time), payments are blocked with an error message
* **Storage:** Cached in Convex database (`exchangeRates` table), single row updated on each fetch

Merchants can see the current exchange rates, when they were last updated, and the data source on the **Dashboard > POS > Currencies** page.

***

## Setting Up Currencies

### Account-Level (all terminals)

1. Go to **Dashboard > POS > Currencies**
2. Set your **default currency** using the searchable dropdown
3. Toggle ON the currencies you want to enable
4. Click **Save Currency Settings**

The default currency is pre-selected when creating new terminals. Only enabled currencies are available in terminal configuration.

### Per-Terminal

Each terminal can override the account-level settings:

1. Go to **Dashboard > POS > Terminals**
2. Click **Edit** on a terminal (or configure during creation)
3. In the **Currency** section:
   * Set the **default currency** for this terminal
   * Toggle **Allow cashier to switch currency** if you want cashiers to change currencies per-transaction
   * If switching is enabled, select which currencies the cashier can choose from
4. Click **Save**

> **Example:**
>
> You have a shop in Paris with occasional tourists.

| Setting            | Value              |
| ------------------ | ------------------ |
| Default currency   | EUR                |
| Allow switching    | Yes                |
| Allowed currencies | EUR, GBP, USD, JPY |

> The cashier sees EUR by default but can tap GBP, USD, or JPY for a tourist.

***

## Cashier Experience

### Single Currency (no switching)

If the terminal has a fixed currency (e.g., EUR), the cashier sees:

1. The **flag icon** and **currency name** above the amount display
2. The **keypad** with locale-aware formatting (e.g., `EUR 0,00` for Euro)
3. Entering `5000` shows `EUR 50,00`

### Multiple Currencies (switching enabled)

If the terminal allows currency switching:

1. A row of **currency pills** appears above the amount display
2. Each pill shows the **country flag** and **currency code** (e.g., flag + `EUR`)
3. Tapping a different currency:
   * Switches the amount display to that currency's formatting
   * Resets the entered amount to zero
   * Changes the keypad behavior (e.g., JPY uses whole numbers)

### Zero-Decimal Currencies

For JPY, KRW, ISK, and HUF:

* The keypad enters **whole units** (no decimal point)
* Typing `1500` means 1,500 yen (not 15.00)
* The maximum amount is higher (up to 9,999,999,999)

For all other currencies:

* The keypad uses the **cents system** (same as USD)
* Typing `5000` means 50.00

### Minimum Amount

The minimum payment amount is **$0.10 USD equivalent** in any currency. The system calculates the minimum in the selected currency using the current exchange rate and shows it to the cashier if they try to enter an amount that's too low.

***

## Payment Records

When a non-USD currency is used, the payment record stores:

| Field           | Description                  | Example |
| --------------- | ---------------------------- | ------- |
| `fiatCurrency`  | ISO 4217 currency code       | `EUR`   |
| `fiatAmount`    | Amount in the fiat currency  | `50.00` |
| `usdEquivalent` | USD value at time of payment | `54.35` |
| `exchangeRate`  | Rate used (fiat per 1 USD)   | `0.92`  |

The payment description also uses locale-aware formatting: `POS Payment - EUR 50,00` instead of `POS Payment - $50.00`.

***

## FAQ

### What happens if the exchange rate API is down?

The system caches rates in the database. If both the primary (Frankfurter) and fallback (Fawaz Ahmed CDN) APIs are unreachable:

* **Under 48 hours:** The last cached rate is used silently. Rates are updated at most daily by the ECB, so short outages have minimal impact.
* **Over 48 hours:** Payments in non-USD currencies are blocked. USD payments still work (no conversion needed).

### Can I use a currency not in the list?

Currently only the 31 ECB currencies are supported. These cover the majority of global retail transactions.

### Does the customer see the fiat amount?

Yes. The QR code / payment screen shows both the original fiat amount (e.g., `EUR 50,00`) and the crypto amount (e.g., `0.01553 ETH`). The USD intermediate step is not shown.

### What if the exchange rate changes between entering the amount and creating the payment?

The rate is locked when the cashier clicks "Create Payment". The rate used is stored on the payment record for audit purposes. Rates update at most every 30 minutes, so the difference is typically negligible for retail transactions.

### How do I see which currency was used for a payment?

In the merchant dashboard (**Dashboard > POS > Activity**), each payment shows the original fiat amount and currency alongside the crypto amount.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.makapay.io/point-of-sale-pos/multi-currency.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
