Integration Guide
The Standalone API plan is not available to new customers.
If you are evaluating Lightning Enable, use one of the current plans instead:
- Agentic Commerce — Individual ($99/mo) — Full REST API + L402 protocol
- Agentic Commerce — Business ($299/mo) — Full REST API + pay-per-request API monetization
- Kentico Commerce ($249/mo) — Full REST API + Xperience by Kentico integration
Existing Standalone subscribers are unaffected. The documentation below is retained for their reference.
This guide walks you through integrating the Lightning Enable Standalone API into your application.
Overview
Integration involves:
- Creating payments when customers checkout
- Displaying payment options (QR code, hosted checkout)
- Receiving payment confirmations (webhooks or polling)
- Fulfilling orders
Authentication
All API requests require an API key in the X-API-Key header:
curl -X GET https://api.lightningenable.com/api/payments/inv_123 \
-H "X-API-Key: le_merchant_abc123..."
Store your API key in environment variables, never in code:
export LIGHTNING_API_KEY="le_merchant_abc123..."
Creating Payments
Basic Payment
const response = await fetch('https://api.lightningenable.com/api/payments', {
method: 'POST',
headers: {
'X-API-Key': process.env.LIGHTNING_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
orderId: 'ORDER-12345',
amount: 99.99,
currency: 'USD',
description: 'Annual Subscription'
})
});
const payment = await response.json();
Payment with Customer Details
const payment = await createPayment({
orderId: 'ORDER-12345',
amount: 99.99,
currency: 'USD',
description: 'Annual Subscription',
customerEmail: 'customer@example.com',
customerName: 'John Doe',
successUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cancel',
metadata: {
customerId: 'cust_123',
planId: 'pro_annual'
}
});
Payment Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
orderId | string | Yes | Your unique order identifier |
amount | decimal | Yes | Payment amount |
currency | string | Yes | Currency code (USD, EUR, GBP, BTC) |
description | string | No | Payment description |
customerEmail | string | No | Customer email |
customerName | string | No | Customer name |
successUrl | string | No | Redirect URL on success |
cancelUrl | string | No | Redirect URL on cancel |
metadata | object | No | Custom key-value data |
Payment Response Fields
| Field | Type | Description |
|---|---|---|
invoiceId | string | Lightning Enable invoice ID |
openNodeChargeId | string | OpenNode charge ID |
status | string | Payment status (unpaid, paid, expired) |
amount | decimal | Payment amount |
currency | string | Currency code |
amountSats | integer | Amount in satoshis |
lightningInvoice | string | BOLT11 Lightning invoice |
onchainAddress | string | Bitcoin address |
hostedCheckoutUrl | string | OpenNode checkout URL |
createdAt | datetime | Creation timestamp |
expiresAt | datetime | Expiration timestamp |
Displaying Payment Options
Option 1: Hosted Checkout (Easiest)
Redirect customers to OpenNode's hosted checkout:
// After creating payment
window.location.href = payment.hostedCheckoutUrl;
Pros:
- No additional code needed
- Mobile-optimized UI
- Handles all payment methods
Cons:
- Customers leave your site
Option 2: Embedded QR Code
Display the Lightning invoice as a QR code:
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
<canvas id="qr-code"></canvas>
<script>
QRCode.toCanvas(
document.getElementById('qr-code'),
payment.lightningInvoice,
{ width: 256 }
);
</script>
Option 3: Copy Invoice
Let users copy the invoice to their wallet:
<input
type="text"
value="${payment.lightningInvoice}"
id="invoice-input"
readonly
/>
<button onclick="copyInvoice()">Copy Invoice</button>
<script>
function copyInvoice() {
document.getElementById('invoice-input').select();
navigator.clipboard.writeText(document.getElementById('invoice-input').value);
alert('Copied!');
}
</script>
Checking Payment Status
Polling
Query the status endpoint periodically:
async function checkStatus(invoiceId) {
const response = await fetch(
`https://api.lightningenable.com/api/payments/${invoiceId}`,
{
headers: { 'X-API-Key': process.env.LIGHTNING_API_KEY }
}
);
return await response.json();
}
// Poll every 3 seconds
const interval = setInterval(async () => {
const payment = await checkStatus(invoiceId);
if (payment.status === 'paid') {
clearInterval(interval);
fulfillOrder(payment.orderId);
} else if (payment.status === 'expired') {
clearInterval(interval);
showExpiredMessage();
}
}, 3000);
Webhooks (Recommended)
Receive instant notifications when payment status changes:
// Express.js webhook handler
app.post('/webhooks/lightning', express.json(), (req, res) => {
const { invoiceId, orderId, status } = req.body;
// Verify signature (important!)
const signature = req.headers['x-gateway-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).send('Invalid signature');
}
if (status === 'paid') {
fulfillOrder(orderId);
}
res.status(200).send('OK');
});
See Webhooks Documentation for complete implementation.
Error Handling
Handle common error scenarios:
async function createPayment(orderData) {
try {
const response = await fetch('https://api.lightningenable.com/api/payments', {
method: 'POST',
headers: {
'X-API-Key': process.env.LIGHTNING_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
});
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 400:
throw new Error(`Invalid request: ${error.message}`);
case 401:
throw new Error('Invalid API key');
case 429:
throw new Error('Rate limit exceeded');
default:
throw new Error(`API error: ${error.message}`);
}
}
return await response.json();
} catch (error) {
console.error('Payment creation failed:', error);
throw error;
}
}
Language Examples
C# / .NET
public class LightningEnableClient
{
private readonly HttpClient _client;
private readonly string _apiKey;
public LightningEnableClient(string apiKey)
{
_client = new HttpClient
{
BaseAddress = new Uri("https://api.lightningenable.com")
};
_apiKey = apiKey;
}
public async Task<PaymentResponse> CreatePaymentAsync(PaymentRequest request)
{
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/payments")
{
Content = JsonContent.Create(request)
};
httpRequest.Headers.Add("X-API-Key", _apiKey);
var response = await _client.SendAsync(httpRequest);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<PaymentResponse>();
}
}
Python
import requests
import os
class LightningEnableClient:
def __init__(self, api_key=None):
self.api_key = api_key or os.environ.get('LIGHTNING_API_KEY')
self.base_url = 'https://api.lightningenable.com'
def create_payment(self, order_id, amount, currency='USD', **kwargs):
response = requests.post(
f'{self.base_url}/api/payments',
headers={
'X-API-Key': self.api_key,
'Content-Type': 'application/json'
},
json={
'orderId': order_id,
'amount': amount,
'currency': currency,
**kwargs
}
)
response.raise_for_status()
return response.json()
def get_payment(self, invoice_id):
response = requests.get(
f'{self.base_url}/api/payments/{invoice_id}',
headers={'X-API-Key': self.api_key}
)
response.raise_for_status()
return response.json()
Go
package lightning
import (
"bytes"
"encoding/json"
"net/http"
)
type Client struct {
APIKey string
BaseURL string
}
func NewClient(apiKey string) *Client {
return &Client{
APIKey: apiKey,
BaseURL: "https://api.lightningenable.com",
}
}
func (c *Client) CreatePayment(request PaymentRequest) (*PaymentResponse, error) {
body, _ := json.Marshal(request)
req, _ := http.NewRequest("POST", c.BaseURL+"/api/payments", bytes.NewBuffer(body))
req.Header.Set("X-API-Key", c.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var payment PaymentResponse
json.NewDecoder(resp.Body).Decode(&payment)
return &payment, nil
}
Best Practices
Security
- Store API keys in environment variables
- Use HTTPS for all webhook endpoints
- Verify webhook signatures
- Validate all input data
Reliability
- Implement webhook retries on your end
- Handle expired invoices gracefully
- Store payment state in your database
- Use idempotent order processing
Performance
- Don't poll more than once per 3 seconds
- Cache exchange rates
- Use webhooks instead of polling in production
Next Steps
- Webhooks Setup - Real-time notifications
- Refunds - Process returns
- API Reference - Complete documentation