feat(machine): stamp fiat_amount on Payment.extra (bill-validator truth)

Follow-up to 138cd1a. Adds the customer-transacted fiat amount as a
top-level field on the kind-21000 Payment.extra payload, sourced
directly from `context.fiatCents` (the bill validator/dispenser
ledger — canonical record of what bills entered/exited the machine).

Why a separate field instead of letting the consumer divide:

  principal_sats / exchange_rate

…is close but not equal to the bill-counted truth. It assumes the
commission was paid entirely in BTC (true today on cash-out) and
introduces sub-cent rounding from `floor()` in the principalSats
calc. The bill-validator number doesn't have those problems and is
the only authoritative record of what cash actually changed hands.

Belongs with the rest of the #44 metadata. Spec didn't enumerate it
originally; adding now before the field name locks in across the
fleet.
This commit is contained in:
Padreug 2026-05-16 16:38:15 +02:00
commit 997968ae06

View file

@ -795,11 +795,19 @@ function createATMServices(
source: 'bitspire',
type: 'cash_out',
txid: context.txid,
// `fiat_amount` is the customer's transaction value — the bills
// that physically went into (cash-in) or out of (cash-out) the
// machine via the validator/dispenser. Sourced from
// `context.fiatCents` directly; never re-derive downstream from
// sats × rate (those have rounding edges and assume commission
// lives in BTC, which is true today but is exactly the kind of
// invariant we don't want a consumer baking in).
fiat_amount: context.fiatCents / 100,
currency: context.currency,
principal_sats: principalSats,
fee_sats: feeSats,
fee_percent: feePercent,
exchange_rate: context.exchangeRate,
currency: context.currency,
// bills/cassettes deferred — they're meaningful for cash-in
// and for partial-dispense reconciliation, neither of which
// is wired on the satmachineadmin side yet (#22, #3).