`useLightningPayment` writes feeMsat onto the paid order, but `Order`
never declared it. `updateOrder` takes `Partial<Order>`, and the write
goes through an inferred variable, so the excess-property check never
fired — the value landed in the store invisible to the type system and
unreadable by any consumer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes#170.
LNbits payment payloads carry `fee`, signed millisats like `amount`.
There is no `fee_msat` field, so three modules read `undefined`:
PaymentService.PaymentResult declared `fee_msat: number` — a field the
API never sends — so the compiler vouched for a number that was always
undefined, and the lie propagated to every consumer.
market/useLightningPayment persisted `feeMsat: undefined` onto paid
orders (on a property the Order type does not even declare).
events/LnbitsPaymentProvider had `data.fee_msat ?? 0`, whose fallback
masked the miss so every payment recorded a zero fee.
`payInvoice` now normalizes at the boundary instead of returning the raw
payload: it maps `fee` to a positive `feeMsat`. Doing it once there means
no call site can pick the wrong field name or forget that outgoing
amounts are signed. `PaymentResult.feeMsat` replaces the phantom field,
so the type finally matches what callers receive.
Verified against a live pay-invoice response, plus a synthetic payment
carrying a real routing fee (internal LNbits transfers are fee-free, so
the live case cannot exercise a non-zero fee):
live response fee present: True fee_msat present: False
synthetic -1234 msat
old (events) -> 0 fee silently lost
old (market) -> undefined
new (both) -> 1234 positive magnitude
Nothing read these values downstream, so no totals were miscomputed; the
fee was simply recorded as missing or zero.