Разработка 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. Сервер возвращает metadata:

{
  "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: metadata
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-совместимые метрики из коробки.