fix(wallet): accept uppercase QR-scanned BOLT11 invoices on send #103

Merged
padreug merged 1 commit from fix/uppercase-bolt11-send into dev 2026-06-15 20:09:15 +00:00
Owner

Problem

Scanning a Lightning invoice QR shows it decoded correctly (amount + description), but sending fails with "Invalid payment destination format" — see the reported screenshot (a 78 848-sat invoice decoded fine yet was rejected).

Root cause

QR codes encode BOLT11 as uppercase bech32 (LNBC…). The detection path (SendDialog.isBolt11) lowercases before matching, so it decodes fine. But WalletService.sendPayment gated the bolt11 branch on a case-sensitive request.destination.startsWith('ln'):

if (request.destination.startsWith('ln')) {   // 'LNBC…'.startsWith('ln') === false

So uppercase invoices fell through to the else and threw the error — despite the UI showing a valid decode.

Fix

  • Detect BOLT11 case-insensitively by its HRP (lnbc / lntb / lntbs / lnbcrt) and send the lowercase canonical form.
  • The bare 'ln' prefix also incidentally matched bech32 LNURLs (lnurl1…) and would misroute them to the bolt11 endpoint (the bolt11 branch is checked first); matching the full HRP closes that latent gap too.

Test notes

  • vue-tsc --noEmit clean for the changed file.
  • Suggest smoke on aio-demo after the dev deploy: scan/paste an uppercase LNBC… invoice and confirm the send succeeds; re-verify a lowercase invoice and a Lightning address still work.

🤖 Generated with Claude Code

## Problem Scanning a Lightning invoice QR shows it decoded correctly (amount + description), but sending fails with **"Invalid payment destination format"** — see the reported screenshot (a 78 848-sat invoice decoded fine yet was rejected). ## Root cause QR codes encode BOLT11 as **uppercase** bech32 (`LNBC…`). The detection path (`SendDialog.isBolt11`) lowercases before matching, so it decodes fine. But `WalletService.sendPayment` gated the bolt11 branch on a **case-sensitive** `request.destination.startsWith('ln')`: ```ts if (request.destination.startsWith('ln')) { // 'LNBC…'.startsWith('ln') === false ``` So uppercase invoices fell through to the `else` and threw the error — despite the UI showing a valid decode. ## Fix - Detect BOLT11 **case-insensitively** by its HRP (`lnbc` / `lntb` / `lntbs` / `lnbcrt`) and send the lowercase canonical form. - The bare `'ln'` prefix also incidentally matched bech32 LNURLs (`lnurl1…`) and would misroute them to the bolt11 endpoint (the bolt11 branch is checked first); matching the full HRP closes that latent gap too. ## Test notes - `vue-tsc --noEmit` clean for the changed file. - Suggest smoke on `aio-demo` after the dev deploy: scan/paste an **uppercase** `LNBC…` invoice and confirm the send succeeds; re-verify a lowercase invoice and a Lightning address still work. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The send path detected and decoded uppercase invoices correctly (SendDialog's
isBolt11 lowercases first) but WalletService.sendPayment gated the bolt11
branch on a case-sensitive startsWith('ln'), so QR-scanned invoices (uppercase
bech32, e.g. LNBC...) fell through to the else and threw "Invalid payment
destination format" despite the UI showing a valid decode.

Detect BOLT11 case-insensitively by its HRP (lnbc/lntb/lntbs/lnbcrt) and send
the lowercase canonical form. The bare 'ln' prefix also incidentally matched
bech32 LNURLs (lnurl1...) and misrouted them to the bolt11 endpoint; matching
the full HRP closes that gap too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
padreug deleted branch fix/uppercase-bolt11-send 2026-06-15 20:09:15 +00:00
Sign in to join this conversation.
No description provided.