Integration Guide
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..."
tip
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