diff --git a/src/core/services/PaymentService.ts b/src/core/services/PaymentService.ts index 0021e46..d6935c5 100644 --- a/src/core/services/PaymentService.ts +++ b/src/core/services/PaymentService.ts @@ -5,7 +5,15 @@ import { toast } from 'vue-sonner' export interface PaymentResult { payment_hash: string - fee_msat: number + /** + * Routing fee in millisats, always positive. + * + * LNbits returns this as `fee`, not `fee_msat`, and signs it negative for + * outgoing payments (like `amount`). This interface previously declared + * `fee_msat: number`, a field the API never sends, so every consumer read + * `undefined` while the compiler vouched for a number. + */ + feeMsat: number preimage: string } @@ -230,7 +238,16 @@ export class PaymentService extends BaseService { throw new Error(errorMessage) } - return await response.json() + // Normalize at the boundary rather than handing the raw LNbits payload + // to callers: `fee` is signed millisats, and consumers all want a + // positive magnitude. Doing it here means no call site can pick the + // wrong field name or forget the sign. + const raw = await response.json() + return { + payment_hash: raw.payment_hash, + feeMsat: Math.abs(Number(raw.fee ?? 0)), + preimage: raw.preimage ?? '' + } } /** @@ -266,7 +283,7 @@ export class PaymentService extends BaseService { this.debug('Payment successful', { paymentHash: paymentResult.payment_hash, - feeMsat: paymentResult.fee_msat + feeMsat: paymentResult.feeMsat }) // Show success notification if enabled diff --git a/src/modules/events/services/LnbitsPaymentProvider.ts b/src/modules/events/services/LnbitsPaymentProvider.ts index 140bad4..76fc968 100644 --- a/src/modules/events/services/LnbitsPaymentProvider.ts +++ b/src/modules/events/services/LnbitsPaymentProvider.ts @@ -84,7 +84,9 @@ export class LnbitsPaymentProvider implements PaymentProvider { const data = await response.json() return { paymentHash: data.payment_hash, - feeMsat: data.fee_msat ?? 0, + // LNbits sends `fee` (signed millisats), never `fee_msat`. Reading the + // latter meant every payment recorded a zero fee. + feeMsat: Math.abs(Number(data.fee ?? 0)), preimage: data.preimage ?? '', } } diff --git a/src/modules/market/composables/useLightningPayment.ts b/src/modules/market/composables/useLightningPayment.ts index 0c1d790..0f4689f 100644 --- a/src/modules/market/composables/useLightningPayment.ts +++ b/src/modules/market/composables/useLightningPayment.ts @@ -42,7 +42,7 @@ export function useLightningPayment() { status: 'paid' as const, paidAt: Math.floor(Date.now() / 1000), paymentHash: paymentResult.payment_hash, - feeMsat: paymentResult.fee_msat, + feeMsat: paymentResult.feeMsat, items: [...order.items], // Convert readonly to mutable shippingZone: order.shippingZone ? { ...order.shippingZone, @@ -88,7 +88,7 @@ export function useLightningPayment() { status: 'paid' as const, paidAt: Math.floor(Date.now() / 1000), paymentHash: result.payment_hash, - feeMsat: result.fee_msat, + feeMsat: result.feeMsat, items: [...order.items], // Convert readonly to mutable shippingZone: order.shippingZone ? { ...order.shippingZone,