Skip to content

bug: amountless BOLT11 invoices are not handled — payment always fails #49

@forge11cuba

Description

@forge11cuba

Summary

When a user scans or pastes an amountless BOLT11 invoice (one with no num_msat field), the app decodes it correctly but navigates straight to the confirmation screen showing "0 sats" with no way to enter an amount. Pressing "Pay" sends a payment request to LNBits without an amount, which the server rejects.

Root Cause

_processBolt11Payment() always navigates to InvoiceConfirmScreen regardless of whether the decoded invoice has an amount:

// lib/screens/10send_screen.dart
final decodedInvoice = await _invoiceService.decodeBolt11(...);

// No check for amountSats == 0
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => InvoiceConfirmScreen(decodedInvoice: decodedInvoice),
  ),
);

DecodedInvoice._parseAmount() correctly returns BigInt.zero for amountless invoices, so decodedInvoice.amountSats == 0. But InvoiceConfirmScreen has no input field for the user to specify an amount, and sendPayment() passes only the raw bolt11 to LNBits with no amount field — causing the server to return an error.

Steps to Reproduce

  1. Generate an amountless BOLT11 invoice (e.g. from BTCPay Server, a tip jar, or any BOLT11 generator with amount left blank).
  2. In LaChispa, go to Send and paste or scan the invoice.
  3. The confirmation screen shows "0 sats".
  4. Press "Pay".
  5. Payment fails with a server error. No sats are sent.

Expected Behavior

When an amountless invoice is detected (amountSats == 0), the app should prompt the user to enter the amount before proceeding to confirmation.

Fix

Detect the zero-amount case in _processBolt11Payment() and show an amount input dialog before navigating to confirmation:

final decodedInvoice = await _invoiceService.decodeBolt11(...);

if (decodedInvoice.amountSats == 0) {
  // Amountless invoice — ask user for amount
  final int? chosenAmount = await _showAmountInputDialog(context);
  if (chosenAmount == null || !mounted) return; // user cancelled
  // Pass the chosen amount to the confirmation screen and to sendPayment()
}

InvoiceConfirmScreen and sendPayment() would also need to accept an optional overrideAmountSats parameter to forward the user-entered amount to the LNBits API.

Note: AmountScreen (11amount_screen.dart) is designed for LNURL-pay and Lightning Address flows and cannot be reused directly here without refactoring.

Impact

  • Amountless invoices are commonly used for tip jars, donations, and BTCPay Server payment buttons
  • Payment is completely broken for this invoice type — no workaround exists in the current UI

Affected Files

  • lib/screens/10send_screen.dart_processBolt11Payment()
  • lib/screens/12invoice_confirm_screen.dart — needs overrideAmountSats
  • lib/services/invoice_service.dartsendPayment() needs amount param

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions