Eclair Lightning Integration

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Eclair Lightning Integration
Medium
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1218
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    853
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1047
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Eclair Integration (Lightning)

Eclair is a Lightning Network implementation in Scala by ACINQ. Unlike LND with its monolithic architecture, Eclair strictly follows BOLT specifications and is known for production reliability: it powers Phoenix wallet (ACINQ mobile Lightning wallet) and several major services. If your stack is JVM-based or you're already working with ACINQ ecosystem — this is natural choice.

Running and configuration

Requirements

Eclair requires Bitcoin Core node for blockchain data access. Neutrino (simplified mode) is not supported by Eclair — needs full node.

eclair.conf (Typesafe Config format):

eclair {
  chain = "mainnet"
  server.port = 9735
  api.enabled = true
  api.port = 8080
  api.password = "your-api-password"
  
  bitcoind {
    host = "localhost"
    rpcport = 8332
    rpcuser = "bitcoinrpc"
    rpcpassword = "rpcpassword"
    zmqblock = "tcp://127.0.0.1:28334"
    zmqtx = "tcp://127.0.0.1:28335"
  }
  
  # Fee policy for routing
  router.path-finding.default.max-fee-flat-sat = 21
  router.path-finding.default.max-fee-proportional = 0.01 // 1%
  
  # Channel limits
  max-htlc-value-in-flight-msat = 100000000000  // 1 BTC in msat
}

HTTP API: main operations

Eclair provides REST API (form-encoded POST requests, not JSON body — often causes confusion):

# Node information
curl -u :your-password http://localhost:8080/getinfo

# Open channel
curl -u :your-password http://localhost:8080/open \
  -d nodeId=<peer_pubkey> \
  -d fundingSatoshis=1000000 \
  -d pushMsat=0

# Create invoice
curl -u :your-password http://localhost:8080/createinvoice \
  -d description="Payment for order 123" \
  -d amountMsat=50000000 \
  -d expireIn=3600
  
# Send payment
curl -u :your-password http://localhost:8080/payinvoice \
  -d invoice=lnbc500u1p... \
  -d blocking=true

TypeScript client

import axios from "axios";
import FormData from "form-data";

class EclairClient {
  private readonly http = axios.create({
    baseURL: `http://${this.host}:${this.port}`,
    auth: { username: "", password: this.password },
  });

  async createInvoice(params: {
    amountMsat: number;
    description: string;
    expireIn?: number;
  }): Promise<{ serialized: string; paymentHash: string }> {
    const form = new FormData();
    form.append("amountMsat", params.amountMsat.toString());
    form.append("description", params.description);
    if (params.expireIn) form.append("expireIn", params.expireIn.toString());

    const { data } = await this.http.post("/createinvoice", form, {
      headers: form.getHeaders(),
    });
    return data;
  }

  async payInvoice(invoice: string, maxFeeMsat?: number): Promise<PaymentResult> {
    const form = new FormData();
    form.append("invoice", invoice);
    form.append("blocking", "true"); // wait for result
    if (maxFeeMsat) form.append("maxFeeFlatMsat", maxFeeMsat.toString());

    const { data } = await this.http.post("/payinvoice", form, {
      headers: form.getHeaders(),
    });
    return data;
  }

  async getPayment(paymentHash: string): Promise<PaymentStatus> {
    const form = new FormData();
    form.append("paymentHash", paymentHash);
    const { data } = await this.http.post("/getsentinfo", form, {
      headers: form.getHeaders(),
    });
    return data[0];
  }
}

WebHooks: real-time events

Eclair supports WebHook notifications for events — main way to react to incoming payments without polling:

// eclair.conf
eclair.api.webhooks = [
  {
    id = "my-backend"
    url = "https://your-backend.com/eclair/webhook"
    secret = "webhook-secret-for-hmac"
  }
]

Event types:

type EclairEvent =
  | { type: "payment-received"; paymentHash: string; amount: number; timestamp: number }
  | { type: "payment-sent"; paymentHash: string; amount: number; feesPaid: number }
  | { type: "payment-failed"; paymentHash: string; failures: string[] }
  | { type: "channel-opened"; channelId: string; remotePubkey: string; capacity: number }
  | { type: "channel-closed"; channelId: string; reason: string };

Webhook handler with signature verification:

app.post("/eclair/webhook", (req, res) => {
  const signature = req.headers["x-eclair-hmac"];
  const expectedSig = createHmac("sha256", WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest("hex");

  if (signature !== expectedSig) {
    return res.status(401).send("Invalid signature");
  }

  const event: EclairEvent = req.body;

  if (event.type === "payment-received") {
    handleIncomingPayment(event.paymentHash, event.amount);
  }

  res.sendStatus(200);
});

Eclair specifics compared to LND

BOLT-12 support — Eclair implements BOLT-12 Offers earlier and more completely than LND. If you need reusable payment codes or offline payment schemes — Eclair is advantage.

Trampoline routing — mechanism for delegating pathfinding to intermediate node. Critical for mobile clients (Phoenix uses this): lightweight client doesn't want to store full network graph. Eclair is one of few implementations with production Trampoline support.

# Send via trampoline
curl -u :password http://localhost:8080/payinvoice \
  -d invoice=lnbc... \
  -d trampolineNodeId=<trampoline_pubkey> \
  -d blocking=true

API style: form-encoded instead of JSON. This is not a bug, it's design decision. Converting JSON to form data — one extra line.

Metrologia: Eclair exports metrics via built-in Kamon instrumentation. Prometheus endpoint enabled by config:

kamon.prometheus.embedded-server.port = 9095

Typical integration scenarios

Merchant payments

  1. User chooses "pay with Lightning" → POST /createinvoice → QR code / BOLT-11 string
  2. Webhook payment-received → update order status in DB
  3. Duplicate polling via /getreceivedinfo for reliability (in case of missed webhook)

Bulk payouts

async function bulkPayout(payments: { invoice: string; maxFeeMsat: number }[]) {
  const results = await Promise.allSettled(
    payments.map((p) => eclairClient.payInvoice(p.invoice, p.maxFeeMsat))
  );

  const failed = results
    .map((r, i) => ({ result: r, payment: payments[i] }))
    .filter((r) => r.result.status === "rejected");

  // Log errors, retry with exponential backoff
  for (const { payment, result } of failed) {
    logger.error("Payment failed", { invoice: payment.invoice, reason: result });
  }
}

Fee policy for routing node

Eclair allows setting individual fee policies per channel:

# Update policy for specific channel
curl -u :password http://localhost:8080/updaterelayfee \
  -d channelId=<channel_id> \
  -d feeBaseMsat=1000 \
  -d feeProportionalMillionths=100  # 0.01%

Monitoring production node

Key metrics for Eclair:

  • channels.count by state (NORMAL, CLOSING, OFFLINE)
  • payment.sent.success_rate — success rate for outgoing payments
  • payment.received.count / amount — incoming flow
  • router.graph.nodes and channels — network size visible to node

Grafana dashboard with these metrics — standard operational necessity for any Lightning node with more than few channels.

Timeline for Eclair integration into existing backend (accept and send payments, webhook handling, monitoring): 3–5 weeks.