Что можно принимать
Lightning принимает BTC, не стейблкоины и не on-chain переводы. У запроса свои сумма, hash и срок. Клиент оплачивает полную сумму Lightning-счёта.
Платежи Bitcoin on-chainНастрой свой магазин
В настройках подключений добавь LND через HTTPS или NWC через WSS и включи его в магазине. Нужны публично доступные endpoints; адрес только LAN или Tor напрямую не подойдёт.
Ключи доступа и хранение средств
Используй ограниченные права приёма, не admin/spending macaroon LND. NWC описывает связь, а не хранение средств: проверь, кто держит ключи.
Куда поступают платежи
QR содержит BOLT11-счёт, не Bitcoin-адрес. Не оплачивай его on-chain. После истечения создай новый счёт: просто продлить срок нельзя.
Когда платёж завершён?
Wholly Crypto проверяет расчёт, сообщённый кошельком. Открытый счёт или удерживаемый HTLC не считаются оплатой. У Lightning нет блоковых подтверждений и публичной ссылки на транзакцию.
Переводи средства, когда готов
Используй внешний кошелёк или узел для отправки, ликвидности и резервных копий. On-chain sweep не переводит Lightning. Экспорт кошелька проекта не сохраняет внешний кошелёк.
Руководство по Lightning-кошелькуПроблемы с платежами
Если нет маршрута, проверь входящую ликвидность, доступность кошелька и срок. Исправный API не гарантирует маршрут. При неясном результате сначала проверь расчёт, потом проси повторить.
Комиссии и проверка
Стандартная комиссия: 1% от фиатной суммы счёта, списывается с отдельного баланса кредитов. Действует твой назначенный тариф. Сеть и сервер оплачиваются отдельно.
Публичные провайдеры могут сбоить или ограничивать запросы. Отправленная транзакция или перенаправление не доказывают оплату. Используй проверенный статус счёта и подписанные уведомления.
Кредиты на обработку API, IPN и вебхукиКалькулятор курсов
Пересчитать сумму
Это оценка, а не платёжная котировка. Сетевые и биржевые комиссии, а также наценка продавца не включены.
Откуда берутся курсы?
Криптовалютные оценки используют кешированные публичные цены Kraken и, где доступны, Binance. Для некоторых цен Binance USDT используется как ориентир USD. Фиатные курсы берутся из Frankfurter: это ежедневные справочные значения, а не текущие торговые котировки.
Снимок данных обновляется автоматически. Проверь даты источников под результатом. Введённая сумма остаётся в браузере. Наличие актива в списке не гарантирует поддержку оплаты.
Kraken · Binance · Frankfurter · Названия и иконки активов: CoinGecko
Создай счёт через API
Запускай примеры на сервере. Замени API-хост, ID проекта и магазина. Храни ключ в секрете. Суммы передаются десятичными строками.
Сначала включи метод в магазине. Выбор сужает активные методы, но не добавляет активы. Если совпадений нет, используются настройки магазина. Проверь возвращённые методы перед показом оплаты.
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new invoice.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: order-1042-attempt-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "25.00",
"currency": "EUR",
"order_id": "order-1042",
"payment_methods": [
{
"chain_slug": "bitcoin",
"payment_rail": "lightning"
}
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new invoice.
const body = `{
"amount": "25.00",
"currency": "EUR",
"order_id": "order-1042",
"payment_methods": [
{
"chain_slug": "bitcoin",
"payment_rail": "lightning"
}
]
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new invoice.
$body = <<<'JSON'
{
"amount": "25.00",
"currency": "EUR",
"order_id": "order-1042",
"payment_methods": [
{
"chain_slug": "bitcoin",
"payment_rail": "lightning"
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: order-1042-attempt-1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new invoice.
headers = {
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "25.00",
"currency": "EUR",
"order_id": "order-1042",
"payment_methods": [
{
"chain_slug": "bitcoin",
"payment_rail": "lightning"
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))