Розробка LNURL-інтеграції

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка LNURL-інтеграції
Середній
~2-3 дні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1286
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Інтеграція LNURL-протоколу

Lightning Network вирішила проблему швидкості Bitcoin-платежів, але створила нову: користувачу потрібно скопіювати invoice з кошелька продавця у свій кошельок — це незручно. LNURL — це набір протоколів поверх Lightning, які дозволяють кошельку автоматично запросити invoice через HTTP, просканувавши QR-код один раз. З точки зору UX це наближається до звичайних платіжних систем.

Сімейство LNURL-протоколів

LNURL — не один протокол, а кілька спеціфікацій (LUD — Lightning URL Definitions). Кожен вирішує свою задачу:

LUD Протокол Призначение
LUD-01 LNURL-pay Оплата: кошельок запитує invoice у сервера продавця
LUD-03 LNURL-withdraw Вивід: кошельок отримує кошти по посиланню
LUD-04 LNURL-auth Аутентифікація через Lightning ключ (passwordless login)
LUD-06 LNURL-channel Відкриття каналу
LUD-12 Lightning Address Формат [email protected] для LNURL-pay

Для приєму платежів важливі прежде всього LNURL-pay та Lightning Address.

Як працює LNURL-pay

Весь процес — два HTTP запити між кошельком та сервером:

Крок 1. Користувач сканує QR. Кошельок бачить lnurl1dp68gurn8ghj7um9wfmxjcm99e3k7mf0v9cxj0m385ekvcenxc6r2c35xvukxefcv5mkvv34x5ekzd3ev56nyd3hhgarjv4ehcmn9wsh8xmmrd9skcnjv... (bech32 encoded HTTPS URL). Кошельок декодує та робить GET на цей URL.

Крок 2. Сервер повертає метаданні:

{
  "tag": "payRequest",
  "callback": "https://merchant.com/lnurl/pay/invoice",
  "minSendable": 1000,
  "maxSendable": 100000000,
  "metadata": "[[\"text/plain\",\"Payment to My Shop\"],[\"image/png;base64\",\"iVBORw0...\"]]"
}

Крок 3. Користувач вводить суму. Кошельок робить GET на callback з параметром amount (у millisatoshi):

GET https://merchant.com/lnurl/pay/invoice?amount=10000

Крок 4. Сервер генерує Lightning invoice через свою LN-ноду та повертає:

{
  "pr": "lnbc100n1pj...",
  "routes": [],
  "successAction": {
    "tag": "message",
    "message": "Payment confirmed! Order #12345"
  }
}

Крок 5. Кошельок оплачує invoice. Після успішної оплати показує successAction.

Реалізація LNURL-pay сервера

Потрібна Lightning нода (LND або Core Lightning) для генерації invoice. Приклад на Node.js з LND через gRPC:

import express from 'express';
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { bech32 } from 'bech32';

const app = express();

// LNURL-pay крок 1: метаданні
app.get('/lnurl/pay/:paymentId', async (req, res) => {
  const { paymentId } = req.params;
  const callbackUrl = `https://${req.hostname}/lnurl/pay/${paymentId}/invoice`;
  
  // Кодуємо URL в lnurl bech32 (для QR коду)
  const lnurlEncoded = encodeLnurl(callbackUrl);
  
  res.json({
    tag: 'payRequest',
    callback: callbackUrl,
    minSendable: 1000,          // 1 sat у millisatoshi
    maxSendable: 100_000_000,   // 0.001 BTC
    metadata: JSON.stringify([
      ['text/plain', `Payment for order ${paymentId}`],
    ]),
  });
});

// LNURL-pay крок 2: генерація invoice
app.get('/lnurl/pay/:paymentId/invoice', async (req, res) => {
  const { paymentId } = req.params;
  const amountMsat = parseInt(req.query.amount as string);
  
  if (!amountMsat || amountMsat < 1000) {
    return res.status(400).json({ status: 'ERROR', reason: 'Invalid amount' });
  }
  
  try {
    const invoice = await lndClient.addInvoice({
      value_msat: amountMsat,
      memo: `Order ${paymentId}`,
      expiry: 3600, // 1 година
    });
    
    // Зберігаємо в БД: зв'язок invoice з paymentId
    await db.saveInvoice({
      paymentHash: invoice.r_hash,
      paymentId,
      amountMsat,
    });
    
    res.json({
      pr: invoice.payment_request,
      routes: [],
      successAction: {
        tag: 'message',
        message: `Order ${paymentId} confirmed!`,
      },
    });
  } catch (err) {
    res.status(500).json({ status: 'ERROR', reason: 'Failed to generate invoice' });
  }
});

function encodeLnurl(url: string): string {
  const words = bech32.toWords(Buffer.from(url, 'utf8'));
  return bech32.encode('lnurl', words, 1023).toUpperCase();
}

Lightning Address: [email protected]

Lightning Address (LUD-12) — найзручніший UX. Замість QR коду користувач вводить адресу як email. Кошельок автоматично робить запрос на https://domain.com/.well-known/lnurlp/username.

// Маршрут для Lightning Address
app.get('/.well-known/lnurlp/:username', async (req, res) => {
  const { username } = req.params;
  
  const user = await db.getUserByLnAddress(username);
  if (!user) {
    return res.status(404).json({ status: 'ERROR', reason: 'User not found' });
  }
  
  res.json({
    tag: 'payRequest',
    callback: `https://${req.hostname}/lnurl/lightning-address/${username}`,
    minSendable: 1000,
    maxSendable: 10_000_000_000,
    metadata: JSON.stringify([
      ['text/identifier', `${username}@${req.hostname}`],
      ['text/plain', `Payment to ${username}`],
    ]),
    commentAllowed: 144, // підтримка коментарів до 144 символів
  });
});

Після цього [email protected] працює як Lightning Address у будь-якому сумісному кошельку (Phoenix, Wallet of Satoshi, Zeus, Breez).

LNURL-auth: passwordless логін

LNURL-auth дозволяє користувачам логінитися через Lightning кошельок без пароля. Кошельок підписує challenge приватним ключем, похідним від Lightning seed.

import crypto from 'crypto';

// Генерація challenge для логіну
app.get('/auth/lnurl', (req, res) => {
  const k1 = crypto.randomBytes(32).toString('hex');
  
  // Зберігаємо k1 в Redis з TTL 5 хвилин
  redis.setex(`lnurl_auth:${k1}`, 300, 'pending');
  
  const lnurlAuthUrl = `https://${req.hostname}/auth/callback?tag=login&k1=${k1}`;
  const encoded = encodeLnurl(lnurlAuthUrl);
  
  res.json({ lnurl: encoded, k1 });
});

// Callback від кошелька (підпис k1)
app.get('/auth/callback', async (req, res) => {
  const { k1, sig, key } = req.query as Record<string, string>;
  
  const status = await redis.get(`lnurl_auth:${k1}`);
  if (!status) {
    return res.json({ status: 'ERROR', reason: 'Unknown k1' });
  }
  
  // Верифікація ECDSA підпису (secp256k1)
  const isValid = verifyLnurlAuthSignature(k1, sig, key);
  if (!isValid) {
    return res.json({ status: 'ERROR', reason: 'Invalid signature' });
  }
  
  // Помічаємо k1 як аутентифіцирований з публічним ключем
  await redis.setex(`lnurl_auth:${k1}`, 300, `authenticated:${key}`);
  
  res.json({ status: 'OK' });
});

Фронтенд polling-ом перевіряє статус k1 — як тільки кошельок підписав, користувач залогінен.

Інфраструктурні вимоги

Lightning нода — обов'язкова. Варіанти: LND (Go, найпоширеніша, gRPC API), Core Lightning / CLN (C, UNIX socket + REST), Eclair (Scala, використовується Acinq/Phoenix). Для production: виділений VPS з 4GB+ RAM, SSD, стабільним інтернетом. Нода повинна мати входящу ліквідність для приєму платежів.

Hosted рішення для швидкого старту: Voltage.cloud (managed LND), Alby Hub (self-custody але спрощено), Strike API (custodial, але не потрібна своя нода). Для серйозного використання з великими обсягами — тільки власна нода.

TLS та домен обов'язкові: LNURL вимагає HTTPS. Самопідписаний сертифікат не пройде — потрібен Let's Encrypt або аналог.

Моніторинг: channel balance (алерт при < 10% входящої ліквідності), invoice expiry, failed payment attempts. LND Metrics експортує Prometheus-сумісні метрики з коробки.