# Идемпотентность и лимиты

## Идемпотентность { #idempotency }

Ключ идемпотентности — ваш `merchant_payment_id`. Он уникален в пределах проекта.
Отдельного заголовка `Idempotency-Key` нет.

| Что пришло | Ответ |
| --- | --- |
| Новый `merchant_payment_id` | `201` — новая заявка |
| Тот же `merchant_payment_id` и те же поля | `200` — уже созданная заявка, реквизиты повторно не запрашиваются |
| Тот же `merchant_payment_id`, но другие поля | `409 payment_idempotency_conflict` |

«Те же поля» — это `merchant_payment_id`, сумма, `currency`, `geo_code`,
`payment_method`, `bank_code`, `is_intrabank` и `callback_url`. Значения по умолчанию
подставляются до сравнения: запрос без `currency` и тот же запрос с `"currency": "RUB"`
считаются одинаковыми.

**Что это даёт.** Если ответ не дошёл — таймаут, обрыв, падение вашего сервера —
просто повторите запрос с тем же номером. Двойной заявки и двойного списания не будет.

**Что делать для новой попытки оплаты.** Если заявка закрылась (`canceled`, `error`)
или получила `422`, для новой попытки нужен **новый** `merchant_payment_id`: повтор
со старым вернёт прежний результат. Удобно добавлять номер попытки: `order-1001-2`.

**Когда номер не занимается.** Отказы до приёма заявки не занимают `merchant_payment_id`:
`429`, `503`, `401 signature_invalid`, `409 request_replayed`, `413 request_too_large`,
`400 bad_request` (тело не разобралось), `400 invalid_payment` (поле не прошло проверку).
Исправьте причину и отправьте запрос с тем же номером. Если исправленный запрос получает `409 payment_idempotency_conflict`,
используйте новый номер.

### Nonce и повторы { #nonce }

Идемпотентность заявки и одноразовость подписи — разные вещи.

- `X-Request-Nonce` одноразовый: повтор того же подписанного запроса — `409 request_replayed`.
- Поэтому при каждом повторе **заново подписывайте** запрос: новый nonce, новые
  `created`/`expires`, тот же `merchant_payment_id` и то же тело.
- Исключение — `429 too_many_concurrent_requests`: nonce не тратится, и тот же
  подписанный запрос можно отправить ещё раз, пока не истёк `expires`.

## Лимиты { #rate-limits }

| Лимит | По умолчанию | Ответ при превышении |
| --- | --- | --- |
| Запросов с одного IP-адреса | 600 в минуту | `429 rate_limited` |
| Запросов проекта | 600 в минуту, всплеск до 60 | `429 rate_limited` |
| Создание заявок проекта | 60 в минуту, всплеск до 6 | `429 rate_limited` |
| Одновременных запросов проекта | 20 | `429 too_many_concurrent_requests` |
| Размер тела запроса | 64 КБ | `413 request_too_large` |

- Лимиты проекта видны в кабинете, изменить их можно через вашего менеджера: напишите
  ему, если ожидаете больше заявок.
- Ответ `429` содержит `Retry-After` — сколько секунд подождать.
- Лимиты считаются по проекту: все ваши серверы делят один счётчик.
- Если учёт лимитов или nonce временно недоступен, API отвечает `503` с
  `Retry-After: 1` — это не ошибка вашего запроса, повторите его.

## Таймауты { #timeouts }

Создание заявки синхронно подбирает реквизиты. Держите таймаут
HTTP-клиента **не меньше 15 секунд**. Если таймаут всё же сработал, повторите запрос с тем
же `merchant_payment_id` — см. [выше](#idempotency).