Entry now spends the tap's single-use SUN once, on the card server's new
/session endpoint (aiolabs/boltcards feat/card-session-endpoint), instead
of parsing the lnurlw locally and deferring every check to Complete. The
server proves a genuine, non-replayed card and returns the wallet balance
plus the hit-keyed LUD-03 withdraw and LUD-06 pay second steps — the same
single-use bearer /scan and /pay hand out — so Complete still needs no
second tap and the ATM holds no p/c for the visit.
- electron/boltcard-session.ts: /scan → /session URL derivation, response
parsing, 404 → 'card server does not support sessions'.
- lnurl-withdraw / lnurl-pay: the second steps are now callable on their
own (executeWithdrawCallback, resolveInvoiceFromPayStep); the tap paths
are unchanged and reuse them.
- IPC: lnurl:open-card-session, lnurl:withdraw-session, lnurl:pay-session.
- store: handleBoltCardEntry opens the session then authorizes the
server-returned external_id; the payment handlers take a source (raw
tap or session); a withheld withdraw step declines with the server's
reason. The boltcard AccessScan no longer carries the lnurlw.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
First, hardware-independent piece of Bolt Card tap-to-pay on the cash-out
flow. When a customer taps a Bolt Card, the ATM (which already has its
cash-out BOLT11) becomes the LNURL-*withdrawing* party: GET the card's
lnurlw voucher → GET callback?k1=…&pr=<invoice> so the card's wallet pays
the invoice. Settlement is still observed via the existing invoice watcher
(a returned ok=true means "card accepted the pull", not "cash dispensed").
- electron/lnurl-withdraw.ts: executeLnurlWithdraw() + lnurlwToHttps().
Runs in the main process (Node fetch) to avoid renderer CORS, since LNURL
endpoints send no CORS headers. Fully injectable fetch for testing.
- electron/lnurl-withdraw.test.ts: 12 tests (scheme mapping, two-step happy
path passing k1+pr, ERROR surfacing, non-withdraw tag, amount-over-limit
short-circuit, callback decline, network failure).
- IPC `lnurl:withdraw` (main) + preload + electron.d.ts.
Next: pcscd + an nfc-pcsc reader driver (reads the NTAG424 NDEF lnurlw),
then wire the tap into the cashOut displayingInvoice state + "tap or scan" UI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>