Unofficial, open-source Python wrapper for Lexe Bitcoin Lightning Network wallet integration
Project description
Lexe Wrapper
A simple Python package for integrating Bitcoin Lightning Network payments using the Lexe wallet. Install with pip and start building Lightning apps in under 30 seconds.
pip install lexe-wrapper
โ ๏ธ Important: This is an unofficial, open-source wrapper around the Lexe Sidecar SDK. It is not officially associated with or endorsed by Lexe. This project is independently developed to help developers integrate with Lexe's Lightning wallet services.
What This Package Solves
The Lexe Sidecar API is already clean and simple, but there are several setup gotchas that slow down development:
- Binary Management: Downloading and extracting the correct Lexe sidecar binary
- Process Management: Starting and stopping the sidecar subprocess
- Credentials Handling: Properly encoding and validating base64 client credentials
- Connection Management: Ensuring the sidecar is healthy and ready to accept requests
- Port Configuration: Managing the correct port (5393) for communication
This package handles all of these automatically, so you can focus on building great Lightning applications.
โก Quick Start (30 seconds)
1. Install from PyPI
pip install lexe-wrapper
2. Set Your Lexe Credentials
export LEXE_CLIENT_CREDENTIALS="your_base64_encoded_credentials_here"
Need credentials? Get them from the Lexe mobile app - create a wallet and export client credentials.
3. Start Building Lightning Apps
Copy-Paste Example
from lexe_wrapper import LexeManager
import requests
# Automatic setup and cleanup
with LexeManager() as lexe:
lexe.start_sidecar() # Downloads binary, starts process, health checks
# Get node info and balance
node_response = requests.get("http://localhost:5393/v1/node/node_info")
node_info = node_response.json()
print(f"๐ฐ Wallet balance: {node_info['balance']} sats")
# Create a Lightning invoice (uses standard Lexe API at localhost:5393)
response = requests.post("http://localhost:5393/v1/node/create_invoice", json={
"amount": "1000",
"description": "Test payment",
"expiration_secs": 3600 # 1 hour expiration
})
invoice_data = response.json()
# CRITICAL: Store payment_index for monitoring payment completion
payment_index = invoice_data['index'] # Save this!
print(f"โก Lightning invoice created successfully!")
print(f"๐ Invoice: {invoice_data.get('invoice', 'Generated')[:50]}...")
print(f"๐ Payment index (save this): {payment_index}")
# Sidecar automatically stops when exiting the context
That's it! Lightning payments are now working in your Python app. ๐
Web App Integration
from lexe_wrapper import LexeManager
# Initialize once when your web app starts
lexe = LexeManager()
lexe.start_for_webapp() # Robust startup with error handling
# Use throughout your app's lifetime
@app.route('/create-invoice', methods=['POST'])
def create_invoice():
response = requests.post("http://localhost:5393/v1/node/create_invoice",
json=request.json)
return response.json()
# Stop when app shuts down
lexe.stop_sidecar()
Manual Management
from lexe_wrapper import LexeManager
lexe = LexeManager()
try:
lexe.start_sidecar()
if lexe.check_health():
print("Lexe is ready!")
# Get node info using the wrapper method
node_info = lexe.get_node_info()
print(f"Balance: {node_info['balance']} sats")
finally:
lexe.stop_sidecar()
For Coding Agents
When using this package in automated coding environments:
Essential Setup Steps
- Install package:
pip install lexe-wrapper - Set credentials:
export LEXE_CLIENT_CREDENTIALS="your_credentials" - Import and start: Use the context manager pattern for automatic cleanup
Example Implementation
from lexe_wrapper import LexeManager
import requests
def setup_lexe_integration():
"""Initialize Lexe for your application"""
lexe = LexeManager()
lexe.start_sidecar() # Downloads binary, starts process, waits for health
return "http://localhost:5393" # Ready to use Lexe API
def create_lightning_invoice(amount_sats, description):
"""Create a Lightning invoice using Lexe API"""
response = requests.post("http://localhost:5393/v1/node/create_invoice", json={
"amount": str(amount_sats),
"description": description
})
return response.json()
# The package handles all the complexity - just use the API directly!
Key Benefits for Automation
- Standard pip installation -
pip install lexe-wrapper(published on PyPI) - Clean imports -
from lexe_wrapper import LexeManager - Zero configuration files needed - everything is handled programmatically
- Automatic binary management - downloads and extracts the right version
- Built-in health checks - ensures the connection is ready before returning
- Error handling - clear error messages when credentials are invalid or missing
- Process lifecycle management - clean startup and shutdown
Advanced Usage
For Development from Source
If you want to contribute or modify the package:
git clone https://github.com/lexe-app/lexe-wrapper.git
cd lexe-wrapper
pip install -e .
โก Payment Monitoring (Critical Information)
Based on real developer feedback, here are the essential patterns for monitoring Lightning payments:
๐จ Critical Fixes (Must Read!)
1. Payment Status Value
# โ WRONG - Documentation was incorrect
if payment['status'] == 'settled':
# โ
CORRECT - API returns 'completed'
if payment['status'] == 'completed':
2. Store Payment Index
# When creating invoice, MUST store the 'index' field!
response = requests.post("http://localhost:5393/v1/node/create_invoice", json={...})
invoice_data = response.json()
# CRITICAL: Store this for payment monitoring
payment_index = invoice_data['index'] # Required for status checking
payment_hash = invoice_data['payment_hash'] # Also recommended
3. Correct API Endpoint
# Correct endpoint for payment status
endpoint = f"http://localhost:5393/v1/node/payment?index={payment_index}"
response = requests.get(endpoint)
Complete Working Example
from lexe_wrapper import LexeManager
import requests
import time
def complete_payment_flow():
with LexeManager() as lexe:
lexe.start_sidecar()
# 1. Create invoice and store essential fields
invoice_response = requests.post("http://localhost:5393/v1/node/create_invoice", json={
"amount": "1000",
"description": "Premium subscription",
"expiration_secs": 3600
})
invoice_data = invoice_response.json()
# CRITICAL: Store these fields in your database
payment_record = {
'invoice': invoice_data['invoice'], # BOLT11 for user
'payment_hash': invoice_data['payment_hash'], # Unique ID
'payment_index': invoice_data['index'], # REQUIRED for monitoring
'amount': 1000,
'status': 'pending'
}
# 2. Show invoice to user
print(f"Pay this: {payment_record['invoice']}")
# 3. Monitor payment completion
payment_index = payment_record['payment_index']
while True:
# Check payment status using correct endpoint
response = requests.get(f"http://localhost:5393/v1/node/payment?index={payment_index}")
if response.status_code == 200:
payment_data = response.json()
payment = payment_data['payment']
# CORRECT: Check for 'completed' status
if payment['status'] == 'completed':
print(f"๐ Payment received! Amount: {payment['amount']} sats")
# Update your database: status = 'completed'
break
elif payment['status'] == 'pending':
print("โณ Still waiting...")
time.sleep(3) # Check every 3 seconds
else:
print(f"โ Payment failed: {payment['status']}")
break
else:
print("Error checking payment status")
break
Web App Integration (Flask)
@app.route('/create-invoice', methods=['POST'])
def create_invoice():
response = requests.post("http://localhost:5393/v1/node/create_invoice", json=request.json)
invoice_data = response.json()
# CRITICAL: Store payment_index for monitoring
# Save to your database: invoice_data['index']
return {'invoice': invoice_data['invoice'], 'payment_index': invoice_data['index']}
@app.route('/check-payment/<payment_index>')
def check_payment_status(payment_index):
try:
response = requests.get(f'http://localhost:5393/v1/node/payment?index={payment_index}')
if response.status_code == 200:
payment = response.json()['payment']
# CRITICAL: Check for 'completed' not 'settled'
return {
'paid': payment['status'] == 'completed',
'status': payment['status'],
'amount': payment['amount']
}
else:
return {'error': 'Payment not found'}, 404
except Exception as e:
return {'error': str(e)}, 500
Database Schema Recommendations
-- Store these fields when creating invoices
CREATE TABLE lightning_payments (
id SERIAL PRIMARY KEY,
invoice TEXT NOT NULL, -- BOLT11 string
payment_hash TEXT NOT NULL, -- Unique payment identifier
payment_index TEXT NOT NULL, -- REQUIRED for status checking
amount_sats INTEGER NOT NULL,
description TEXT,
status VARCHAR(20) DEFAULT 'pending', -- pending, completed, failed
created_at TIMESTAMP DEFAULT NOW(),
finalized_at TIMESTAMP,
UNIQUE(payment_index)
);
๐ See examples/correct_payment_flow.py for complete, tested examples.
API Reference
LexeManager Class
Constructor
from lexe_wrapper import LexeManager
LexeManager(client_credentials=None, port=5393)
client_credentials: Base64 encoded credentials (usesLEXE_CLIENT_CREDENTIALSenv var if None)port: Port for sidecar to listen on (default: 5393)
Methods
start_sidecar(wait_for_health=True, health_timeout=30)
- Downloads binary if needed, starts process, optionally waits for health check
- Returns:
bool- True if started successfully
start_for_webapp(health_timeout=30)
- Web app specific startup with robust error handling
- Returns:
bool- True if started successfully - Raises:
RuntimeErrorif startup fails
stop_sidecar()
- Gracefully stops the sidecar process
- Returns:
bool- True if stopped successfully
check_health()
- Checks if sidecar is responding to health checks
- Returns:
bool- True if healthy
ensure_running()
- Ensures sidecar is running and healthy (great for health check endpoints)
- Returns:
bool- True if running and healthy
restart_if_needed()
- Restarts sidecar if not running or unhealthy
- Returns:
bool- True if now running and healthy
get_node_info()
- Gets node information from Lexe API
- Returns:
dict- Node information including balance, channels, etc.
is_running()
- Checks if the sidecar process is currently running
- Returns:
bool- True if running
CLI Reference
The package includes a command-line interface for testing and development:
# Using the installed package
python -m lexe_wrapper <command> [options]
# Or directly (if you cloned the repo)
python cli.py <command> [options]
Commands:
start Start the sidecar
stop Stop the sidecar
status Show sidecar status
health Check sidecar health
node-info Get node information
download Download sidecar binary only
Options:
--credentials TEXT Override credentials from env var
--port INTEGER Port for sidecar (default: 5393)
--timeout INTEGER Health check timeout (default: 30)
--no-wait Don't wait for health check when starting
--verbose Enable verbose logging
Web App Integration (Long-Lived Connections)
The wrapper is specifically designed to support long-lived connections for web applications. Here's how to integrate it:
Flask Example
from flask import Flask, jsonify, request
from lexe_manager import LexeManager
import requests
import atexit
app = Flask(__name__)
# Global Lexe manager instance
lexe_manager = None
def init_lexe():
"""Initialize Lexe sidecar when app starts"""
global lexe_manager
lexe_manager = LexeManager()
try:
# Use the web app specific startup method
lexe_manager.start_for_webapp(health_timeout=30)
print("โ
Lexe sidecar started successfully")
return True
except RuntimeError as e:
print(f"โ Failed to start Lexe: {e}")
return False
def cleanup_lexe():
"""Clean shutdown when app stops"""
global lexe_manager
if lexe_manager:
lexe_manager.stop_sidecar()
print("๐ Lexe sidecar stopped")
# Initialize Lexe when app starts
with app.app_context():
if not init_lexe():
exit(1)
# Register cleanup function
atexit.register(cleanup_lexe)
@app.route('/health')
def health_check():
"""App health check - includes Lexe status"""
if lexe_manager and lexe_manager.ensure_running():
return jsonify({"status": "healthy", "lexe": "connected"})
else:
return jsonify({"status": "degraded", "lexe": "disconnected"}), 503
@app.route('/node-info')
def node_info():
"""Get Lexe node information"""
try:
# Use direct API call (the whole point of this wrapper!)
response = requests.get("http://localhost:5393/v1/node/node_info", timeout=10)
response.raise_for_status()
return jsonify(response.json())
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/create-invoice', methods=['POST'])
def create_invoice():
"""Create a Lightning invoice"""
try:
amount = request.json.get('amount', '1000')
description = request.json.get('description', 'Payment')
# Direct Lexe API usage
response = requests.post("http://localhost:5393/v1/node/create_invoice",
json={
"amount": str(amount),
"description": description
}, timeout=15)
response.raise_for_status()
return jsonify(response.json())
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/restart-lexe', methods=['POST'])
def restart_lexe():
"""Restart Lexe if having issues"""
if lexe_manager and lexe_manager.restart_if_needed():
return jsonify({"status": "restarted"})
else:
return jsonify({"error": "restart failed"}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
FastAPI Example
from fastapi import FastAPI, HTTPException
from contextlib import asynccontextmanager
from lexe_manager import LexeManager
import requests
# Global Lexe manager
lexe_manager = None
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
global lexe_manager
lexe_manager = LexeManager()
try:
lexe_manager.start_for_webapp()
print("โ
Lexe sidecar started")
yield
except RuntimeError as e:
print(f"โ Lexe startup failed: {e}")
raise
finally:
# Shutdown
if lexe_manager:
lexe_manager.stop_sidecar()
print("๐ Lexe sidecar stopped")
app = FastAPI(lifespan=lifespan)
@app.get("/health")
def health_check():
if lexe_manager and lexe_manager.ensure_running():
return {"status": "healthy", "lexe": "connected"}
raise HTTPException(503, "Lexe not available")
@app.post("/invoices")
def create_invoice(amount: int, description: str = "Payment"):
try:
response = requests.post("http://localhost:5393/v1/node/create_invoice",
json={"amount": str(amount), "description": description})
response.raise_for_status()
return response.json()
except Exception as e:
raise HTTPException(500, str(e))
Web App Best Practices
- Startup Pattern: Use
start_for_webapp()during app initialization - Health Monitoring: Use
ensure_running()in your health check endpoints - Recovery: Use
restart_if_needed()for automatic recovery - Shutdown: Ensure
stop_sidecar()is called when your app shuts down - Direct API Usage: Once started, use standard HTTP requests to
localhost:5393
Process Management for Production
import signal
import sys
from lexe_manager import LexeManager
# Global manager for signal handlers
lexe_manager = None
def signal_handler(signum, frame):
"""Handle shutdown signals gracefully"""
global lexe_manager
print("๐ Received shutdown signal, stopping Lexe...")
if lexe_manager:
lexe_manager.stop_sidecar()
sys.exit(0)
# Register signal handlers for graceful shutdown
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
signal.signal(signal.SIGTERM, signal_handler) # Docker/systemd stop
def main():
global lexe_manager
lexe_manager = LexeManager()
# Start for long-lived operation
lexe_manager.start_for_webapp()
# Your web app code here
# The sidecar stays running until explicitly stopped
After Starting the Sidecar
Once the wrapper starts the sidecar, you can use the standard Lexe Sidecar API directly:
- Health:
GET http://localhost:5393/v1/health - Node Info:
GET http://localhost:5393/v1/node/node_info - Create Invoice:
POST http://localhost:5393/v1/node/create_invoice - Pay Invoice:
POST http://localhost:5393/v1/node/pay_invoice - Check Payment:
GET http://localhost:5393/v1/node/payment?index=<index>
Error Handling
The wrapper provides clear error messages for common issues:
- Missing credentials: Clear message about setting
LEXE_CLIENT_CREDENTIALS - Invalid credentials: Validates base64 format and provides specific error
- Download failures: Network issues when downloading the binary
- Health check failures: When sidecar doesn't respond within timeout
- Process management: Issues starting or stopping the sidecar process
Package Structure
lexe-wrapper/
โโโ lexe_wrapper/ # Main package
โ โโโ __init__.py # Package exports LexeManager
โ โโโ manager.py # Core LexeManager class
โ โโโ __main__.py # CLI entry point
โโโ examples/ # Usage examples
โ โโโ simple_usage.py # Simple integration examples
โโโ setup.py # Package installation
โโโ pyproject.toml # Modern Python packaging
โโโ README.md # This documentation
Requirements
- Python 3.7+
requestslibrary (automatically installed as dependency)- x86_64 Linux environment (where Lexe sidecar runs)
- Valid Lexe client credentials
Contributing
Contributions are welcome! This is an open-source project and we encourage community involvement:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
Code of Conduct: Please be respectful and constructive in all interactions.
License
MIT License - This project is free and open-source software. See the LICENSE file for full details.
Copyright (c) 2025 Mat Balez
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
About
This package is designed to eliminate the friction in getting started with Lexe Bitcoin Lightning Network integration. The Lexe Sidecar API itself is excellent - this package just handles the setup complexity so you can focus on building great Lightning applications.
Key Design Principles:
- Standard Python packaging - Install with pip, import normally
- Minimal dependencies - Only requires
requests - Clean API surface - Simple import, clear methods
- Production ready - Proper error handling, logging, and lifecycle management
- Direct API access - No unnecessary abstraction over the Lexe Sidecar API
Open Source & Legal
๐ License: This project is released under the MIT License, making it free to use, modify, and distribute.
โ๏ธ Disclaimer: This is an unofficial, community-developed wrapper around the Lexe Sidecar SDK. It is not officially associated with, endorsed by, or supported by Lexe. This project was independently created to help Python developers integrate with Lexe's Lightning wallet services more easily.
๐ง Open Source:
- Source Code: Available on GitHub for transparency and community contributions
- Issues & Contributions: Welcome! Please submit bug reports and feature requests
- No Warranty: Provided "as is" - please review the code and test thoroughly before production use
๐ Official Lexe Resources:
- Official Lexe Website: lexe.app
- Official Lexe Sidecar SDK: github.com/lexe-app/lexe-sidecar-sdk
- Lexe Documentation: Follow official Lexe channels for authoritative information
Author
Created and maintained by Mat Balez
This project was developed to help the Python community easily integrate Bitcoin Lightning Network payments using Lexe's excellent infrastructure, with a focus on eliminating common setup friction points.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lexe-wrapper-1.1.0.tar.gz.
File metadata
- Download URL: lexe-wrapper-1.1.0.tar.gz
- Upload date:
- Size: 30.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eba0bdbb8e2b4f6146e4e7eccf75e20e1070ac2fc7c7990b5e55ea9951734fa9
|
|
| MD5 |
e292e20e45d099b1b0cf9854cde9661d
|
|
| BLAKE2b-256 |
f409cf27365326560af99c354cbdf8ca132f887ff18898ee76300431fe9dbb04
|
File details
Details for the file lexe_wrapper-1.1.0-py3-none-any.whl.
File metadata
- Download URL: lexe_wrapper-1.1.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c90ff17f94db507363958108a21048eccd7f63214e243f496a00f18ac434ee7
|
|
| MD5 |
3b494409dfa60ebc9e0518acd734e1b6
|
|
| BLAKE2b-256 |
abe1af56adbbeacc0b8d37feafdc35b844a99aa04e7ca834345103f46bfaf158
|