Main Menu
Implementation Cheatsheet
Receiving
7 min
receiving payments lightning network payments begin with invoice creation and require monitoring to confirm successful payment reception lnd provides multiple methods for creating invoices and tracking their payment status, from simple rest polling to real time streaming interfaces 1\ create invoice lightning invoices are payment requests that contain all the information needed for a payer to send funds to your node when you create an invoice, lnd generates a unique payment hash and returns a bolt11 encoded payment request string that can be shared via qr code, copied directly, or embedded in your application each invoice includes essential details like the payment amount (in satoshis), an optional memo describing the payment purpose, and an expiration time the invoice remains valid until either it's paid in full, manually canceled, or reaches its expiry time lightning decoder with decoded invoice const createinvoice = async (amount, memo) => { try { const invoice = await lnd post("/v1/invoices", { value amount, memo memo, expiry 3600 // 1 hour expiry }); return invoice data; } catch (error) { console error("invoice creation failed ", error); throw error; } };async def create invoice(lnd client, amount int, memo str) """ create a new lightning invoice args lnd client the lnd client instance amount invoice amount in satoshis memo invoice description/memo returns dict invoice data including payment request and r hash """ try invoice = await lnd client post("/v1/invoices", { "value" amount, "memo" memo, "expiry" 3600 # 1 hour expiry }) return invoice except exception as error print(f"invoice creation failed {error}") raise error \# synchronous version def create invoice sync(lnd client, amount int, memo str) try invoice = lnd client post("/v1/invoices", { "value" amount, "memo" memo, "expiry" 3600 # 1 hour expiry }) return invoice except exception as error print(f"invoice creation failed {error}") raise errorfunction createinvoice($lndclient, $amount, $memo) { / create a new lightning invoice @param lndclient $lndclient the lnd client instance @param int $amount invoice amount in satoshis @param string $memo invoice description/memo @return array invoice data including payment request and r hash / try { $invoice = $lndclient >post('/v1/invoices', \[ 'value' => $amount, 'memo' => $memo, 'expiry' => 3600 // 1 hour expiry ]); return $invoice; } catch (exception $error) { error log("invoice creation failed " $error >getmessage()); throw $error; } } 2\ monitor invoice status choose one of these methods based on your needs once an invoice is created, you need to monitor its status to know when payment has been received lnd offers several approaches for tracking invoice payments, each suited to different application architectures and requirements choose the monitoring method that best fits your needs rest polling best for simple integrations, http only environments, or applications that don't require immediate payment notifications rest polling involves making periodic api calls to check if an invoice has been paid while straightforward to implement, this method introduces some latency between payment completion and detection it's ideal when you need basic payment tracking without the complexity of persistent connections const checkinvoicestatus = async (rhash) => { try { const invoice = await lnd get(/v1/invoice/${rhash}); return invoice data; } catch (error) { console error("invoice lookup failed ", error); throw error; } };async def check invoice status(lnd client, r hash str) """ check the status of an invoice by its r hash args lnd client the lnd client instance r hash the invoice's r hash (payment hash) returns dict invoice data including state and settlement info """ try invoice = await lnd client get(f"/v1/invoice/{r hash}") return invoice except exception as error print(f"invoice lookup failed {error}") raise error function checkinvoicestatus($lndclient, $rhash) { / check the status of an invoice by its r hash @param lndclient $lndclient the lnd client instance @param string $rhash the invoice's r hash (payment hash) @return array invoice data including state and settlement info / try { $invoice = $lndclient >get("/v1/invoice/{$rhash}"); return $invoice; } catch (exception $error) { error log("invoice lookup failed " $error >getmessage()); throw $error; } } websocket subscription best for real time applications, browser based implementations, or when you need immediate payment notifications with lower server overhead than rest polling websockets provide real time, bidirectional communication that allows your application to receive instant notifications when invoice status changes this eliminates polling delays and reduces server load, making it perfect for dashboards, point of sale systems, or any application where immediate payment confirmation enhances user experience const subscribetoinvoice = (paymenthash) => { const ws = new websocket(wss\ //${host} 8080/v1/invoices/subscribe); ws onmessage = (event) => { const invoice = json parse(event data); if (invoice r hash === paymenthash) { // handle invoice update handleinvoiceupdate(invoice); } }; };import websockets import json async def subscribe to invoice(host str, macaroon str, payment hash str) """ subscribe to invoice updates via websocket args host lnd node host macaroon authentication macaroon payment hash the invoice's payment hash to monitor """ uri = f"wss\ //{host} 8080/v1/invoices/subscribe" headers = { "grpc metadata macaroon" macaroon } async with websockets connect(uri, extra headers=headers, ssl=true) as websocket async for message in websocket invoice = json loads(message) if invoice get('r hash') == payment hash \# handle invoice update await handle invoice update(invoice) \# exit if settled if invoice get('state') == 'settled' breakuse websocket\client; function subscribetoinvoice($host, $macaroon, $paymenthash) { / subscribe to invoice updates via websocket @param string $host lnd node host @param string $macaroon authentication macaroon @param string $paymenthash the invoice's payment hash to monitor / $uri = "wss\ //{$host} 8080/v1/invoices/subscribe"; try { $client = new client($uri, \[ 'timeout' => 60, 'headers' => \[ 'grpc metadata macaroon' => $macaroon ], 'context' => stream context create(\[ 'ssl' => \[ 'verify peer' => false, // set to true in production 'verify peer name' => false // set to true in production ] ]) ]); while (true) { $message = $client >receive(); $invoice = json decode($message, true); if (isset($invoice\['r hash']) && $invoice\['r hash'] === $paymenthash) { // handle invoice update handleinvoiceupdate($invoice); // exit if settled if (isset($invoice\['state']) && $invoice\['state'] === 'settled') { break; } } } $client >close(); } catch (exception $e) { error log("websocket error " $e >getmessage()); throw $e; } } grpc best for server side applications requiring the most efficient real time updates, high throughput environments, or when you need the full feature set of lnd's streaming capabilities grpc provides the most efficient and feature complete interface for monitoring invoice status with strongly typed responses and bidirectional streaming, it offers the lowest latency and highest reliability for payment tracking this method is ideal for production applications that need robust, persistent connections to your lightning node const subscribetoinvoices = (client) => { const stream = client subscribeinvoices({}); stream on('data', (invoice) => { // handle invoice update handleinvoiceupdate(invoice); }); stream on('error', (error) => { console error("invoice subscription error ", error); }); };import grpc from lightning import lightning pb2, lightning pb2 grpc def subscribe to invoices grpc(host str, port int, macaroon str, cert path str) """ subscribe to all invoice updates via grpc args host lnd node host port grpc port (usually 10009) macaroon authentication macaroon (hex string) cert path path to tls certificate """ \# create credentials with open(cert path, 'rb') as f cert = f read() creds = grpc ssl channel credentials(cert) \# create channel with macaroon metadata channel = grpc secure channel(f'{host} {port}', creds) stub = lightning pb2 grpc lightningstub(channel) \# prepare macaroon metadata metadata = \[('macaroon', macaroon)] \# subscribe to invoices request = lightning pb2 invoicesubscription() try \# stream invoice updates for invoice in stub subscribeinvoices(request, metadata=metadata) handle invoice update grpc(invoice) except grpc rpcerror as e print(f"grpc subscription error {e code()} {e details()}") raise// grpc implementation using grpc/grpc (requires protobuf compilation) // first install composer require grpc/grpc google/protobuf function subscribetoinvoicesgrpc($host, $port, $macaroon, $certpath) { / subscribe to all invoice updates via grpc @param string $host lnd node host @param int $port grpc port (usually 10009) @param string $macaroon authentication macaroon (hex string) @param string $certpath path to tls certificate / // read certificate $cert = file get contents($certpath); // create credentials $credentials = \grpc\channelcredentials createssl($cert); // create client $client = new lightning\lightningclient( "{$host} {$port}", \[ 'credentials' => $credentials, 'update metadata' => function($metadata) use ($macaroon) { $metadata\['macaroon'] = \[$macaroon]; return $metadata; } ] ); // create subscription request $request = new lightning\invoicesubscription(); // subscribe to invoices $call = $client >subscribeinvoices($request); // process stream foreach ($call >responses() as $invoice) { handleinvoiceupdategrpc($invoice); } } implementation considerations security use read only or invoice specific macaroons rather than admin macaroons when possible or in environments that do not need full admin access (for sending out funds onchain or over lightning) implement sensible rate limiting (e g , max 100 invoices per minute per ip) to prevent abuse error handling websocket connections drop frequently—implement exponential backoff reconnection starting at 1 second, maxing at 30 seconds for grpc streams, catch unavailable and deadline exceeded errors specifically and restart the subscription rest polling should handle 5xx errors by increasing the poll interval temporarily (e g , from 5 seconds to 30 seconds) before returning to normal frequency scalability for high volume applications, grpc streaming typically offers the best performance, while rest polling may be sufficient for lower volume use cases user experience real time payment confirmation significantly improves user experience compared to polling based approaches, particularly for interactive applications like point of sale systems