Menu
Lightning Python Executable Jan 31, 2026

Create Lightning Invoices with LNbits

Generate BOLT11 invoices for receiving Lightning payments via LNbits API

#lnbits #invoice #bolt11 #api

Overview

Create Lightning invoices to receive payments using the LNbits API. LNbits provides a simple REST API that works with any Lightning backend.

The Code

"""
Lightning Invoice Creator
Generate BOLT11 invoices via LNbits API

Requirements:
- requests (pip install requests)
- LNbits instance with Invoice/Read key

Environment variables:
- LNBITS_URL: Your LNbits instance URL
- LNBITS_INVOICE_KEY: Invoice/Read API key
"""

import os
import requests
from typing import Optional
from dataclasses import dataclass

# Configuration
LNBITS_URL = os.getenv("LNBITS_URL", "https://legend.lnbits.com")
LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY", "")


@dataclass
class Invoice:
    """Lightning invoice data."""
    payment_hash: str
    payment_request: str  # BOLT11 string
    amount_sats: int
    memo: str
    checking_id: str


def create_invoice(
    amount_sats: int,
    memo: str = "",
    expiry: int = 3600,
    webhook: Optional[str] = None
) -> Invoice:
    """
    Create a Lightning invoice.

    Args:
        amount_sats: Amount in satoshis
        memo: Invoice description
        expiry: Expiry time in seconds (default 1 hour)
        webhook: Optional webhook URL for payment notification

    Returns:
        Invoice object with BOLT11 string

    Raises:
        ValueError: If API key not configured
        requests.RequestException: On API error
    """
    if not LNBITS_INVOICE_KEY:
        raise ValueError("LNBITS_INVOICE_KEY not set")

    headers = {
        "X-Api-Key": LNBITS_INVOICE_KEY,
        "Content-Type": "application/json"
    }

    payload = {
        "out": False,  # False = incoming invoice
        "amount": amount_sats,
        "memo": memo,
        "expiry": expiry
    }

    if webhook:
        payload["webhook"] = webhook

    response = requests.post(
        f"{LNBITS_URL}/api/v1/payments",
        headers=headers,
        json=payload,
        timeout=30
    )
    response.raise_for_status()

    data = response.json()

    return Invoice(
        payment_hash=data["payment_hash"],
        payment_request=data["payment_request"],
        amount_sats=amount_sats,
        memo=memo,
        checking_id=data.get("checking_id", data["payment_hash"])
    )


def check_invoice_status(payment_hash: str) -> dict:
    """
    Check if an invoice has been paid.

    Args:
        payment_hash: The payment hash from invoice creation

    Returns:
        dict with paid status and details
    """
    if not LNBITS_INVOICE_KEY:
        raise ValueError("LNBITS_INVOICE_KEY not set")

    headers = {"X-Api-Key": LNBITS_INVOICE_KEY}

    response = requests.get(
        f"{LNBITS_URL}/api/v1/payments/{payment_hash}",
        headers=headers,
        timeout=10
    )
    response.raise_for_status()

    data = response.json()

    return {
        "paid": data.get("paid", False),
        "pending": data.get("pending", True),
        "amount_sats": abs(data.get("amount", 0)) // 1000,  # msat to sat
        "fee_msat": data.get("fee", 0),
        "preimage": data.get("preimage"),
        "time": data.get("time")
    }


def wait_for_payment(payment_hash: str, timeout: int = 60) -> bool:
    """
    Wait for an invoice to be paid.

    Args:
        payment_hash: The payment hash to monitor
        timeout: Maximum seconds to wait

    Returns:
        True if paid, False if timeout
    """
    import time

    start = time.time()
    while time.time() - start < timeout:
        status = check_invoice_status(payment_hash)
        if status["paid"]:
            return True
        time.sleep(2)

    return False


def format_invoice_qr_data(bolt11: str) -> str:
    """
    Format invoice for QR code generation.

    Args:
        bolt11: BOLT11 invoice string

    Returns:
        Uppercase BOLT11 (better for QR codes)
    """
    return bolt11.upper()


# Example usage
if __name__ == "__main__":
    # Set your API key
    if not LNBITS_INVOICE_KEY:
        print("Set LNBITS_INVOICE_KEY environment variable")
        print("Example: export LNBITS_INVOICE_KEY='your-invoice-key'")
        exit(1)

    try:
        # Create a 1000 sat invoice
        invoice = create_invoice(
            amount_sats=1000,
            memo="Test payment from agent",
            expiry=3600
        )

        print("=== Invoice Created ===")
        print(f"Amount:       {invoice.amount_sats} sats")
        print(f"Memo:         {invoice.memo}")
        print(f"Payment hash: {invoice.payment_hash}")
        print(f"\nBOLT11:")
        print(invoice.payment_request)

        print("\n=== QR Code Data ===")
        print(format_invoice_qr_data(invoice.payment_request))

        print("\n=== Waiting for payment (60s) ===")
        if wait_for_payment(invoice.payment_hash, timeout=60):
            print("Payment received!")
            status = check_invoice_status(invoice.payment_hash)
            print(f"Preimage: {status['preimage']}")
        else:
            print("Timeout - no payment received")

    except requests.RequestException as e:
        print(f"API error: {e}")
    except ValueError as e:
        print(f"Configuration error: {e}")

Usage

# Set environment variables
export LNBITS_URL="https://your-lnbits.com"
export LNBITS_INVOICE_KEY="your-invoice-read-key"

# Install and run
pip install requests
python create_invoice.py

Example Output

=== Invoice Created ===
Amount:       1000 sats
Memo:         Test payment from agent
Payment hash: abc123def456...

BOLT11:
lnbc10u1pjq2ywdpp5...

=== QR Code Data ===
LNBC10U1PJQ2YWDPP5...

=== Waiting for payment (60s) ===
Payment received!
Preimage: 789xyz...

Agent Notes

Key types in LNbits:

  • Invoice/Read key: Can create invoices and check balances (safe to expose)
  • Admin key: Can also spend funds (keep secret!)

Best practices for agents:

  1. Always set a meaningful memo for tracking
  2. Use webhooks for async payment notifications
  3. Store payment_hash to verify payments later
  4. Set reasonable expiry (1 hour default, shorter for time-sensitive)

Zero-amount invoices: Omit amount to let the payer choose the amount. Useful for donations.