На странице
Коды ошибок
Обрабатывайте ошибки Merchant API по стабильным машинным кодам, деталям валидации полей и документированным сценариям восстановления.
Каталог кодов
Стабильные машинно-читаемые идентификаторы возвращаются в code (а при ошибке по конкретному полю — в errors[].code). Список — фактически выдаваемый набор: каждый код ниже реально отдаёт хотя бы один обработчик, валидатор или middleware в API. Список только пополняется: семантика существующих кодов не меняется, новые коды добавляются под новые сценарии.
Используйте code для программной обработки. detail приходит только на английском — для логов и запасного UI; локализуйте на клиенте по code.
Коды поля
Возвращаются при валидации запроса. У кодов уровня поля (ниже) field содержит имя поля, не прошедшего проверку, в snake_case. Когда сразу несколько полей не проходят проверку, код верхнего уровня — validation_failed, а разбивка по полям лежит в errors[].
| Code | Когда | Что делать |
|---|---|---|
validation_failed |
Сразу несколько полей не прошли валидацию. Это код верхнего уровня; разбивка по полям — у каждого свой code и field — лежит в errors[]. |
Разберите errors[] и поправьте каждое перечисленное field. |
field_required |
Обязательное поле пустое, null или состоит только из пробелов. |
Передайте значение в field. |
field_too_long |
Поле длиннее допустимого максимума. | Обрежьте до лимита, указанного для этого эндпоинта. |
field_out_of_range |
Числовое значение вне допустимого диапазона. | Приведите значение к документированному диапазону. |
field_invalid_enum |
Значение не входит в набор допустимых значений enum (например, незнакомая currency / network, или токен не включён в проекте). |
Сверьтесь со справочником допустимых значений; для токенов — убедитесь, что пара токен+сеть включена в проекте. |
field_invalid_format |
Значение не соответствует формату поля: либо не парсится как id (например, invoice_id без префикса inv_…), либо amount передан не простой decimal-строкой вида "10.00". |
Передавайте поле в документированном формате; id — ровно в том виде, в каком их вернул API. |
entity_id_empty |
Переданный id — это нулевой (пустой) GUID (00000000-0000-…). field указывает на конкретный id — например, invoice_id или withdrawal_id. |
Передавайте id ровно в том виде, в каком его вернул API; не отправляйте пустой или обнулённый id. |
field_must_be_positive |
Десятичное или целое число должно быть больше 0. | Передайте положительное значение. |
field_percentage_out_of_range |
Процент должен быть в диапазоне [0, 100] включительно. |
Приведите значение к диапазону [0, 100]. |
query_parameter_unknown |
В list-запросе передан query-параметр, которого нет у этого эндпоинта. | Удалите параметр или используйте его документированное имя в snake_case. |
pagination_cursor_invalid |
Курсор повреждён, истёк, подделан, относится к другому ресурсу или совмещён с другими фильтрами. | Начните пагинацию без cursor, затем сохраняйте фильтры при переходе по next_cursor. |
amount_too_many_decimals |
Для инвойса с суммой в фиатной валюте amount содержит больше знаков после запятой, чем допускает минимальная единица валюты (например, 23.345 для USD, 23.01 для JPY). |
Округлите до минимальной единицы валюты. |
amount_too_large |
amount при создании инвойса или выплаты выходит за поддерживаемый диапазон — выше потолка в 20 целых знаков, который хранит API. |
Передайте сумму в пределах поддерживаемого диапазона. |
Резервные коды по HTTP-статусу
Возвращаются, когда у ошибки нет своего бизнес-кода. Каждый код — резервный вариант для HTTP-статуса: запрос не прошёл, а описать причину точнее было нечем.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
forbidden |
403 | Аутентификация прошла, но этому ключу операция не разрешена: не хватает возможностей (scope) или не то окружение — например, Sandbox-ключ на эндпоинте только для Production. | Используйте ключ с нужными возможностями и в нужном окружении. Владение никогда не даёт 403: чужой ресурс возвращает not_found (404), а не forbidden. |
payment_key_required |
403 | Эндпоинту нужен Payment-ключ (pk_), а запрос пришёл с Payout-ключом (rk_) — например, получение списка инвойсов. |
Вызовите его Payment-ключом. |
payout_key_required |
403 | Эндпоинту нужен Payout-ключ (rk_), а запрос пришёл с Payment-ключом (pk_) — например, чтение балансов. |
Вызовите его Payout-ключом. Учтите: Payout-ключу также нужен IP-whitelist, иначе он не работает. |
internal_error |
500 | Непредвиденная ошибка или баг на нашей стороне. | Повторите один раз. Если не проходит — обратитесь в поддержку и укажите время ответа. |
validation_failed |
400 | Запрос не прошёл валидацию, но кода под конкретное поле не нашлось. | Смотрите поле detail — там написано, что отклонено. |
Аутентификация
Возвращается только на 401 Unauthorized от обработчика HMAC. Каждый код указывает на конкретную точку отказа — клиент может показать осмысленное сообщение вместо общего «auth failed».
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
unauthorized |
401 | Запасной код, когда не подошёл ни один из кодов ниже — обычно запрос к эндпоинту с обязательной аутентификацией без заголовка Authorization (или с нераспознанной схемой). |
Отправьте корректный заголовок HMAC Authorization; проверьте всю сборку подписи. |
authorization_missing |
401 | Заголовок Authorization есть, но его значение пустое. |
Отправьте полный заголовок Authorization: HMAC-SHA256 {apiKeyId}:{base64signature}. |
authorization_malformed |
401 | Заголовок есть, но его формат нарушен: схема распознана, но в учётных данных нет двоеточия, не разбирается id API-ключа или пустая подпись. | Соберите заголовок по разделу Authentication; чаще всего — опечатка в id API-ключа. |
timestamp_missing |
401 | Заголовок X-Request-Timestamp отсутствует или не разбирается как положительное целое. |
Передайте текущую Unix-метку в секундах. |
timestamp_expired |
401 | Метка вышла за допустимое окно расхождения часов (по умолчанию ±5 минут — слишком в прошлом или слишком в будущем). | Синхронизируйте часы по NTP; пересчитайте подпись со свежей меткой. |
invalid_credentials |
401 | Ключ использовать нельзя: id не найден, ключ отозван, префикс окружения или типа не совпадает с сохранённым ключом либо подпись не сходится. Один код на все четыре случая — намеренно: разные ответы позволили бы тому, у кого нет секрета, выяснить, какие id ключей существуют. | Проверьте, что id ключа верный и ключ активен, а подпись считается секретом того же ключа. Чаще всего причина в подписи: сверьте сборку строки для подписи ({ts}\n{METHOD}\n{path}\n{query}\n{bodyHash}) и то, что для пустого тела bodyHash — пустая строка (не хешируйте пустую строку). |
ip_not_allowed |
401 | Для Payout-ключа задан белый список IP, и адрес клиента в него не входит. Проверка по IP есть только у Payout-ключей — Payment-ключи вызываются с любого адреса. Идёт после успешной подписи, поэтому корректная подпись с «не той» сети всё равно вернёт 401. | Добавьте IP клиента (или его CIDR-блок) в белый список Payout-ключа в дашборде. |
payout_whitelist_required |
401 | У только что созданного Payout-ключа ещё не задан белый список IP. Payout-ключи остаются неактивными, пока мерчант не добавит хотя бы один разрешённый IP — код отделён от ip_not_allowed, чтобы клиент мог отличить «не настроены параметры в дашборде» от «не тот адрес-источник». |
Откройте дашборд, перейдите на страницу API-ключей и добавьте хотя бы один IP или CIDR в белый список Payout-ключа. |
Лимит запросов
Возвращается при 429 Too Many Requests, когда запрос превышает лимит для мерчанта. Лимит считается на мерчанта, в окне в одну секунду — не на IP и не на ключ API.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
rate_limited |
429 | Превышен лимит запросов для мерчанта. Действуют два лимита: общий на все эндпоинты (по умолчанию 30 запросов в секунду) и строже — на создание инвойса, POST /v1/invoices (по умолчанию 5 запросов в секунду). В ответе указано, какой лимит сработал и на каком значении. |
Прочитайте заголовок Retry-After (пауза в секундах) и повторите запрос после неё. Большинство HTTP-клиентов с повторами учитывают Retry-After автоматически. |
Транспорт и системные ошибки
Возвращаются системным обработчиком ошибок, когда запрос не дошёл до обработчика самого эндпоинта.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
malformed_request |
400 | Тело запроса не удалось разобрать — повреждённый JSON. | Проверьте, что тело — валидный JSON. |
payload_too_large |
413 | Тело запроса превышает лимит в 1 MiB. | Отправьте payload поменьше; разбейте крупные объёмы на отдельные запросы. |
unsupported_media_type |
415 | Content-Type не application/json. |
Выставьте Content-Type: application/json. |
method_not_allowed |
405 | Путь существует, но не для этого HTTP-метода. | Используйте метод, указанный для эндпоинта (например, POST для создания). |
route_not_found |
404 | Ни один эндпоинт не подходит под этот путь. | Сверьте URL со справочником API. |
Инвойсы
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
invoice_not_found |
404 | Инвойс с таким id не существует или принадлежит другому мерчанту. | Используйте id, возвращённый POST /v1/invoices, без изменений. |
project_not_found |
404 | project_id в запросе на создание инвойса не существует или не виден вызывающему. |
Используйте project id из дашборда (проект должен быть активен и принадлежать тому же мерчанту, что и API-ключ). |
invoice_terminal |
410 | confirm-payment вызван на инвойсе со статусом expired или cancelled. (Инвойс paid возвращает invoice_not_awaiting_client, 409, а не этот код.) |
Обновите статус инвойса; делать больше нечего. |
invoice_deadline_passed |
410 | confirm-payment вызван после истечения срока инвойса. |
Создайте новый инвойс. |
requested_amount_invalid |
400 | amount в запросе на создание инвойса отсутствует, не разбирается, выходит за поддерживаемый диапазон десятичных значений или не положителен. |
Передайте amount положительной десятичной строкой вместе с выбранной currency и при необходимости — network. |
invoice_amount_below_minimum |
400 | USD-эквивалент инвойса ниже минимума для токена/сети из каталога. | Увеличьте сумму выше минимума, возвращённого для выбранного токена. |
deposit_amount_exceeds_limit |
400 | USD-эквивалент ожидаемого платежа превышает лимит на один платёж MaxDepositAmount из политики мерчанта. Ограничивает рассчитанную сумму инвойса при подтверждении, а не фактический приход в блокчейне. |
Уменьшите сумму инвойса или запросите повышение лимита в поддержке. |
currency_not_enabled_for_project |
400 | Запрошенная криптовалюта не входит в enabled_tokens проекта (ни в одной сети). |
Включите валюту в проекте либо выберите из enabled_tokens. |
tokens_required |
409 | Создание инвойса или подтверждение платежа для проекта, где не включён ни один токен. | Включите в проекте хотя бы один токен через дашборд. |
acceptance_disabled |
503 | Приём запрошенного токена сейчас отключён глобально. | Используйте другой токен или дождитесь возобновления. |
exchange_rate_unavailable |
503 | Нет свежего курса для пары токен/фиат. | Повторите запрос; курсы обновляются часто. |
network_unavailable |
503 | При автоподтверждении не удалось зарезервировать адрес: пул адресов сети или сама сеть недоступны. | Откажитесь от автоподтверждения; дайте клиенту выбрать токен на странице оплаты Paymos. |
no_available_address |
503 | Нет свободного адреса для токена/сети. | Повторите запрос. |
address_lease_failed |
503 | Не удалось зарезервировать адрес для зачисления платежа. | Повторите запрос. |
bridge_unavailable |
503 | Мост для выбранной сети временно недоступен, поэтому при подтверждении не удалось выделить в ней адрес для зачисления. | Повторите запрос — перебой временный. |
client_confirmation_failed |
409 | Подтверждение со стороны клиента (выбор токена, автоподтверждение) отклонено. detail содержит причину. |
Смотрите detail; типично — инвойс уже не в awaiting_client или токен не разрешён. |
invoice_not_awaiting_client |
409 | confirm-payment вызван на инвойсе, уже вышедшем из awaiting_client. |
Обновите статус инвойса; клиент уже прошёл выбор. |
invoice_cannot_be_cancelled |
409 | Инвойс уже не в awaiting_client — клиент выбрал токен либо инвойс уже paid или expired. Повторная отмена уже отменённого инвойса возвращает 200, а не эту ошибку. |
Проверьте статус; отмена работает только в awaiting_client. |
simulate_payment_failed |
409 | Вызов simulate_payment для тестового режима отклонён. |
Смотрите detail; обычно — несоответствие статуса или суммы. |
not_sandbox |
403 | simulate-payment вызван для счёта рабочего режима. |
Симуляция доступна только для тестового счёта. Для рабочего счёта отправьте реальный перевод в выбранной сети. |
Инвойсы через виджет
Эти коды возвращает эндпоинт POST /public/v1/sdk — создание счёта через виджет-SDK.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
widget_key_missing |
401 | Заголовок X-Sdk-Key отсутствует или пуст на эндпоинте, где нужна авторизация виджета. |
Передайте публичный SDK-ключ в заголовке X-Sdk-Key. |
widget_key_invalid |
401 | X-Sdk-Key передан, но не распознаётся как идентификатор ключа Paymos. |
Скопируйте ключ из панели управления; проверьте префикс и что в значение не попали лишние пробелы или URL-кодирование. |
widget_key_not_found |
401 | Ключ распознан, но активной записи за ним нет: он отозван, удалён или это не Payment-ключ (ключи Payout виджетом не управляют). | Возьмите активный Payment-ключ из панели управления. |
widget_inactive |
403 | Виджет проекта выключен или ещё ни разу не был инициализирован. | Включите виджет в настройках проекта. |
pos_not_enabled |
403 | Запрос пришёл с source=pos, но у виджета проекта режим POS не включён. |
Включите POS в настройках виджета или передавайте source=embed. |
origin_not_allowed |
401 / 403 | Браузерный Origin не входит в список разрешённых доменов. Проверок две: 401 на уровне авторизации, где origin сверяется с объединённым списком всех проектов ключа с активным виджетом, и 403 в обработчике, где сверка идёт с конкретным проектом запроса. |
Добавьте домен в разрешённые источники виджета. Запросы с того же домена — например, с POS-терминала на том же хосте — проверку пропускают. |
Выводы
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
withdrawal_not_found |
404 | Вывода с таким id нет либо он принадлежит другому мерчанту. | Используйте id, который вернул POST /v1/withdrawals, без изменений. |
insufficient_balance |
409 | Доступный баланс мерчанта ниже amount + fee. |
Пополните баланс или уменьшите сумму вывода. |
whitelist_required |
403 | Адрес назначения не добавлен в белый список для выбранной сети. (Сеть белый список поддерживает — просто этот адрес ещё не внесён.) | Добавьте адрес в белый список и повторите запрос. |
destination_address_invalid |
400 | Адрес не проходит проверку формата или контрольной суммы для этой сети. | Сверьте адрес со спецификацией сети. |
withdrawal_amount_below_minimum |
400 | Сумма ниже минимума для выбранного токена. | Увеличьте сумму выше минимума (возвращается в detail). |
withdrawal_quota_exceeded |
409 | Достигнут лимит активных выводов. | Дождитесь завершения текущих или запросите повышение лимита. |
withdrawal_amount_exceeds_limit |
400 | USD-эквивалент тела вывода превышает лимит на один платёж MaxWithdrawalAmount из политики мерчанта. |
Уменьшите сумму или запросите повышение лимита в поддержке. |
exchange_rate_unavailable |
503 | У мерчанта задан лимит MaxWithdrawalAmount, но свежего курса токен/USD для проверки тела вывода нет — лимит не подтвердить, поэтому запрос отклоняется. |
Повторите запрос; курсы обновляются часто. |
withdrawal_fee_not_configured |
400 | Paymos не выводит запрошенную пару токен/сеть. Пара названа в detail — например Withdrawals are not available for USDT on TRC20. Какие пары доступны для вывода, настраиваем мы, а не вы. |
Выведите токен в сети, по которой Paymos делает выплаты, либо спросите в поддержке про нужную пару. |
withdrawal_cannot_be_cancelled |
409 | Вывод уже вышел из состояния, в котором его можно отменить. | Проверьте текущий статус: отмена работает только до отправки в сеть. |
withdrawal_simulation_failed |
409 | Вызов simulate_completion для тестового режима отклонён. |
Смотрите detail. |
merchant_suspended |
403 | Мерчант приостановлен для исходящих операций, поэтому создать вывод нельзя. | Обратитесь в поддержку, чтобы снять приостановку. |
outbound_frozen |
503 | Исходящие операции заморожены — либо на всей платформе, либо для той сети и токена, в которых вы выводите. | Дождитесь снятия, попробуйте другую сеть или токен либо обратитесь в поддержку. |
hot_wallet_insufficient_liquidity |
503 | В горячем кошельке целевой сети недостаточно ликвидности в блокчейне для этой выплаты. | Попробуйте другую сеть назначения или обратитесь в поддержку. |
Состояние проекта
Возвращается при создании инвойса или подтверждении платежа, если статус проекта не позволяет их выпускать.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
project_state_invalid |
409 | Проект не в статусе Active — он Suspended или Archived. Какой именно, ответ не сообщает. |
Посмотрите статус проекта в дашборде: архивный — восстановите, приостановленный — активируйте. Либо отправьте запрос в уже активный проект. |