Статусы и ошибки¶
Статусы заявки¶
| Статус | Финальный | Что значит |
|---|---|---|
| created | нет | Заявка принята, реквизиты ещё не выданы |
| processing | нет | Реквизиты выданы, ждём перевод плательщика |
| completed | да | Оплата подтверждена |
| canceled | да | Не оплачено: истёк срок заявки, её отменили или оплату не удалось подтвердить после выдачи реквизитов |
| error | да | Реквизиты не выданы, причина — в error_code |
| appeal | нет | Оплату по закрытой заявке перепроверяют |
Ответ на создание может сразу прийти в любом статусе, кроме appeal. Финальный статус
меняется только после проверки оплаты или решения службы поддержки — см.
Смена статуса после закрытия.
error бывает только до выдачи реквизитов: из created, обычно сразу в ответе на
создание. Если реквизиты уже выданы (processing), заявка заканчивается только completed
или canceled. Если после выдачи оплату не удалось подтвердить, заявка тоже становится
canceled, без error_code.
Срок заявки задаёт настройка проекта: по умолчанию 15 минут с created_at. Заявка, не
оплаченная к концу срока, становится canceled.
Callback приходит только о completed и canceled — при подтверждении оплаты,
отмене, истечении срока, после проверки оплаты и после решения службы поддержки.
О статусе error callback не приходит: его видно в ответе на создание и при чтении
заявки. Подробнее — в разделе
Callback-и.
Коды error_code¶
error_code и error_comment есть только у заявки в статусе error — в ответе на
создание и при чтении. Код объясняет, почему реквизиты не выданы. У canceled кода нет,
в том числе когда оплату не удалось подтвердить. Для плательщика во всех
случаях одно действие: оплатить заново, новой заявкой с новым merchant_payment_id.
| Код | Что случилось |
|---|---|
provider_unavailable |
Платёж сейчас нельзя принять: способ оплаты временно недоступен |
provider_no_requisites |
Не нашлось свободных реквизитов |
rate_unavailable |
Нет курса для расчёта |
provider_result_ambiguous |
Выдача реквизитов не подтвердилась, реквизиты не выданы |
provider_result_unknown |
Результат выдачи реквизитов неизвестен, реквизиты не выданы |
stale_dispatch |
Реквизиты не удалось получить вовремя |
invalid_payment |
Заявка не прошла проверку полей |
json_invalid |
Тело запроса не разобралось |
Список может пополняться: обрабатывайте незнакомый код как общую ошибку.
Ошибки HTTP¶
Тело ошибки — всегда JSON:
Content-Type: application/json, заголовок X-Request-ID возвращается как в запросе.
| HTTP | error |
Когда | Что делать |
|---|---|---|---|
| 400 | bad_request |
Тело не JSON, неизвестное поле, лишний текст после объекта | Исправить запрос |
| 400 | invalid_payment |
Поле не прошло проверку: сумма, номер заказа, гео, метод, банк, callback_url и т. п. |
Исправить запрос |
| 401 | token_invalid |
Нет токена, он неизвестен, отозван, истёк, или проект не активен | Проверить токен в кабинете |
| 401 | signature_invalid |
Подпись не прошла проверку | Чек-лист подписи |
| 403 | insufficient_scope |
У токена нет нужного права | Выпустить токен с payments:create / payments:read |
| 403 | ip_not_allowed |
Адрес не в allowlist проекта | Добавить адрес в кабинете |
| 403 | project_blocked |
API проекта закрыт сотрудником | Связаться с менеджером |
| 403 | merchant_blocked |
Мерчант заблокирован | Связаться с менеджером |
| 403 | merchant_archived |
Мерчант удалён | Связаться с менеджером |
| 404 | payment_not_found |
Заявки нет или она чужая | Проверить id |
| 409 | request_replayed |
Этот X-Request-Nonce уже был |
Повторить с новым nonce и новой подписью |
| 409 | payment_idempotency_conflict |
merchant_payment_id уже занят заявкой с другими полями |
Новый номер или те же поля, см. Идемпотентность |
| 413 | request_too_large |
Тело больше лимита проекта (по умолчанию 64 КБ) | Уменьшить тело |
| 422 | payment_not_covered |
Метод, валюта или сумма вне вашего договора | Проверить условия с менеджером |
| 422 | payment_rate_unavailable |
Нет курса | Повторить позже новой заявкой |
| 422 | payment_not_routable |
Сейчас платёж этим способом принять нельзя | Повторить позже новой заявкой |
| 429 | rate_limited |
Превышен лимит запросов | Подождать Retry-After секунд |
| 429 | too_many_concurrent_requests |
Больше 20 одновременных запросов проекта | Подождать Retry-After (1 с) и повторить тот же запрос |
| 500 | internal_error |
Сбой на стороне API | Повторить с паузой |
| 503 | rate_limit_unavailable |
Временно недоступен учёт лимитов | Подождать Retry-After и повторить |
| 503 | replay_protection_unavailable |
Временно недоступна проверка nonce | Подождать Retry-After и повторить |
Если заявка при 422 успела сохраниться (в статусе error), в теле есть её id:
Когда повторять¶
| Ответ | Повторять? | Как |
|---|---|---|
| Таймаут, обрыв связи | да | Тот же запрос и merchant_payment_id, новые nonce и подпись |
429, 503 |
да | Через Retry-After секунд, затем с растущей паузой |
500 |
да | С растущей паузой: 1, 2, 4, 8 секунд… |
409 request_replayed |
да | С новым nonce и новой подписью |
401, 403, 400, 404, 413 |
нет | Сначала исправить причину |
422, статус error |
нет | Для новой попытки — новая заявка с новым merchant_payment_id |
Повтор с тем же merchant_payment_id безопасен: второй заявки не будет. Подробнее —
в разделе Идемпотентность и лимиты.
# Повтор с растущей паузой: тело и merchant_payment_id те же, подпись — новая.
for attempt in range(5):
headers = sign_request("POST", url, body, key, key_id) # новый nonce каждый раз
response = send(url, body, headers)
if response.status in (429, 503):
time.sleep(int(response.headers.get("Retry-After", "1")))
continue
if response.status >= 500 or response.status == 0: # 0 — таймаут или обрыв
time.sleep(2 ** attempt)
continue
break