fix(send): route QR-scanned usernames to BTC-default recipients via LNURL (ENG-399)#646
Merged
islandbitcoin merged 1 commit intoJun 20, 2026
Conversation
…NURL (ENG-399) A bare Flash username scanned via QR that resolves to a recipient whose default wallet is BTC/Breez previously failed when the sender's wallet was USD: the shared parser treated it as PaymentType.Intraledger and the USD send hit intraLedgerUsdPaymentSend against the recipient's BTC wallet, failing with the generic support error. Manual entry already handled this (PR #602) via a module-private maybeResolveManualUsernameToLnurl. This: - Extracts that helper into payment-destination/resolve-username-to-lnurl.ts so every entry point can share the routing decision (the ticket flags the duplicated-logic risk). - Applies it in the QR-scan processInvoice via the same `maybeResolve... ?? parseDestination` pattern as manual entry, so a BTC-default recipient is re-routed through `username@lnAddressHostname`. - Adds unit tests for the helper covering: non-intraledger pass-through, USD-recipient pass-through, self-payment, successful LNURL re-route, and the unreachable-lightning-address error. Scope: QR is the only entry point that carries a bare username into a send. NFC is receive/withdraw-only (rejects Send-direction, reads lnurl payloads only) and the card flow always carries a Flashcard LNURL, so neither is changed. The durable cross-entry-point fix is the backend resolveSendDestination resolver (ENG-399 Phase 2), filed separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A bare Flash username scanned via QR that resolves to a recipient whose default wallet is BTC/Breez fails when the sender's wallet is USD: the shared parser treats it as
PaymentType.Intraledger, and the USD send then callsintraLedgerUsdPaymentSendagainst the recipient's BTC wallet — failing with the generic support error.Manual bare-username entry already handles this (PR #602) via a module-private
maybeResolveManualUsernameToLnurl, which detects the BTC-default recipient and re-routes the payment through their lightning address (username@lnAddressHostname). This PR brings the QR path to parity.Changes
maybeResolveManualUsernameToLnurlintopayment-destination/resolve-username-to-lnurl.tsso every entry point shares one routing decision (the ticket explicitly calls out the duplicated-logic risk). No behavior change for manual entry — it now imports the shared helper.processInvoiceusing the samemaybeResolve… ?? parseDestinationfallthrough as manual entry. The helper returnsnullfor anything that isn't a BTC-default-recipient username, so all other QR destinations are unaffected.__tests__/payment-destination/resolve-username-to-lnurl.spec.tscovers all five branches: non-intraledger pass-through, USD-recipient pass-through, self-payment, successful LNURL re-route (handle@hostname), and the unreachable-lightning-address (LnurlError) case.Scope (why only QR)
Of the four
parseDestinationentry points, QR is the only one that carries a bare username into a send:modal-nfc.tsx) is receive/withdraw-only — it readslnurlpayloads only and explicitly rejects Send-direction destinations (nfcOnlyReceive).card.tsx) always carries a Flashcard LNURL, never a bare username.Editing those would be incorrect/risky, so they're intentionally untouched. The durable, all-entry-point fix is the backend
resolveSendDestinationresolver — ENG-399 Phase 2 — filed as a separate follow-up.Validation
main's CI is currently red for toolchain reasons unrelated to this PR — thetsconfigTS5095 error (fixed onfeat/fygaroby #642) and thettypescript/ts-jestbreakage (ENG-421/425).maininherits these fixes when #621 lands. Validated locally instead:babel-jest, sincemain'sts-jestcan't load).tscshows zero new errors vs themainbaseline (82 → 82; the 82 are the pre-existing ENG-425 cluster).Acceptance criteria
nullto fall through)resolveSendDestination) — tracked separatelyRefs ENG-399 (Phase 1).
🤖 Generated with Claude Code