Main Menu
Implementation Cheatsheet
Sending
6 min
sending payments lightning network payments are straightforward but require some consideration and validation to ensure successful and secure fund transfers the payment sending process involves decoding payment requests, validating payment details, executing the payment, and tracking its progress until completion or failure unlike traditional payment systems, lightning payments are near instantaneous but require awareness of possible routing failures, fee management, and payment validation lnd provides robust apis for managing the entire payment lifecycle, from initial invoice inspection to final settlement confirmation 1\ decode payment request (invoice) before sending any lightning payment, you should decode the bolt11 payment request to inspect its contents and validate the payment details this critical first step allows you to verify the payment amount, destination, expiry time, and description before committing funds decoding reveals essential information including the recipient's public key, the exact amount requested, when the invoice expires, and any routing hints that may be required for successful payment delivery this step is mandatory for implementing proper payment validation and user confirmation flows lightning decoder with decoded invoice const decodeinvoice = async (paymentrequest) => { try { const decoded = await lnd get(/v1/payreq/${paymentrequest}); return decoded data; } catch (error) { console error("invoice decode failed ", error); throw error; } };# decode payment request (invoice) async def decode invoice(payment request str) > dict\[str, any] try decoded = await lnd get(f"/v1/payreq/{payment request}") return decoded except exception as error print(f"invoice decode failed {error}") raise error \# synchronous version def decode invoice sync(payment request str) > dict\[str, any] try decoded = lnd get(f"/v1/payreq/{payment request}") return decoded except exception as error print(f"invoice decode failed {error}") raise errorfunction decodeinvoice($lnd, $paymentrequest) { try { $decoded = $lnd >get("/v1/payreq/{$paymentrequest}"); return $decoded; } catch (exception $error) { error log("invoice decode failed " $error >getmessage()); throw $error; } } 2\ send payment after decoding and validating the invoice, you can initiate the actual payment this step involves setting appropriate fee limits, choosing routing preferences, and executing the payment through lnd's payment engine the payment process includes automatic route finding, fee calculation, and retry logic for failed attempts setting proper fee limits is crucial to prevent fee siphoning attacks and control payment costs lnd will attempt multiple routes if the first attempt fails, automatically handling most routing complexities while respecting your specified constraints const sendpayment = async (paymentrequest) => { try { // 1 decode and verify the invoice const decodedinvoice = await decodeinvoice(paymentrequest); // 2 verify payment details validatepaymentdetails(decodedinvoice); // 3 send payment const payment = await lnd post("/v1/channels/transactions", { payment request paymentrequest, fee limit { fixed 1000 } // protect against fee siphoning }); return payment data; } catch (error) { console error("payment failed ", error); throw error; } };async def send payment(payment request str) > dict\[str, any] try \# 1 decode and verify the invoice decoded invoice = await decode invoice(payment request) \# 2 verify payment details validate payment details(decoded invoice) \# 3 send payment payment = await lnd post("/v1/channels/transactions", { "payment request" payment request, "fee limit" {"fixed" 1000} # protect against fee siphoning }) return payment except exception as error print(f"payment failed {error}") raise error \# synchronous version def send payment sync(payment request str) > dict\[str, any] try \# 1 decode and verify the invoice decoded invoice = decode invoice sync(payment request) \# 2 verify payment details validate payment details(decoded invoice) \# 3 send payment payment = lnd post("/v1/channels/transactions", { "payment request" payment request, "fee limit" {"fixed" 1000} # protect against fee siphoning }) return payment except exception as error print(f"payment failed {error}") raise errorfunction sendpayment($lnd, $paymentrequest) { try { // 1 decode and verify the invoice $decodedinvoice = decodeinvoice($lnd, $paymentrequest); // 2 verify payment details validatepaymentdetails($decodedinvoice); // 3 send payment $payment = $lnd >post("/v1/channels/transactions", \[ "payment request" => $paymentrequest, "fee limit" => \["fixed" => 1000] // protect against fee siphoning ]); return $payment; } catch (exception $error) { error log("payment failed " $error >getmessage()); throw $error; } } 3\ handle payment response payment responses require careful handling to distinguish between successful payments, failed attempts, and payments still in progress implementing proper response handling ensures your application correctly processes payment outcomes and provides appropriate user feedback lightning payments can fail for various reasons including insufficient liquidity, routing failures, or invoice expiration your validation logic should check payment amounts against business limits, verify destinations are authorized, and ensure invoices haven't expired before sending const validatepaymentdetails = (decodedinvoice) => { // implement your validation logic // check amount is within limits // verify destination is allowed // check expiry // validate other business rules };def validate payment details(decoded invoice dict\[str, any]) > none """ implement your validation logic \ check amount is within limits \ verify destination is allowed \ check expiry \ validate other business rules """function validatepaymentdetails($decodedinvoice) { / implement your validation logic check amount is within limits verify destination is allowed check expiry validate other business rules / implementation considerations security always decode and validate invoices before payment—verify the amount matches user expectations and the destination pubkey is authorized set conservative fee limits (typically 1 5% of payment amount) to prevent fee siphoning attacks never automatically retry payments without user confirmation, as this could lead to duplicate payments if the first attempt actually succeeded but appeared to fail error handling lightning payments usually fail due to routing issues—implement proper retry logic with exponential backoff (start at 5 seconds, max 60 seconds between attempts) handle specific payment failure reasons failure reason timeout suggests trying a different route, while failure reason no route indicates insufficient liquidity scalability for high volume payment processing, use the streaming sendpaymentv2 grpc method instead of rest endpoints to get real time payment status updates batch multiple small payments when possible to reduce routing overhead user experience show payment progress immediately after initiation—lightning payments typically complete within 5 10 seconds but can take up to 60 seconds in challenging routing conditions display estimated fees during payment confirmation to set proper expectations implement payment status polling or websocket updates to provide real time feedback without requiring page refreshes