Receiving was sats-only. An invoice can now be quoted in any currency
the LNbits instance allows (~165 by default, narrowable via
`LNBITS_ALLOWED_CURRENCIES`), which is what a merchant pricing in EUR
actually needs.
LNbits does the conversion: `unit: "EUR"` with `amount: 5.50` yields an
ordinary BOLT11 invoice for the equivalent sats and records the original
denomination on the payment's `extra` (`fiat_currency`, `fiat_amount`,
`fiat_rate`). Nothing about the payment rail changes — the payer still
settles over Lightning.
- `useCurrencies` (base module) fetches the currency list and the
instance default once per process, de-duplicating concurrent callers.
It sits beside the other shared payment primitives because four
modules already duplicate this same `getCurrencies()` call; they can
adopt it later.
- Decimal places come from `Intl.NumberFormat`, not a hardcoded table,
so the input step is 0.01 for EUR and 1 for JPY, which has no minor
unit. Amount validation tracks the unit too.
- Switching unit clears a typed amount. Reinterpreting "10" from sats to
EUR would silently create an invoice for a wildly different value.
- The created invoice leads with what the payer was quoted (€5.50) and
shows the sat amount as secondary, noting the rate was fixed at
creation.
- A live "≈ N sats" preview uses the shared conversion composable. It is
best-effort: a failed lookup renders nothing and never blocks
creation, since LNbits converts authoritatively server-side.
- Sats stays the default, and the selector is hidden entirely if the
currency list is unavailable, so the wallet still works if the rate
service is down.
Verified in a headless browser against the live LNbits:
selector lists 166 options (sats + 165 currencies)
EUR 5.50 -> preview "≈ 7,301 sats" -> POST {amount: 5.5, unit: "EUR"}
-> invoice lnbc73010n... (7301 sats), shown as "€5.50"
with "Payable as 7,301 sats"
sats 250 -> POST {amount: 250, unit: "sat"}, no fiat line
JPY step/min 1; EUR step/min 0.01
switching EUR -> JPY clears the typed "12.34"
Fiat in the transaction history is deliberately left out; it applies to
all payments rather than just newly created ones, and is tracked
separately.