From 997968ae06be2c2cac64eab9bbec7c524f943ef0 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sat, 16 May 2026 16:38:15 +0200 Subject: [PATCH] feat(machine): stamp fiat_amount on Payment.extra (bill-validator truth) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- apps/machine/src/services/lightning.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/machine/src/services/lightning.ts b/apps/machine/src/services/lightning.ts index a2e237f..c4f4d51 100644 --- a/apps/machine/src/services/lightning.ts +++ b/apps/machine/src/services/lightning.ts @@ -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).