When the user's wallet (e.g. Zeus) is connected to the same LND node
that LP uses, LNURL-withdraw fails because LND rejects the payment
with "no self-payments allowed". This is safe because LP always
decrements the user's balance before paying and refunds on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When userPubkey is provided, resolve the ApplicationUser and call
applicationManager.PayAppUserInvoice instead of paymentManager.PayInvoice
directly. This ensures notifyAppUserPayment fires, sending
LiveUserOperation events via Nostr for real-time balance updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store the Nostr pubkey of the user who creates a withdraw link so the
LNURL callback debits the correct user's balance instead of the app
owner's. Pass userPubkey through from RPC handler to WithdrawManager.
- Add creator_pubkey column (migration v4)
- Store creatorPubkey on link creation
- Pass creator_pubkey to payInvoice on LNURL callback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Initialize extension system before nostrMiddleware so registered
RPC methods are available. Extension methods (e.g. withdraw.createLink)
are intercepted and routed to the extension loader before falling
through to the standard nostrTransport.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add POST /api/v1/withdraw/create endpoint to allow external apps (ATM,
web clients) to create LNURL-withdraw links via HTTP instead of RPC.
Changes:
- Add handleCreateWithdrawLink HTTP handler
- Fix route ordering: callback routes before wildcard :unique_hash
- Extract app_id from Authorization header (Bearer app_<id>)
- Use is_unique=false for simple single-use ATM links
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enable CORS on the extension HTTP server to allow cross-origin requests
from ATM apps and other web-based clients.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add extension loader initialization to startup
- Create mainHandlerAdapter to bridge mainHandler with extension context
- Mount extension HTTP routes on separate port (main port + 1)
- Configure EXTENSION_SERVICE_URL for LNURL link generation
The withdraw extension provides LUD-03 LNURL-withdraw support for
creating withdraw links that allow users to pull funds.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements LUD-03 (LNURL-withdraw) for creating withdraw links
that allow anyone to pull funds from a Lightning wallet.
Features:
- Create withdraw links with min/max amounts
- Quick vouchers: batch creation of single-use codes
- Multi-use links with wait time between uses
- Unique QR codes per use (prevents sharing exploits)
- Webhook notifications on successful withdrawals
- Full LNURL protocol compliance for wallet compatibility
Use cases:
- Faucets
- Gift cards / prepaid cards
- Tips / donations
- User onboarding
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HttpRoute, HttpRequest, and HttpResponse types were used by extensions
(withdraw, nip05) but not defined in the shared types.ts. Adds them
here so extensions import from the shared module instead of defining
locally. Also adds getHttpRoutes() as an optional method on the
Extension interface for extensions that expose HTTP endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
notifyAppUserPayment was sending the stale cached balance from the
entity loaded before PayInvoice decremented it. Update the entity's
balance_sats from the PayInvoice response so LiveUserOperation events
contain the correct post-payment balance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .dockerignore for runtime state files (sqlite, logs, secrets)
- Bump Node.js base image from 18 to 20
- Add @types/better-sqlite3 dev dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @noble/curves secp256k1.getSharedSecret expects Uint8Array arguments,
not hex strings. Use hex.decode() to convert the private and public keys.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enables extensions to get LNURL-pay info for users by pubkey,
supporting Lightning Address (LUD-16) and zap (NIP-57) functionality.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a modular extension system for Lightning.Pub that allows
third-party functionality to be added without modifying core code.
Features:
- ExtensionLoader: discovers and loads extensions from directory
- ExtensionContext: provides extensions with access to Lightning.Pub APIs
- ExtensionDatabase: isolated SQLite database per extension
- Lifecycle management: initialize, shutdown, health checks
- RPC method registration: extensions can add new RPC methods
- Event dispatching: routes payments and Nostr events to extensions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Each sendEvent() call created a new SimplePool() but never closed it,
causing relay WebSocket connections to accumulate indefinitely (~20/min).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all NostrSend call sites to properly handle the async nature
of the function now that it returns Promise<void>.
Changes:
- handler.ts: Add async to sendResponse, await nostrSend calls
- debitManager.ts: Add logging for Kind 21002 response sending
- nostrMiddleware.ts: Update nostrSend signature
- tlvFilesStorageProcessor.ts: Update nostrSend signature
- webRTC/index.ts: Add async/await for nostrSend calls
This ensures Kind 21002 (ndebit) responses are properly sent to
wallet clients, fixing the "Debit request failed" issue in ShockWallet.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The NostrSend type was incorrectly typed as returning void when it actually
returns Promise<void>. This caused async errors to be silently swallowed.
Changes:
- Update NostrSend type signature to return Promise<void>
- Make NostrSender._nostrSend default to async function
- Add .catch() error handling in NostrSender.Send() to log failures
- Add logging to track event publishing status
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>