> ## Documentation Index
> Fetch the complete documentation index at: https://shinydapps-bd9fa40b.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Устранение неполадок

> Распространённые ошибки, их причины и способы устранения.

## Ошибки токенов

### `Token already used` (401)

**Причина:** Один и тот же preimage был отправлен дважды — атака повторного воспроизведения или случайная повторная попытка.

**Решение:** Каждый платёж по инвойсу создаёт одноразовый токен. Сгенерируйте новый инвойс и оплатите его снова. На стороне клиента убедитесь, что `L402Client` кэширует токены по URL конечной точки (по умолчанию это так).

***

### `Token expired` (401 / `valid: false`)

**Причина:** Инвойсы истекают через 1 час. Поле `exp` токена задаётся в миллисекундах.

**Решение:** Запросите новый инвойс. Если срок действия истекает слишком быстро, проверьте точность системных часов сервера (`Date.now()` на сервере и `time.time()` на клиенте должны отличаться не более чем на несколько секунд).

***

### `Invalid preimage format` (вместо 200 возвращается 402)

**Причина:** Preimage содержит не ровно 64 шестнадцатеричных символа (32 байта).

**Решение:** Убедитесь, что ваш кошелёк возвращает сырой preimage в виде hex-строки из 64 символов. Некоторые кошельки возвращают его в формате base64 — в этом случае сначала декодируйте его.

***

### `Webhook signature mismatch` (401)

**Причина:** Заголовок `l402-signature` не совпадает с секретом, или тело запроса было изменено при передаче.

**Решение:**

1. Убедитесь, что `L402_WEBHOOK_SECRET` совпадает на стороне отправителя и получателя.
2. Используйте `express.raw({ type: 'application/json' })` (а не `express.json()`) для чтения сырого тела запроса перед верификацией — JSON-парсинг переформатирует строку и аннулирует подпись.
3. Проверьте, что ваш обратный прокси (nginx, Cloudflare) не изменяет тело запроса.

***

## Ошибки провайдера

### `ManagedProvider: invoice creation failed` / HTTP 503

**Причина:** `l402kit.com` временно недоступен или API Blink не работает.

**Решение:** Повторите запрос с экспоненциальной задержкой. Проверьте статус на [status.blink.sv](https://status.blink.sv). Для производственных нагрузок, требующих нулевого времени простоя, используйте собственный провайдер (BlinkProvider, AlbyProvider и т. д.) с вашими учётными данными.

***

### `Blink: INSUFFICIENT_CHANNEL_BALANCE`

**Причина:** Ваш кошелёк Blink имеет недостаточную исходящую ликвидность для разделённого платежа.

**Решение:** Пополните кошелёк `shinydapps@blink.sv`. Платформе требуется небольшой баланс для маршрутизации разделённых платежей. Это влияет только на разделение через ManagedProvider — создание инвойсов не затрагивается.

***

### `LNURL fetch failed` при разделении платежа

**Причина:** Lightning Address разработчика не разрешается. Конечная точка `/.well-known/lnurlp/` вернула статус, отличный от 200.

**Решение:** Убедитесь, что Lightning Address действителен, а LNURL-pay конечная точка домена доступна. Проверьте с помощью:

```bash theme={null}
curl https://<domain>/.well-known/lnurlp/<username>
```

***

### `AlbyProvider: HTTP 401`

**Причина:** Токен доступа Alby истёк или был отозван.

**Решение:** Перегенерируйте токен в Alby Hub → Настройки → Токены доступа. Убедитесь, что область видимости включает `invoices:create`.

***

### `BTCPayProvider: HTTP 403`

**Причина:** У API-ключа отсутствует необходимое разрешение.

**Решение:** В BTCPay Server → Аккаунт → API-ключи убедитесь, что ключ имеет область видимости `btcpay.store.cancreatelightninginvoice` для нужного магазина.

***

## Защита от повторных атак

### Несколько экземпляров / повторные атаки не блокируются

**Причина:** По умолчанию `MemoryReplayAdapter` работает только в рамках текущего процесса. При перезапуске или при наличии нескольких экземпляров он сбрасывается.

**Решение:** Используйте `RedisReplayAdapter` для развёртываний с несколькими экземплярами:

<CodeGroup>
  ```ts TypeScript theme={null}
  import Redis from "ioredis";
  import { RedisReplayAdapter } from "l402-kit";

  const redis = new Redis(process.env.REDIS_URL!);
  app.get("/api", l402({
    priceSats: 10,
    lightning,
    replayAdapter: new RedisReplayAdapter(redis),
  }), handler);
  ```

  ```python Python theme={null}
  from l402kit import RedisReplayAdapter
  import redis.asyncio as redis

  r = redis.from_url(os.environ["REDIS_URL"])
  # pass replayAdapter to l402_required (coming soon)
  ```
</CodeGroup>

Уникальное ограничение `payment_hash` в Supabase выступает в роли надёжного второго уровня защиты вне зависимости от адаптера в памяти, поэтому повторные атаки всегда блокируются даже без Redis.

***

## Ограничения скорости

### `Too many requests. Max 20 invoices/minute per IP.` (429)

**Причина:** Более 20 запросов на создание инвойса в минуту с одного IP-адреса, достигших конечной точки ManagedProvider.

**Решение:** Реализуйте кэширование на стороне клиента — не создавайте новый инвойс при каждой загрузке страницы. `L402Client` автоматически кэширует токены по URL конечной точки. Для потоков между серверами, генерирующих много инвойсов, используйте собственный провайдер.

***

## Специфика фреймворков

### Express: middleware не срабатывает

**Причина:** Обработчик маршрута Express зарегистрирован до middleware `l402()`, или порядок middleware неверен.

**Решение:**

```ts theme={null}
// ✅ Правильно — l402 перед обработчиком
app.get("/api", l402({ priceSats: 10, lightning }), myHandler);

// ❌ Неправильно — l402 зарегистрирован после обработчика
app.get("/api", myHandler, l402(...));
```

***

### FastAPI: `422 Unprocessable Entity` при ответе 402

**Причина:** FastAPI валидирует тела ответов в соответствии с объявленной моделью ответа. Тело ответа 402 не совпадает с ней.

**Решение:** Исключите статус 402 из валидации ответа или не объявляйте модель ответа для декорированных конечных точек:

```python theme={null}
@app.get("/premium")  # no response_model= here
@l402_required(price_sats=10, lightning=provider)
async def premium():
    return {"data": "paid content"}
```

***

### Go: `panic: l402: no Lightning provider set`

**Причина:** `Options.Lightning` равен nil.

**Решение:** Всегда задавайте провайдер:

```go theme={null}
// ✅
l402kit.Middleware(l402kit.Options{
    PriceSats: 10,
    Lightning: l402kit.NewManagedProvider("you@blink.sv"),
}, handler)

// ❌ вызывает panic
l402kit.Middleware(l402kit.Options{PriceSats: 10}, handler)
```

***

## Всё ещё не решили проблему?

Откройте issue на [github.com/ShinyDapps/l402-kit/issues](https://github.com/ShinyDapps/l402-kit/issues), указав:

* Язык и версию SDK
* Минимальный воспроизводящий пример
* Сообщение об ошибке и трассировку стека
