ABSOLUT PAYMerchant API · ДокументацияPostman ↗OpenAPI ↗

Absolut Pay API

Приём платежей, выплаты и баланс магазина. Начните с доступа и подписи или выберите нужную операцию.

Окружения и доступность
API
https://api.absolutpay.com
Платёжная форма
Не подтверждён; используйте полный payment_url из API
Документация
https://api.absolutpay.com

API base URL подтверждён 25.10.2026: https://api.absolutpay.com (без /api/v2). HTTPS и /livez доступны; проверенные GET /api/v2/banks и GET /api/v2/balance возвращают 503. Доступность бизнес-операций не подтверждена. Адрес не означает разрешение реальных переводов; подключение магазина и методов согласуется отдельно. Origin платёжной формы не подтверждён. Портал не отправляет API-запросы.

Начало работы

Интеграция выполняется с вашего сервера. Плательщик получает реквизиты либо открывает платёжную форму; API token и secret остаются на сервере мерчанта.

Что получить от Absolut Pay

ДанныеНазначение
Адрес API для согласованного окруженияТочный BASE_URL, без добавленного /api/v2
API token и API secret магазинаОдин магазин определяется его проверенными ключами; shop_id в запросе не передаётся
Подтверждение разрешённых исходящих IPПубличные адреса вашего backend; пустой список запрещает доступ
Разрешённые методы, валюты и тип трафикаНе все сочетания словаря доступны каждому магазину
Адрес вашего приёмника webhookДоступный HTTPS endpoint для уведомлений, без редиректов
Согласованный канал поддержкиПередавайте request_id и свой номер заказа, а не секреты и полные платёжные ссылки

Подтверждённый API base URL: https://api.absolutpay.com, без добавленного /api/v2. Например, полный адрес чтения баланса — https://api.absolutpay.com/api/v2/balance. Проверено 25.10.2026: HTTPS и /livez доступны; GET /api/v2/banks и GET /api/v2/balance возвращают HTTP 503. Доступность бизнес-операций не подтверждена; рабочие ключи, разрешённые IP и активация магазина согласуются отдельно. Проверка health не подтверждает готовность платежей. Origin hosted payment page пока не подтверждён: используйте только полный payment_url, возвращённый API.

Рекомендуемый порядок

  1. Сохраните ключи в серверном хранилище секретов и согласуйте IP вашего backend.
  2. Настройте точное формирование подписи и синхронизацию системных часов.
  3. Реализуйте собственную таблицу платежных попыток с merchant_order_id, полученным id, текущим состоянием и историей обработки.
  4. Реализуйте проверку webhook и устойчивую обработку повторов.
  5. Создайте одну тестовую заявку. Проверьте чтение по своему номеру и по выданному id.
  6. Проверьте потерю ответа и повторные уведомления. В локальном контуре merchant webhook подавлен: его формат проверяется отдельными подписанными примерами, не ожиданием доставки после тестового POST.
  7. Пройдите согласованный полный сценарий на предоставленном окружении перед включением реального приёма.

Первый запрос

Файл examples/create-payin.json содержит тестовый запрос. Замените номер заказа и данные плательщика своими тестовыми значениями. Адрес webhook в файле — заполнитель: в окружении с доставкой укажите ваш настоящий HTTPS endpoint.

Задайте ABSOLUT_API_BASE_URL=https://api.absolutpay.com, ABSOLUT_API_TOKEN, ABSOLUT_API_SECRET в окружении серверного процесса. Не записывайте реальные значения в файлы примеров, историю команд, исходники или клиентский JavaScript.

sh
# Команды выполняются из папки merchant-v2.
node examples/client.mjs create --body examples/create-payin.json
node examples/client.mjs result shop-order-0001
node examples/client.mjs get ord_123

ord_123 — условный идентификатор, замените его значением реального ответа. Создание выполняется только по явной команде create; примеры не повторяют POST автоматически. Полный ответ может содержать конфиденциальную ссылку или реквизиты: не направляйте вывод примера в общие логи.

Два режима выдачи

modeРезультат
h2hAPI возвращает requisite; ваш интерфейс отображает реквизиты
payment_formAPI возвращает payment_url; откройте именно эту ссылку

В текущем локальном контуре реквизиты имеют тип mock и значение TEST-ONLY-NOT-PAYABLE. По ним нельзя отправлять деньги. Возможности живых реквизитов согласуются после подключения реального провайдера.

Успешное создание HTTP 201 означает выдачу платежа, а не факт оплаты. Выдавайте оплаченный товар/услугу только после проверенного статуса 2 и собственной защищённой обработки результата.

Подключение выплат

Для выплат согласуйте отдельные условия магазина и способ исполнения. Прочитайте Выплаты клиентам, получите наш bank_id из справочника, проверьте доступный USDT-баланс и подготовьте отдельный уникальный номер поручения. Условия приёма не определяют курс и комиссию выплаты.

Аутентификация запросов

Каждый запрос Payin API v2 подписывает ваш сервер. Для магазина выдаётся пара api_token и api_secret; она определяет магазин, от имени которого выполняется запрос. Передавать shop_id в теле не требуется и не допускается.

Настройте адрес API, выданный Absolut Pay для вашего подключения. Наличие примеров в документации не означает, что тестовый или рабочий адрес уже активирован. В проверенной версии API доступен в локальном тестовом окружении; рабочее подключение согласовывается отдельно.

Что необходимо перед первым запросом

  • Токен и секрет нужного магазина.
  • Добавленные в разрешённый список исходящие IP-адреса вашего сервера.
  • Синхронизированное время на сервере.
  • Активный магазин и разрешённые условия приёма для создания новых платежей.

Храните секрет в серверном хранилище конфигурации. Не передавайте его в браузер, мобильное приложение, query string или тело запроса. Не записывайте секрет, токен и полный набор подписанных заголовков в обычные логи.

Заголовки

Каждый заголовок должен присутствовать ровно один раз.

ЗаголовокЗначение
X-API-TokenВыданный токен магазина: pt_ и 64 строчных шестнадцатеричных символа
X-TimestampТекущее Unix-время в секундах, десятичная строка без пробелов и ведущих нулей
X-NonceНовый UUID версии 4 для этого HTTP-запроса
X-SignatureHMAC-SHA512, 128 строчных шестнадцатеричных символов
Content-Typeapplication/json для JSON-запроса создания платежа

Сервер принимает время запроса в пределах ±300 секунд от своего текущего времени. Миллисекунды вместо секунд не подходят. Рекомендуем использовать обычную строчную запись UUID; если вы используете другую допустимую запись, подпись должна содержать именно отправленную строку.

Строка для подписи

Соедините пять компонентов в таком порядке, используя байт перевода строки LF, \n:

text
timestamp + "\n" +
nonce + "\n" +
UPPERCASE_METHOD + "\n" +
exact_path_and_query + "\n" +
raw_body

Между компонентами ровно четыре LF. CRLF, пробелы около разделителей и дополнительные переводы строки не добавляются.

КомпонентКак получить
timestampТочная строка из X-Timestamp
nonceТочная строка из X-Nonce
UPPERCASE_METHODHTTP-метод в верхнем регистре: POST или GET
exact_path_and_queryПуть начиная с / и, если есть, ? с query string в том виде, в котором они отправлены
raw_bodyИсходные UTF-8 байты тела; для GET — пустые байты

В подпись не входят схема и домен. Например, для запроса к BASE_URL/api/v2/payins подписывается /api/v2/payins.

Вычислите:

text
signature = lowercase_hex(HMAC_SHA512(api_secret, canonical_bytes))

Ключ HMAC — буквальная строка выданного api_secret. Не декодируйте её из Base64 или Base64URL, даже если её вид напоминает такую кодировку. Не заменяйте исходное тело его хешем.

POST с JSON

Сначала сериализуйте объект в UTF-8 байты один раз. Эти же байты используйте для подписи и отправки.

text
1791460800\n
01234567-89ab-4cde-8fab-0123456789ab\n
POST\n
/api/v2/payins\n
{"merchant_order_id":"invoice-10428","amount":"15000.00",...}

Здесь \n обозначает один LF; многоточие показывает сокращение примера и не является допустимым телом запроса. Полный JSON нужно взять из описания создания платежа.

Изменение порядка JSON-полей, пробелов, экранирования или добавление перевода строки после подписи даст другие байты. Если HTTP-библиотека автоматически сериализует объект, убедитесь, что она отправляет именно подписанную сериализацию; проще передавать подготовленные байты.

GET с query string

Для поиска платежа по вашему номеру заказа сначала сформируйте полный закодированный путь. Например:

text
/api/v2/payins/result?merchant_order_id=invoice-10428

Тело GET пустое. Поэтому канонические байты заканчиваются LF, который разделяет путь и пустое тело:

text
1791460800\n01234567-89ab-4cde-8fab-0123456789ab\nGET\n/api/v2/payins/result?merchant_order_id=invoice-10428\n

После этого LF дополнительных байтов нет. Это не произвольный завершающий перевод строки: он нужен перед пустым пятым компонентом.

Подписывайте query string после URL-кодирования. Не меняйте порядок параметров, + на %20, %2f на %2F или другие байты между подписью и отправкой. Для этого маршрута разрешён ровно один параметр merchant_order_id; дополнительные и повторяющиеся параметры не принимаются.

IP-адреса и область доступа

Разрешённый список относится к исходящему сетевому адресу вашего API-сервера. Поле payer.ip — IP плательщика для обработки заявки; оно не определяет доступ вашего сервера к API.

Пустой разрешённый список запрещает все запросы. Если сервер работает за NAT или использует несколько исходящих адресов, согласуйте фактические адреса заранее. Произвольные X-Forwarded-For и X-Real-IP, отправленные клиентом, не дают доступ с другого IP.

Ключи одного магазина не позволяют читать платежи другого магазина. Для создания новых платежей магазин и его группа должны быть активны. Чтение уже созданных платежей и результатов остаётся доступным после отключения приёма, пока действительны ключи и разрешён исходящий IP.

Повторы и восстановление результата

Для каждого нового HTTP-запроса, включая повтор и поиск результата, создавайте новый UUID v4, свежий timestamp и новую подпись. Уже принятый nonce нельзя использовать повторно, даже если ответ потерялся.

Nonce защищает HTTP-запрос от повторного воспроизведения. merchant_order_id защищает создание платежа от дублирования. Это разные значения с разным назначением:

ЗначениеПоведение
X-NonceНовое для каждого запроса
merchant_order_idОдин устойчивый номер для конкретного платежа в магазине

При тайм-ауте или потере ответа на создание ищите результат по прежнему merchant_order_id новым подписанным GET. Не создавайте другую заявку под новым номером, пока исходный результат не выяснен. Подробный порядок восстановления приведён в описании Payin API.

Смена ключей

При ротации токен и секрет магазина заменяются. Обновляйте их на своём сервере одной парой. Новые запросы со старой парой будут отклонены; запрос, уже принятый до ротации, может продолжить обработку.

Тот же секрет используется для проверки уведомлений о платежах. Согласуйте ротацию с обработчиком webhook: новый запрос уведомления подписывается текущим секретом, но уже начатая отправка может завершиться со старым. Правила короткого переходного периода проверки описаны в руководстве по webhook.

Ошибки и диагностика

HTTP и кодЧто проверить
401 authentication_failedЗаголовки, время, UUID v4, свежий nonce, актуальность пары ключей и точные байты подписи
403 ip_not_allowedФактический исходящий IP и разрешённый список магазина
403 shop_disabledАктивность магазина/группы для создания новых платежей
503 internal_errorСохранить контекст запроса и получить результат новым подписанным чтением; не создавать другой платёж вслепую

Причины ошибок подписи, неизвестного токена и повторного nonce объединены в authentication_failed; API не раскрывает внутреннюю причину. Для обращения в поддержку сохраните request_id, свой merchant_order_id, время, метод и маршрут. Секрет и токен в обращение не включайте.

Ограничения подписанного запроса: тело до 1 МиБ UTF-8, путь с query string до 8192 байтов. GET с непустым телом не принимается.

Методы оплаты и валюты

Для приёма выберите как платит клиент — method_id, и в каких денежных единицах задана сумма — currency. Это два разных поля. Например, method_id: 2 означает оплату картой, а currency: "UZS" — сумму в узбекских сумах. Такая комбинация работает только после подключения для вашего магазина.

Методы приёма: какой method_id передавать

method_id — целое число в запросе POST /api/v2/payins. Передавайте 2, а не "2", "card" или название банка.

method_idНазваниеЧто означает для клиентаЧто передаёт мерчант
1СБПОплата через Систему быстрых платежейmethod_id: 1, согласованную валюту и сумму. Банк и реквизиты приёма выдаёт сервер; bank_id при создании не передаётся
2Банковская картаОплата по карточным реквизитамmethod_id: 2, согласованную валюту и сумму. Номер карты плательщика в Create Payin не запрашивается
3QRОплата с использованием QRmethod_id: 3, согласованную валюту и сумму. QR не означает конкретный банк или платёжную систему: формат выдачи должен поддерживать подключённый провайдер
4Мобильный платёжМетод мобильного платежаmethod_id: 4, согласованную валюту и сумму. Оператор, страна и способ исполнения определяются подключением; отдельного поля выбора оператора в текущем Create Payin нет
5Трансграничный переводОтдельный метод cross_border для перевода между странамиID согласован; проектируемая возможность. В текущий API пока не передавать

Методы 1–4 входят в текущий серверный контракт. За Трансграничным переводом (cross_border) по согласованию закреплён method_id: 5. Поддержка на сервере ещё не реализована: текущий API отклонит 5. Произвольные другие номера не допускаются. Подробнее — Трансграничный перевод.

Названия в таблице объясняют назначение методов, а не подтверждают живое подключение: текущий Payin-контур возвращает только mock / TEST-ONLY-NOT-PAYABLE. Нельзя обещать настоящие карту, QR или мобильные реквизиты до проверки провайдера. У карточного метода нет публичных отдельных ID для Visa, Mastercard, HUMO или Uzcard; не подставляйте чужие номера методов.

Как показать оплату клиенту

mode определяет интерфейс, а не новый метод оплаты. Оба значения передаются отдельно от method_id:

modeЧто возвращает серверЧто сделать на своей стороне
h2hrequisite — реквизитыОтобразить выданные реквизиты и срок; не придумывать отсутствующие поля. Текущие mock-реквизиты неплатёжные
payment_formpayment_url — полный URLОткрыть полученный URL; не собирать ссылку из ID или домена документации

Для всех методов используйте один запрос создания приёма, одну схему подписи и проверку статуса. Получение HTTP 201 означает создание, а подтверждённая оплата — status_id: 2.

Валюты и суммы

В currency передаётся строка с кодом в верхнем регистре. Числового currency_id в текущем Merchant API v2 нет.

Код currencyНазваниеДенежная единицаПример amountЧто означает пример
RUBРоссийский рубльРубли"1500.00"1 500 рублей
AZNАзербайджанский манатМанаты"1500.00"1 500 манатов
KZTКазахстанский тенгеТенге"1500.00"1 500 тенге
KGSКыргызский сомСомы"1500.00"1 500 сомов
UZSУзбекский сумСумы"1500.00"1 500 сумов

Все пять кодов принимаются валидаторами приёма и выплат, но разрешённые пары для магазина настраиваются отдельно по каждому направлению. Примеры показывают единицы, а не эквивалентные суммы или допустимые лимиты. Код валюты не подтверждает страну отправителя/получателя и не включает трансграничный перевод.

amount — сумма в основных единицах валюты, не в копейках и не в USDT. "1500.00" RUB означает 1 500 рублей, а не 15 рублей. Не умножайте сумму на 100 перед отправкой. KGS и UZS — разные валюты; их названия «сом» и «сум» не взаимозаменяемы.

ПравилоПриёмВыплата
Тип amountDecimal-строка, например "1500.00"Decimal-строка, например "1500.00"
ЗначениеБольше нуляБольше нуля
ФорматБез знака, экспоненты, запятой и пробелов; до 80 символовДо 18 цифр целой части и до 2 после точки
Дробная частьВалидатор не задаёт единый предел 2 знака; точность и денежные лимиты согласуются для пары магазина/терминалаНе более 2 знаков для всех пяти кодов в текущей реализации
Расчёты в вашей системеСтроки/Decimal, без binary floatСтроки/Decimal, без binary float

USDT — валюта учётного баланса и расчётных полей. Её нельзя передать в currency Create Payin/Create Payout. GET /api/v2/balance возвращает USDT независимо от валюты платежа; значения баланса имеют 8 знаков после точки. USD, EUR, RUR и строчные rub текущими валидаторами не принимаются.

Как подключить нужную валюту

  1. Определите направление: приём оплаты от клиента или выплата клиенту.
  2. Выберите метод и валюту отдельно. Например, для приёма картой в UZS это method_id: 2 и currency: "UZS".
  3. Получите подтверждение подключения этой пары для вашего магазина и окружения: активные условия, минимальная/максимальная сумма, точность, комиссия, курс, режим выдачи и исполнитель. В текущем API нет метода, который вернёт полную доступную матрицу; её предоставляет команда подключения.
  4. Для приёма заполните сумму в выбранной валюте, payer, traffic_type, mode и адрес webhook. Сохраните уникальный merchant_order_id до отправки, подпишите фактически отправляемые байты.
  5. Для выплаты используйте строковый method и реквизиты получателя из таблицы ниже. Проверьте отдельные условия выплат, канонический банк и достаточный USDT-баланс.
  6. После отправки обработайте ответ, webhook и lookup. Проверяйте валюту и подтверждённую сумму, не выдавайте товар по факту создания заявки и не повторяйте POST при timeout.

Изменение только currency в прежнем запросе не конвертирует сумму. Если было "1500.00" RUB и вы заменили код на UZS, теперь вы запросили 1 500 UZS. Для новой намеренной операции используйте новый номер; после неизвестного результата старой сначала выполните lookup.

Пример: приём картой в UZS

Пример формата после согласования пары карта + UZS. Он не подтверждает подключение провайдера или достаточность суммы для лимитов. Текущий sandbox не выдаёт платёжных реквизитов.

json
{
  "merchant_order_id": "shop-card-uzs-0001",
  "amount": "150000.00",
  "currency": "UZS",
  "method_id": 2,
  "mode": "payment_form",
  "traffic_type": "primary",
  "payer": {
    "id": "customer-1001",
    "ip": "203.0.113.10"
  },
  "webhook_url": "https://merchant.example.invalid/webhooks/absolut-pay"
}

Здесь клиенту предлагается оплатить 150 000 UZS. Сохраните JSON в файл, задайте ключи и выданный API origin в серверном окружении, отправьте тем же клиентом:

sh
node examples/client.mjs create --body request-uzs.json
node examples/client.mjs result 'shop-card-uzs-0001'

При сформированном платеже откройте полученный payment_url. Для H2H поменяйте mode на h2h до создания новой операции и обрабатывайте requisite. Для AZN/KZT/KGS используется тот же маршрут и схема запроса; сумма уже должна быть рассчитана именно в выбранной валюте, а пара с методом — отдельно подключена. Алгоритм подписи от валюты не меняется.

Выплаты: какой метод и реквизиты передавать

Выплаты используют POST /api/v2/payouts и строковое поле method, а не числовой method_id. Не копируйте тело приёма в выплату.

methodНазначениеОбязательные реквизиты recipientbank_id
sbpВыплата по СБПphone_number в международном формате с + и customer_nameОбязателен, из справочника Absolut Pay
cardВыплата на картуcard_number: 12–19 цифр без пробелов; customer_nameНеобязателен по схеме; требования и маршрут согласуются. Без банка — авторизованный ручной путь
accountВыплата на счётaccount_number: 8–34 заглавных латинских букв/цифр; customer_nameНеобязателен по схеме; текущий путь исполнения ручной

Передавайте ровно один вид реквизита выбранного метода. Для выплаты нет публичных методов qr и mobile. Во всех вариантах amount — сумма получателю в указанной currency; курс и комиссию в запросе вы не задаёте.

Пример формата выплаты на счёт в KZT после согласования отдельной пары account + KZT. Номер счёта и получатель синтетические, не предназначены для реального перевода:

json
{
  "merchant_order_id": "shop-account-kzt-0001",
  "amount": "15000.00",
  "currency": "KZT",
  "method": "account",
  "recipient": {
    "account_number": "TESTACCOUNT00000001",
    "customer_name": "Тестовый получатель"
  }
}

Это поручение на 15 000 KZT, а резерв рассчитывается в USDT. HTTP 201 не означает перечисление. Без webhook проверяйте результат через подписанный API. Поля и ошибки создания выплаты · Получить банки.

Как связаны валюта клиента и USDT

Приём: rate — сколько единиц валюты платежа приходится на 1 USDT. Для UZS это UZS за USDT; для RUB — RUB за USDT. fee — проценты. Сохраняйте исходный amount и фактический success_paid отдельно.

Выплата: rate_fiat_per_usdt имеет то же направление — единиц валюты выплаты за 1 USDT. Сервер возвращает principal_usdt, fee_usdt, total_usdt; не пересчитывайте резерв по текущему курсу. Условия приёма не задают курс и комиссию выплаты.

Курс UZS/USDT не является курсом RUB/UZS. Приём в UZS сам по себе не означает, что отправитель платит RUB. Две валюты отправителя и получателя относятся к отдельному проекту трансграничного метода.

Если выбранная пара не работает

ОшибкаЧто проверить
invalid_request / invalid_amountВ приёме: точные имена полей, числовой ID 1–4, код валюты и decimal-строку
unsupported_payment_configurationВ приёме: активные условия именно выбранной пары и режима. Не подменяйте метод/валюту автоматически
no_requisiteРеквизиты не выданы. Проверьте lookup исходного запроса и подключение; это не успешная оплата
invalid_payout_requestВ выплате: строковый method, валюту, точность суммы, один тип реквизитов и канонический bank_id
payout_configuration_disabledОтдельные активные условия выплат для пары
insufficient_fundsДоступный USDT-баланс должен покрывать основную стоимость и комиссию

Полные объяснения и правила повторов — Ошибки. Не пробуйте другую валюту, ID или новый номер заказа как автоматический обход отказа.

Что фактически доступно

Приём проверен в локальном sandbox. Публичный API origin подтверждён, но доступность бизнес-операций, production-сочетаний и реальных коридоров этой публикацией не подтверждается. У выплат реализованы создание, учётный резерв и ручное исполнение; настоящий автоматический провайдер не подтверждён. Настройки магазина и терминала должны разрешать конкретную пару; справочник банков не заменяет эту проверку.

Матрица реализации в JSON фиксирует ограничения версии. Адреса API и платёжной формы выдаются отдельно от домена документации.

Справочник и выбор банка

Получите ID из справочника Absolut Pay. Выбор банка через bank_id относится к выплатам, не к созданию входящего платежа. Наш ID не является кодом НСПК, БИК или кодом провайдера. Он не меняется при переименовании банка и не назначается другому банку.

Список банков и платёжных организаций

Ниже — все 250 записей предоставленного справочника от 08.10.2026: банки, кошельки и мобильные операторы. Повторяющиеся названия сохранены: исходные записи нельзя автоматически объединять.

Это справочный список, а не перечень подключённых банков. Активные записи и канонические ID вашего окружения возвращает API. Страны и типы в исходнике не подтверждены. Старые коды и внутренние сопоставления шлюзов здесь не публикуются.

Записей: 250

НазваниеНазвание в исходникеbank_id Absolut Pay
ГазпромбанкГазпромбанкПолучить через API
Банк СинараБанк СинараПолучить через API
Т-БанкТ-БанкПолучить через API
АК БАРС БАНКАК БАРС БАНКПолучить через API
РайффайзенбанкРайффайзен БанкПолучить через API
Альфа-БанкАльфа-БанкПолучить через API
Промсвязьбанк (PSB)Промсвязьбанк (PSB)Получить через API
РНКБ БанкРНКБ филиал ВТБПолучить через API
РОСБАНКРОСБАНКПолучить через API
СовкомбанкСовкомбанкПолучить через API
Банк Русский СтандартБанк Русский СтандартПолучить через API
Банк ОТКРЫТИЕБанк ОТКРЫТИЕПолучить через API
Почта БанкПочта БанкПолучить через API
МТС-БанкМТС-БанкПолучить через API
ОТП БанкОТП БанкПолучить через API
РоссельхозбанкРоссельхозбанкПолучить через API
НКО ЮМаниНКО ЮМаниПолучить через API
Хоум кредитХоум кредитПолучить через API
МОСКОВСКИЙ КРЕДИТНЫЙ БАНК (MKB)МОСКОВСКИЙ КРЕДИТНЫЙ БАНК (MKB)Получить через API
БАНК УРАЛСИББАНК УРАЛСИБПолучить через API
Кредит Европа Банк (Россия)Кредит Европа Банк (Россия)Получить через API
АКБ АВАНГАРДАКБ АВАНГАРДПолучить через API
Банк Санкт-ПетербургБанк Санкт-ПетербургПолучить через API
ЮниКредит БанкЮниКредит БанкПолучить через API
КБ УБРиРКБ УБРиРПолучить через API
КБ Ренессанс КредитКБ Ренессанс КредитПолучить через API
ТКБ БАНКТРАНСКАПИТАЛБАНК(ТКБ)Получить через API
ГЕНБАНКГЕНБАНКПолучить через API
Банк ФИНАМБанк ФИНАМПолучить через API
БКС БанкБКС БанкПолучить через API
КБ ЮНИСТРИМКБ ЮНИСТРИМПолучить через API
ГазэнергобанкГазэнергобанкПолучить через API
ЭкспобанкЭкспобанкПолучить через API
Банк ЗЕНИТБанк ЗЕНИТПолучить через API
МеталлинвестбанкМеталлинвестбанкПолучить через API
АКБ Абсолют БанкАКБ Абсолют БанкПолучить через API
Банк ВБРРБанк ВБРРПолучить через API
КБ Кубань КредитКБ Кубань КредитПолучить через API
Банк ЛевобережныйБанк ЛевобережныйПолучить через API
Бланк банкБланк банкПолучить через API
КБ ХлыновКБ ХлыновПолучить через API
ВЛАДБИЗНЕСБАНКВЛАДБИЗНЕСБАНКПолучить через API
КБ Центр-инвестКБ Центр-инвестПолучить через API
НОКССБАНКНОКССБАНКПолучить через API
Кредит Урал БанкКредит Урал БанкПолучить через API
Точка ФК ОткрытиеТочка ФК ОткрытиеПолучить через API
Земский банкЗемский банкПолучить через API
КБ Новый векКБ Новый векПолучить через API
СДМ-БанкСДМ-БанкПолучить через API
ДатабанкДатабанкПолучить через API
НС БанкНС БанкПолучить через API
Ингосстрах Банк (Инго)Ингосстрах Банк (Инго)Получить через API
АКБ АлмазэргиэнбанкАКБ АлмазэргиэнбанкПолучить через API
АКБ ФорштадтАКБ ФорштадтПолучить через API
Банк ДОМ.РФБанк ДОМ.РФПолучить через API
Дальневосточный банкДальневосточный банкПолучить через API
РосДорБанкРосДорБанкПолучить через API
ПНКО ЭЛПЛАТПНКО ЭЛПЛАТПолучить через API
Банк ПСКББанк ПСКБПолучить через API
СКБ Приморья ПримсоцбанкСКБ Приморья ПримсоцбанкПолучить через API
Банк ЕкатеринбургБанк ЕкатеринбургПолучить через API
БАНК СНГББАНК СНГБПолучить через API
БыстроБанкБыстроБанкПолучить через API
УглеметбанкУглеметбанкПолучить через API
ЧЕЛЯБИНВЕСТБАНКЧЕЛЯБИНВЕСТБАНКПолучить через API
АБ РОССИЯАБ РОССИЯПолучить через API
УралфинансУралфинансПолучить через API
КБ РостФинансКБ РостФинансПолучить через API
КБ МодульбанкКБ МодульбанкПолучить через API
Банк АгроросБанк АгроросПолучить через API
КБ Пойдём!КБ Пойдём!Получить через API
Эс-Би-Ай БанкЭс-Би-Ай БанкПолучить через API
ЧЕЛИНДБАНКЧЕЛИНДБАНКПолучить через API
АКИБАНКАКИБАНКПолучить через API
Азиатско-Тихоокеанский БанкАзиатско-Тихоокеанский БанкПолучить через API
КБ МоскоммерцбанкКБ МоскоммерцбанкПолучить через API
СбербанкСбербанкПолучить через API
КБ Гарант-ИнвестКБ Гарант-ИнвестПолучить через API
АКБ Алеф-БанкАКБ Алеф-БанкПолучить через API
НИКО-БАНКНИКО-БАНКПолучить через API
ПроБанкПроБанкПолучить через API
КБ АГРОПРОМКРЕДИТКБ АГРОПРОМКРЕДИТПолучить через API
КБ СолидарностьКБ СолидарностьПолучить через API
ИК БанкИК БанкПолучить через API
БАНК ОРЕНБУРГБАНК ОРЕНБУРГПолучить через API
ГОРБАНКГОРБАНКПолучить через API
Банк СаратовБанк СаратовПолучить через API
Хакасский муниципальный банкХакасский муниципальный банкПолучить через API
КБ СитибанкКБ СитибанкПолучить через API
КБ АРЕСБАНККБ АРЕСБАНКПолучить через API
АвтоградбанкАвтоградбанкПолучить через API
ББР БанкББР БанкПолучить через API
НБД-БанкНБД-БанкПолучить через API
Банк АкцептБанк АкцептПолучить через API
МЕТКОМБАНКМЕТКОМБАНКПолучить через API
Первый ДортрансбанкПервый ДортрансбанкПолучить через API
Тойота БанкТойота БанкПолучить через API
КБ ЭНЕРГОТРАНСБАНККБ ЭНЕРГОТРАНСБАНКПолучить через API
МБ БанкМБ БанкПолучить через API
УРАЛПРОМБАНКУРАЛПРОМБАНКПолучить через API
Тимер БанкТимер БанкПолучить через API
КОШЕЛЕВ-БАНККОШЕЛЕВ-БАНКПолучить через API
КБ СИНКО-БАНККБ СИНКО-БАНКПолучить через API
ГУТА-БАНКГУТА-БАНКПолучить через API
Яндекс БанкЯндексПолучить через API
КБ Урал ФДКБ Урал ФДПолучить через API
ТольяттихимбанкТольяттихимбанкПолучить через API
Банк ВенецБанк ВенецПолучить через API
Банк АверсБанк АверсПолучить через API
Банк ИТУРУПБанк ИТУРУППолучить через API
АКБ ЭнергобанкАКБ ЭнергобанкПолучить через API
ЮГ-ИнвестбанкЮГ-ИнвестбанкПолучить через API
КБ ЛОКО-БанкКБ ЛОКО-БанкПолучить через API
СнежинскийСнежинскийПолучить через API
РусьуниверсалбанкРусьуниверсалбанкПолучить через API
СИБСОЦБАНКСИБСОЦБАНКПолучить через API
АКБ ЕВРОФИНАНС МОСНАРБАНКАКБ ЕВРОФИНАНС МОСНАРБАНКПолучить через API
МП БанкМП БанкПолучить через API
Банк ИнтезаБанк ИнтезаПолучить через API
МОРСКОЙ БАНКМОРСКОЙ БАНКПолучить через API
Банк Развитие-СтолицаБанк Развитие-СтолицаПолучить через API
Таврический БанкТаврический БанкПолучить через API
Первый Инвестиционный БанкПервый Инвестиционный БанкПолучить через API
АКБ ТЕНДЕР-БАНКАКБ ТЕНДЕР-БАНКПолучить через API
МОСКОМБАНКМОСКОМБАНКПолучить через API
АКБ НОВИКОМБАНКАКБ НОВИКОМБАНКПолучить через API
КубаньторгбанкКубаньторгбанкПолучить через API
АвтоторгбанкАвтоторгбанкПолучить через API
Банк Объединенный капиталБанк Объединенный капиталПолучить через API
ГазтрансбанкГазтрансбанкПолучить через API
АКБ НРБанкАКБ НРБанкПолучить через API
НацинвестпромбанкНацинвестпромбанкПолучить через API
Банк РЕСО КредитБанк РЕСО КредитПолучить через API
ТАТСОЦБАНКТАТСОЦБАНКПолучить через API
КБЭР Банк КазаниКБЭР Банк КазаниПолучить через API
Банк МБА-МОСКВАБанк МБА-МОСКВАПолучить через API
КБ СТРОЙЛЕСБАНККБ СТРОЙЛЕСБАНКПолучить через API
КБ РУСНАРБАНККБ РУСНАРБАНКПолучить через API
КузнецкбизнесбанкКузнецкбизнесбанкПолучить через API
ИнбанкИнбанкПолучить через API
АКБ ТрансстройбанкАКБ ТрансстройбанкПолучить через API
ЭкономбанкЭкономбанкПолучить через API
ИШБАНКИШБАНКПолучить через API
АКБ СЛАВИЯАКБ СЛАВИЯПолучить через API
Банк КремлевскийБанк КремлевскийПолучить через API
Норвик БанкНорвик БанкПолучить через API
АКБ МЕЖДУНАРОДНЫЙ ФИНАНСОВЫЙ КЛУБАКБ МЕЖДУНАРОДНЫЙ ФИНАНСОВЫЙ КЛУБПолучить через API
Банк ЗаречьеБанк ЗаречьеПолучить через API
ТомскпромстройбанкТомскпромстройбанкПолучить через API
Северный Народный БанкСеверный Народный БанкПолучить через API
Банк АЛЕКСАНДРОВСКИЙБанк АЛЕКСАНДРОВСКИЙПолучить через API
КБ Крокус-БанкКБ Крокус-БанкПолучить через API
Джей энд Ти БанкДжей энд Ти БанкПолучить через API
ВУЗ-банкВУЗ-банкПолучить через API
Банк ФинсервисБанк ФинсервисПолучить через API
АКБ ФОРА-БАНКАКБ ФОРА-БАНКПолучить через API
СевергазбанкСевергазбанкПолучить через API
УКБ НовобанкУКБ НовобанкПолучить через API
СОЦИУМ-БАНКСОЦИУМ-БАНКПолучить через API
УКБ БелгородсоцбанкУКБ БелгородсоцбанкПолучить через API
АКБ ПриморьеАКБ ПриморьеПолучить через API
Банк БКФБанк БКФПолучить через API
Прио-ВнешторгбанкПрио-ВнешторгбанкПолучить через API
МС Банк РусМС Банк РусПолучить через API
АКБ СолидАКБ СолидПолучить через API
ЦентроКредитЦентроКредитПолучить через API
РЕАЛИСТ БАНКРЕАЛИСТ БАНКПолучить через API
НК БанкНК БанкПолучить через API
БАНК МОСКВА-СИТИБАНК МОСКВА-СИТИПолучить через API
АКБ ДержаваАКБ ДержаваПолучить через API
Банк ИПББанк ИПБПолучить через API
Банк Национальный стандартБанк Национальный стандартПолучить через API
АКБ Ланта-БанкАКБ Ланта-БанкПолучить через API
банк Раундбанк РаундПолучить через API
КБ ВНЕШФИНБАНККБ ВНЕШФИНБАНКПолучить через API
Драйв Клик БанкДрайв Клик БанкПолучить через API
Авто Финанс БанкАвто Финанс БанкПолучить через API
АИКБ Енисейский объединенный банкАИКБ Енисейский объединенный банкПолучить через API
Вайлдберриз БанкВайлдберриз БанкПолучить через API
Банк БЖФБанк БЖФПолучить через API
Цифра банкЦифра банкПолучить через API
банк Элитабанк ЭлитаПолучить через API
СтавропольпромстройбанкСтавропольпромстройбанкПолучить через API
КБ ДолинскКБ ДолинскПолучить через API
НКО Мобильная картаНКО Мобильная карта (ЦУПИС)Получить через API
Хайс БанкХайс БанкПолучить через API
Озон Банк (Ozon)Озон Банк (Ozon)Получить через API
ФИНСТАР БАНКФИНСТАР БАНКПолучить через API
ЦМРБанкЦМРБанкПолучить через API
Банк ТочкаБанк ТочкаПолучить через API
Банк ОранжевыйБанк ОранжевыйПолучить через API
РНКО Платежный конструкторРНКО Платежный конструкторПолучить через API
МТС Деньги (ЭКСИ Банк)МТС Деньги (ЭКСИ Банк)Получить через API
ИКБР ЯРИНТЕРБАНКИКБР ЯРИНТЕРБАНКПолучить через API
ЖИВАГО БАНКЖИВАГО БАНКПолучить через API
ПлайтПлайтПолучить через API
Цифра БанкЦифра БанкПолучить через API
Роял Кредит БанкРоял Кредит БанкПолучить через API
РОКЕТБАНКРОКЕТБАНКПолучить через API
Т-мобайлТ-мобайлПолучить через API
ДБО ФактураДБО ФактураПолучить через API
АвитоАвитоПолучить через API
БилайнБилайнПолучить через API
AkipayAlipayПолучить через API
Банк РоссияБанк РосияПолучить через API
QPlusQPlusПолучить через API
Амра-банкАмра-банкПолучить через API
Банк 131Банк 131Получить через API
Амра-банкАмра-банкПолучить через API
БалтИнвестБанкБалтИнвестБанкПолучить через API
БАНК ВОЛОГЖАНИНБАНК ВОЛОГЖАНИНПолучить через API
БМ-БанкБМ-БанкПолучить через API
РГС БанкРГС БанкПолучить через API
ЗапсибкомбанкЗапсибкомбанкПолучить через API
Еком БанкЕком БанкПолучить через API
ВестаВестаПолучить через API
НБСНБСПолучить через API
КузбассхимбанкКузбассхимбанкПолучить через API
КосмосКосмосПолучить через API
ВБРРВБРРПолучить через API
МотивМотивПолучить через API
YOTAYOTAПолучить через API
А-МобаилА-МобаилПолучить через API
Банк Йошкар-ОлаБанк Йошкар-ОлаПолучить через API
MultitransferMultitransferПолучить через API
СургунтнефтегазСургунтнефтегазПолучить через API
МегафонМегафонПолучить через API
T2 cardsT2 cardsПолучить через API
Банк ВТБВТБПолучить через API
Свой БанкСвой БанкПолучить через API
ПАО Банк "Кузнецкий"КузнецкийПолучить через API
Tele2Tele2Получить через API
МегафонМегафонПолучить через API
МТСМТС mobileПолучить через API
Alif BankAlif BankПолучить через API
Душанбе СитиДушанбе СитиПолучить через API
Telcell WalletTelcell WalletПолучить через API
Kapital BankKapital BankПолучить через API
IdbankIdbankПолучить через API
ОриёнбанкОриёнбанкПолучить через API
АрвандАрвандПолучить через API
BIDVBIDVПолучить через API
МатинМатинПолучить через API
Babilon-Mobile ТаджикистанBabilon-Mobile ТаджикистанПолучить через API
ACBОтображаемое название требует сверкиACBПолучить через API
BIDVОтображаемое название требует сверкиBIDVПолучить через API
MB BankОтображаемое название требует сверкиMB BankПолучить через API
TechcombankОтображаемое название требует сверкиTechcombankПолучить через API
VietcombankОтображаемое название требует сверкиVietcombankПолучить через API
VietinBankОтображаемое название требует сверкиVietinBankПолучить через API

Чтобы получить рабочий ID: найдите название в таблице, запросите GET /api/v2/banks?q=... с URL-кодированным названием и передайте items[].id выбранной записи в bank_id выплаты. При нескольких совпадениях уточните банк и страну; не выбирайте автоматически первую запись. Если список пуст, согласуйте подключение банка. Не создавайте ID самостоятельно.

GET /api/v2/banks

Запрос выполняется с вашего сервера и подписывается теми же четырьмя заголовками, что Payin API. Тело GET пустое. В подпись входит точный путь с закодированными параметрами:

text
GET /api/v2/banks?country_code=RU

Пример ответа HTTP 200:

json
{
  "items": [
    {
      "id": "bank_1",
      "original_name": "Т-Банк",
      "country_code": "RU",
      "kind": "bank"
    }
  ],
  "next_after": null
}

bank_1 — пример, а не гарантированный ID Т-Банка. Всегда используйте фактический items[].id своего окружения. Не переносите ID из чужого справочника или другой установки.

ПараметрПравило
country_codeНеобязательный двухбуквенный код страны в верхнем регистре, например RU
qНеобязательная подстрока original_name, до 200 UTF-8 байт; подпишите после URL-кодирования
afterЗначение next_after предыдущей страницы; остальные фильтры сохраняются

Не передавайте дополнительные или повторяющиеся параметры. Страница содержит не более 100 записей в порядке ID. При next_after: null чтение закончено. Пустой items — корректный результат: например, нет активных банков, соответствующих фильтру.

original_name — согласованное название, которое показывается плательщику. country_code может быть null, если страна не задана. kind различает банк (bank), кошелёк (wallet), мобильного оператора (mobile) и другой тип (other). Названия и технические коды отдельных шлюзов клиенту не передаются.

Наличие банка в справочнике не означает, что он настроен для каждого магазина, метода или терминала. Разрешённые сочетания подтверждаются для вашего подключения.

Банк при приёме платежа

В POST /api/v2/payins поле bank_id не передаётся. Банк определяется по фактически выданным шлюзом реквизитам и проверенному сопоставлению. Когда банк установлен, ответ содержит bank_id и bank_name для обоих режимов; в H2H эта пара также указана в requisite.

bank_name — сохранённый original_name, а не произвольное название провайдера. Hosted form показывает это же название. Переименование банка не переписывает уже выданную заявку. Неизвестное значение провайдера требует сверки: не считайте отсутствие реквизитов доказательством отсутствия операции у провайдера.

Банк при выплате

В POST /api/v2/payouts передайте bank_id из этого справочника. Полный запрос, правила методов, получатель, резервирование и повторы описаны в разделе Выплаты клиентам. ID обязателен для СБП; для карты и счёта требования исполнителя подтверждаются отдельно.

ID передаётся отдельно от карты или телефона получателя. Не передавайте вместо него название банка, код провайдера или числовую часть ID. original_name и внутренние сопоставления вы не задаёте. Сетл мерчанту и выплата его клиенту — разные операции.

sh
node examples/client.mjs banks
# Если next_after не null, подставьте возвращённое значение:
node examples/client.mjs banks --after bank_100

Публичный API origin подтверждён; доступность операций не подтверждена (проверенный GET /api/v2/banks возвращает 503). Локальные тесты приёма используют неплатёжный sandbox. Поддержка справочника не включает настоящий провайдерский адаптер или автоматическое исполнение выплат через реальный шлюз.

Создание и проверка платежа

Все три API-запроса подписываются по протоколу аутентификации. Магазин определяется ключами. Для нового запроса нужна новая подпись с новым nonce, даже если вы читаете тот же платёж.

Создание: POST /api/v2/payins

json
{
  "merchant_order_id": "shop-order-0001",
  "amount": "15000.00",
  "currency": "RUB",
  "method_id": 1,
  "mode": "h2h",
  "traffic_type": "primary",
  "payer": {
    "id": "customer-1001",
    "ip": "203.0.113.10"
  },
  "webhook_url": "https://merchant.example.invalid/webhooks/absolut-pay"
}

Это пример формата, не рабочий адрес webhook. IP в примере относится к плательщику и не заменяет IP вашего сервера в allowlist.

Обязательные поля

ПолеФормат и значение
merchant_order_idВаш уникальный номер попытки оплаты внутри магазина. До 128 UTF-8 байт; без пробелов по краям и управляющих символов
amountПоложительная десятичная строка, до 80 символов. Например "15000.00"; без экспоненты, знака, запятой и разделителей тысяч
currencyRUB, AZN, KZT, KGS, UZS; сочетание должно быть настроено для магазина
method_idЧисло: 1 СБП, 2 карта, 3 QR, 4 мобильный платёж
modeh2h или payment_form
traffic_typeprimary или secondary, согласно согласованной политике магазина
payer.idУстойчивый идентификатор плательщика в вашей системе, до 128 UTF-8 байт
payer.ipIPv4/IPv6 плательщика без zone identifier

Не меняйте payer.id при каждом платеже одного клиента: он участвует в проверках трафика и ограничений. Не передавайте произвольный тип трафика ради обхода настроек.

Необязательные поля

ПолеПравило
payer.emailВалидный email, до 254 UTF-8 байт
payer.phoneНепустая строка, до 32 UTF-8 байт
payer.user_agentДо 1024 UTF-8 байт
payer.fingerprintДо 256 UTF-8 байт
metadataJSON-объект до 16384 UTF-8 байт. Не передавайте секреты и лишние персональные данные
webhook_urlHTTPS URL до 2048 UTF-8 байт. Имеет приоритет над настройкой магазина
redirect_urls.success/failure/returnHTTPS URL до 2048 UTF-8 байт каждый. Поля сохраняются, но текущая hosted form не выполняет автоматические переходы по ним

При отсутствии необязательного значения опускайте поле. Не используйте null как универсальную замену. Неизвестные поля, повторяющиеся JSON-ключи и другие написания имён отклоняются. Не передавайте shop_id, is_test, live, status, курс или комиссию: клиент не задаёт эти значения.

Если нет webhook_url, используется адрес магазина, сохранённый при подготовке заявки. Если отсутствуют оба, запрос заканчивается 422 webhook_required, а merchant_order_id остаётся занятым. Добавление URL позже не перезапускает этот запрос.

Ответ создания

POST ожидает выдачи реквизитов и фиксации условий до 30 секунд. HTTP 201 возвращает объект платежа напрямую, без обёртки data:

json
{
  "id": "ord_123",
  "merchant_order_id": "shop-order-0001",
  "status_id": 1,
  "amount": "15000.00",
  "currency": "RUB",
  "method_id": 1,
  "mode": "h2h",
  "traffic_type": "primary",
  "rate": "100.00",
  "fee": "2.00",
  "created_at": "2026-10-08T12:00:00Z",
  "expires_at": "2026-10-08T12:10:00Z",
  "is_test": true,
  "requisite": {"type": "mock", "value": "TEST-ONLY-NOT-PAYABLE"}
}

Пример условный и неплатёжный. Не требуйте именно два знака после точки: сервер может вернуть другую точную десятичную запись. В режиме payment_form вместо requisite приходит payment_url. Эти поля взаимно исключаются.

Поле ответаЗначение
idВыданный платформой идентификатор платежа. Передавайте его без изменений
rateСохранённый курс: единицы исходной валюты за 1 USDT
feeСохранённая комиссия в процентах; "2" означает 2%, не 2 RUB/USDT
created_atВремя выдачи реквизитов, а не время получения исходного HTTP-запроса
expires_atФактический срок платежа; используйте это поле, не собственный таймер «10 минут»
is_testВ текущем контракте всегда true; реальный платёжный режим не активирован
success_paid, paid_atПоявляются только при status_id=2: подтверждённая сумма в исходной валюте и время оплаты
close_reasonПоявляется при status_id=4
bank_id, bank_nameПри проверенном сопоставлении ответа шлюза — ID банка и сохранённый original_name, в обоих режимах. В H2H эта пара также есть в requisite

Даты API — ISO 8601 UTC, возможны доли секунды. Для отображения преобразуйте их в выбранный часовой пояс; подпись и данные запроса не меняйте. Деньги и курс сохраняйте как строки/Decimal, без binary float.

Статусы платежа

status_idЗначениеДействие мерчанта
1Создан, реквизиты выданыОжидать подтверждения; создание не означает оплату
2ОплаченПроверить сумму и выполнить ваше бизнес-действие ровно один раз
3Срок истёкНе выдавать товар/услугу как оплаченную; сохранять возможность позднего подтверждения
4ОтменёнУчитывать причину; отмена не даёт права игнорировать последующее подтверждение уже состоявшейся оплаты

Подтверждённая поздняя оплата может перевести 3 в 2, а некоторые причины отмены — 4 в 2. Применяйте ответы POST/GET и webhook с проверкой актуального состояния вашего заказа. Уже сохранённый успех 2 нельзя перезаписать задержанным ответом со статусом 1, 3 или 4. Опрос API и webhook должны использовать общий ключ однократной выдачи товара или услуги.

Автоматический callback провайдера принимается только при численном равенстве подтверждённой и исходной суммы. Например, "15000" и "15000.00" равны; сравнивайте точные десятичные значения, а не текстовую запись или binary float. Другая сумма в callback требует сверки и сама по себе не переводит платёж в успех с зачислением.

Ручное подтверждение фактической суммы

Уполномоченный сотрудник может отдельно подтвердить фактически оплаченную сумму с доказательством и аудитом. Тогда status_id=2, исходный amount сохраняется, а success_paid может отличаться. Курс rate и ставка fee остаются сохранёнными условиями заявки; текущие настройки не применяются. Это отдельное подтверждение, а не автоматическое принятие произвольной частичной оплаты и не второе начисление.

Условный пример результата GET для той же тестовой заявки после ручного подтверждения 12000 RUB вместо исходных 15000 RUB; пример не свидетельствует о реальном платеже:

json
{
  "id": "ord_123",
  "merchant_order_id": "shop-order-0001",
  "status_id": 2,
  "amount": "15000.00",
  "success_paid": "12000.00",
  "currency": "RUB",
  "method_id": 1,
  "mode": "h2h",
  "traffic_type": "primary",
  "rate": "100.00",
  "fee": "2.00",
  "created_at": "2026-10-08T12:00:00Z",
  "expires_at": "2026-10-08T12:10:00Z",
  "paid_at": "2026-10-08T12:09:00Z",
  "is_test": true,
  "requisite": {"type": "mock", "value": "TEST-ONLY-NOT-PAYABLE"}
}

В уведомлении те же суммы называются amount и successPaid. Сохраните обе суммы; не подменяйте фактическую оплату исходной суммой заказа. При расхождении приостановите автоматическую выдачу полного товара или услуги и выполните сверку через API и поддержку по вашим правилам обработки недоплаты или переплаты. Само расхождение сумм не означает недействительную подпись или ошибочный статус. После сверки используйте общий для API и webhook ключ однократного бизнес-действия.

Потерянный ответ: GET /api/v2/payins/result

text
GET /api/v2/payins/result?merchant_order_id=shop-order-0001

При URL-кодировании номера подпишите тот же точный path/query, который отправляете. Ответ HTTP 200:

json
{"merchant_order_id":"shop-order-0001","state":"processing"}
stateСмысл
processingЗапрос принят и обрабатывается; продолжайте проверку результата
completedПлатёж сформирован; объект payin содержит его текущий status_id. Это не обязательно оплата
failedФормирование завершилось ошибкой, error_code содержит безопасную причину; объекта payin нет
review_requiredТребуется сверка неопределённого результата; не запускайте новую оплату того же поручения

После потери ответа, HTTP 409, 503 или 504 проверяйте этот маршрут. Новый nonce нужен на каждый GET. Интервал опроса согласуйте для выданного окружения; общего обещанного лимита или расписания здесь нет.

merchant_order_id уникален внутри магазина после принятия запроса, в том числе после терминальной ошибки. Повтор POST возвращает 409 duplicate_merchant_order_id, а не первоначальный успешный ответ. Нет заголовка Idempotency-Key в текущем контракте.

HTTP 404 при проверке означает отсутствие видимой записи в данный момент. Он не доказывает, что ранее отправленный POST не может ещё завершиться. Сначала завершите сверку; не создавайте другой номер для той же покупки с неопределённым исходом. Новая намеренная попытка после подтверждённого завершения предыдущей получает новый merchant_order_id и связь с покупкой в вашей системе.

Чтение сформированного платежа: GET /api/v2/payins/{id}

text
GET /api/v2/payins/ord_123

HTTP 200 возвращает тот же объект платежа. Пока он не сформирован, используйте поиск по merchant_order_id. Неизвестный или принадлежащий другому магазину id возвращает 404.

Платёжная форма

Открывайте только payment_url, полученный от сервера. Не собирайте его из id или домена админки: ссылка содержит секретный capability-токен. Не публикуйте её в общих логах/аналитике, не добавляйте посторонние query-параметры. При открытии новой вкладки используйте noopener и noreferrer.

Повторное открытие не создаёт новый платёж. Форма показывает текущее состояние, результат оплаты либо истечение срока. При наличии настроенных действий загрузка чека и «Я оплатил» передают информацию, не подтверждают оплату. Переход браузера, сообщение пользователя и скриншот не заменяют проверенный статус API/webhook.

Выплаты клиентам

Клиентская выплата — перевод получателю по поручению магазина. Это отдельная операция от входящего платежа и сетла мерчанту. Запрос создаёт сохранённую заявку и резервирует USDT; он не означает, что получатель уже получил деньги.

У магазина должны быть активированы отдельные условия выплат для метода и валюты: курс в единицах валюты за 1 USDT и комиссия магазина. Условия приёма платежей не подставляются автоматически. Перед первым запросом согласуйте доступные методы, валюты, банк и способ исполнения с Absolut Pay.

POST /api/v2/payouts

JSON-запрос подписывается четырьмя заголовками из раздела «Подпись запросов». Секрет хранится на сервере мерчанта. Подписывайте точные байты тела, не меняя их после вычисления подписи.

json
{
  "merchant_order_id": "PAYOUT-2026-000042",
  "amount": "15000.00",
  "currency": "RUB",
  "method": "sbp",
  "bank_id": "bank_1",
  "recipient": {
    "phone_number": "+79991112233",
    "customer_name": "Тестовый получатель"
  },
  "webhook_url": "https://merchant.example/payouts-hook",
  "metadata": {
    "customer_ref": "customer-42"
  }
}

bank_1 — пример. Получите фактический ID из GET /api/v2/banks. Не отправляйте наш ID как код НСПК напрямую в API другого провайдера.

ПолеОбязательность и проверка
merchant_order_idОбязательно: уникальный номер поручения в пределах магазина, 1–128 UTF-8 байт, без управляющих символов и пробелов по краям
amountОбязательно: положительная десятичная строка, не более двух знаков после точки; сумма в валюте получателя
currencyОбязательно: RUB, AZN, KZT, KGS или UZS; наличие в перечне не заменяет активные условия магазина
methodОбязательно: sbp, card или account; банк и исполнитель должны поддерживать этот метод
bank_idОбязательно для sbp, необязательно для других методов; строковый ID активной записи справочника
recipientОбязательно: объект получателя по правилам ниже
webhook_urlНеобязательно: HTTPS URL без userinfo и fragment, порт 443 или без явного порта, до 2000 байт. Если отсутствует, используется URL магазина; если оба отсутствуют, уведомления не создаются
metadataНеобязательно: ваш JSON-объект, до 16 KiB, без секретов. Возвращается при чтении поручения; в уведомление не включается

Для всех методов обязательно recipient.customer_name: имя получателя, 1–200 UTF-8 байт. Остальные поля зависят от метода:

МетодРеквизиты
sbpТолько phone_number: международный формат + и 8–15 цифр, первая цифра не 0. Например, +79991112233. Обязателен bank_id
cardТолько card_number: строка из 12–19 цифр. Не передавайте CVV, срок действия, PIN или данные входа в банк
accountТолько account_number: 8–34 символа, заглавные латинские буквы и цифры

Нельзя передавать карту одновременно с телефоном или счётом. Неизвестные поля, дубли ключей, null, повторные параметры URL и числовое JSON-значение вместо суммы отклоняются. Метод account в этой версии доступен для ручного исполнения; автоматический адаптер для него не подключён.

Магазин определяется подписанным API-токеном. Не передавайте shop_id, курс, комиссию или желаемый финансовый статус в публичном запросе.

Расчёт и резерв

При создании система сохраняет сумму, валюту, банк и его название, реквизиты получателя, курс, комиссию и версию условий. Изменение условий магазина или названия банка не переписывает созданную выплату.

text
principal_usdt = round(amount / rate_fiat_per_usdt, 8)
fee_usdt       = round(principal_usdt × fee_percent / 100, 8)
total_usdt     = principal_usdt + fee_usdt

Округление выполняется до ближайшего значения с восемью знаками; точная половина округляется вверх. Для положительной суммы, которая округляется в нулевую основную сумму USDT, создание отклоняется. Сумма и курс рассчитываются точной десятичной арифметикой.

Резерв равен total_usdt и уменьшает доступный баланс. При подтверждённом успехе списываются основная сумма и комиссия; при подтверждённом отказе или отмене до отправки резерв освобождается. Неопределённый результат сохраняет резерв до сверки. Для разделённой выплаты каждая часть имеет собственный резерв; отмена оставшейся части не возвращает уже исполненную сумму.

HTTP 201 означает, что поручение и резерв сохранены:

json
{
  "id": "b7a09187-553b-4d11-bc28-a725192653fc",
  "merchant_order_id": "PAYOUT-2026-000042",
  "amount": "15000.00",
  "currency": "RUB",
  "method": "sbp",
  "bank_id": "bank_1",
  "bank_name": "Тестовый банк",
  "recipient": {
    "phone_number": "+79991112233",
    "customer_name": "Тестовый получатель"
  },
  "status": "queued",
  "principal_usdt": "150.00000000",
  "fee_usdt": "3.00000000",
  "total_usdt": "153.00000000",
  "rate_fiat_per_usdt": "100.00000000",
  "fee_percent": "2.00000000",
  "paid_amount": "0.00",
  "remainder": "15000.00",
  "revision": 1,
  "created_at": "2026-10-08T18:00:00Z",
  "updated_at": "2026-10-08T18:00:00Z",
  "metadata": {
    "customer_ref": "customer-42"
  }
}

amount остаётся исходной суммой. paid_amount — сумма подтверждённых успешных частей, remainder — исходная сумма минус оплаченные части. Остаток в этом поле не означает, что сумма доступна для повторной отправки: неотправленные, отказанные и неизвестные части различаются внутренними состояниями. Для следующего действия используйте актуальный статус и сверку с поддержкой.

Статусы и чтение

statusЗначение
queuedСоздана, средства зарезервированы, ещё не отправлена
processingЕсть отправленная часть, ожидается подтверждение
unknownЕсть неопределённый результат; резерв удерживается, выполняется сверка
successВсе части исполнены; исходная сумма полностью оплачена
partialВсе части завершены; часть суммы оплачена, остальные части завершились отказом или отменой
failedВсе неуспешные части завершены отказом; успешных частей нет
cancelledВыплата отменена без исполнения; успешных частей нет

processing может содержать уже исполненные части: их сумма указана в paid_amount. partial — завершение со смешанным результатом. Перед отдельным поручением на неполученную сумму сверяйте результат; не создавайте его автоматически по одному уведомлению. Внутренние исполнители, терминалы, ключи и доказательства сотрудника не возвращаются публичным API.

text
GET /api/v2/payouts/b7a09187-553b-4d11-bc28-a725192653fc
GET /api/v2/payouts/result?merchant_order_id=PAYOUT-2026-000042

Оба запроса подписанные, тело пустое. Первый возвращает объект выплаты; второй возвращает результат регистрации номера:

json
{
  "status": "created",
  "payout": {
    "id": "b7a09187-553b-4d11-bc28-a725192653fc",
    "merchant_order_id": "PAYOUT-2026-000042",
    "amount": "15000.00",
    "currency": "RUB",
    "method": "sbp",
    "bank_id": "bank_1",
    "bank_name": "Тестовый банк",
    "recipient": {
      "phone_number": "+79991112233",
      "customer_name": "Тестовый получатель"
    },
    "status": "queued",
    "principal_usdt": "150.00000000",
    "fee_usdt": "3.00000000",
    "total_usdt": "153.00000000",
    "rate_fiat_per_usdt": "100.00000000",
    "fee_percent": "2.00000000",
    "paid_amount": "0.00",
    "remainder": "15000.00",
    "revision": 1,
    "created_at": "2026-10-08T18:00:00Z",
    "updated_at": "2026-10-08T18:00:00Z",
    "metadata": {}
  }
}

При creating поле payout равно null: номер зарегистрирован, окончательный результат требует проверки. При rejected поле payout равно null, дополнительно возвращается error_code; этот номер также нельзя использовать заново. HTTP 404 означает, что запись не найдена в вашем магазине; при потерянном ответе на создание сначала повторите чтение с новой подписью и свяжитесь с поддержкой, не создавая другое поручение автоматически.

Повторы и изменение поручения

merchant_order_id резервируется постоянно после принятия корректного запроса. Параллельные и повторные POST с тем же номером не создают новый резерв; ответ — HTTP 409 duplicate_merchant_order_id. Другой магазин имеет отдельное пространство номеров.

После тайм-аута, обрыва ответа или 5xx обращайтесь к /payouts/result с тем же номером и новыми X-Timestamp/X-Nonce/X-Signature. Если запись остаётся creating, требуется разбор, а не новая отправка. При подтверждённом rejected исправленное поручение требует нового согласованного номера.

Публичных методов изменения суммы, дробления или отмены нет. Сотрудник может разделить только полностью неотправленную выплату; после отправки изменение реквизитов, курса и повторное создание частей запрещены. Ручное исполнение требует отдельного права, суммы и валюты результата, доказательства и уникального номера фактического перевода. Это подтверждение уполномоченного сотрудника, не автоматическая проверка банка или блокчейна.

GET /api/v2/balance

Подписанный запрос без тела и параметров возвращает USDT-баланс магазина, которому принадлежит API-токен:

json
{
  "currency": "USDT",
  "available": "847.00000000",
  "reserved": "153.00000000",
  "total": "1000.00000000"
}

Предварительное чтение баланса не резервирует деньги и не гарантирует следующую выплату. Проверка средств и резервирование выполняются атомарно при создании. total включает зарезервированные средства.

Уведомления о выплатах

Payout webhook — отдельный JSON-формат с типом payout.updated. Подпись: HMAC-SHA512 от X-Webhook-Timestamp + "." + сырые байты тела, тот же алгоритм проверки, что у уведомлений приёма. Не используйте SHA512 от склеенных сортированных полей.

json
{
  "event_id": "836b36d7-bcec-4652-b512-6b53bf9c5721",
  "event_type": "payout.updated",
  "revision": 2,
  "payout_id": "b7a09187-553b-4d11-bc28-a725192653fc",
  "merchant_order_id": "PAYOUT-2026-000042",
  "status": "processing",
  "amount": "15000.00",
  "currency": "RUB",
  "paid_amount": "0.00",
  "remainder": "15000.00",
  "principal_usdt": "150.00000000",
  "fee_usdt": "3.00000000",
  "updated_at": "2026-10-08T18:01:00Z"
}
  1. Проверьте подпись по сырым байтам и свежесть заголовка, затем форму сообщения.
  2. В одной транзакции сохраните event_id, примените только новую revision для этого поручения и зафиксируйте свой эффект. Повторный event_id не исполняйте второй раз.
  3. Ответьте HTTP 200 после надёжного сохранения. Тело 1 не требуется. Ответы 201/202/204 не подтверждают доставку.

Доставка выполняется как минимум один раз. При потере HTTP-ответа платформа может повторить уже принятую вами запись. Повтор сохраняет event_id и байты тела; подпись и timestamp обновляются для текущей попытки. Не используйте updated_at как ключ дедупликации.

Всего до восьми попыток. Интервалы после неуспеха: 5 секунд, 30 секунд, 2 минуты, 10 минут, 30 минут, 2 часа, 12 часов. После исчерпания попыток сверяйте статус через GET. Работает отдельный worker с MERCHANT_WEBHOOKS_ENABLED=true; наличие URL без запущенного worker не обеспечивает доставку. Ошибка уведомления не меняет выплату и не освобождает резерв.

Дата API — ISO 8601 с часовым поясом; сравнивайте абсолютные моменты. Представление времени в кабинете — московское, независимо от часового пояса браузера.

Доступность первой версии

Рабочий серверный цикл включает создание, резервирование, чтение, разделение до отправки и ручное подтверждение/отказ с аудитом. Исполняемые автоматические подключения пока только тестовые: внесение реального токена в терминал само по себе не подключает шлюз. Перед запуском согласуйте фактический исполнительный адаптер, проверки его результата и доступность worker; тестовое подключение не перечисляет деньги.

sh
node examples/client.mjs balance
node examples/client.mjs payout-create --body examples/create-payout.json
node examples/client.mjs payout-result 'PAYOUT-2026-000042'
node examples/client.mjs payout-get 'b7a09187-553b-4d11-bc28-a725192653fc'

Уведомления о платежах

Absolut Pay отправляет webhook на ваш сервер после изменения статуса сформированного платежа. Уведомление позволяет надёжно обновить заказ в вашей системе; переход браузера и нажатие плательщиком «Я оплатил» не заменяют подтверждённый результат.

Payin API v2 и Merchant Webhook v1 — два разных формата. Запросы API используют merchant_order_id, method_id и даты ISO 8601. Уведомления сохраняют clientOrderID, methodID и Unix-время. Версия webhook не означает наличие платёжных маршрутов /api/v1.

В текущем локальном sandbox тестовые платежи помечены is_test: true: их события сохраняются без внешней доставки и без финансовых зачислений. Отсутствие webhook в таком тесте ожидаемо. Доставку на ваш адрес и рабочее подключение нужно подтвердить отдельно; этот документ описывает реализованный протокол уведомлений, а не активацию production.

Адрес доставки

Укажите HTTPS-адрес в webhook_url при создании платежа или настройте адрес по умолчанию для магазина. Адрес из запроса имеет приоритет. Система сохраняет выбранный адрес для заявки; последующее изменение настроек магазина не меняет адрес старой заявки.

Если адрес отсутствует в обоих местах, создание завершается ошибкой webhook_required до формирования платежа. Его merchant_order_id остаётся зарезервированным. У такого отказа нет сформированного платежа и webhook о его отмене.

Приёмник должен быть доступен по публичному HTTPS-адресу с действительным TLS-сертификатом. Перенаправления не выполняются. Private, loopback, link-local и служебные сетевые адреса для доставки запрещены. Адрес для webhook — серверный обработчик, а не страница возврата браузера.

Заголовки и подпись

http
POST /payments/absolut-pay/webhook HTTP/1.1
Content-Type: application/json
X-Webhook-Timestamp: UNIX_SECONDS
X-Webhook-Signature: LOWERCASE_HMAC_SHA512_HEX

Значения UNIX_SECONDS и LOWERCASE_HMAC_SHA512_HEX — обозначения для примера. Подпись проверяется по формуле:

text
canonical_bytes = timestamp + "." + raw_body
expected_signature = lowercase_hex(HMAC_SHA512(api_secret, canonical_bytes))

Используется буквальная строка секрета API соответствующего магазина; декодировать её из Base64/Base64URL не нужно. Отдельный webhook-secret не выдаётся. В подпись не входят метод, путь, nonce или токен API. Заголовки X-API-Token и X-Nonce в уведомлении не передаются.

Проверьте подпись до доверенного использования JSON. Получите точные исходные байты тела, добавьте к ним строку timestamp и точку, вычислите HMAC и сравните подписи способом с постоянным временем. Повторная JSON-сериализация после разбора тела не подходит: пробелы и порядок полей могут измениться.

Для вашего приёмника рекомендуем проверку свежести X-Webhook-Timestamp в пределах ±300 секунд и синхронизацию времени. Это рекомендуемая защита на вашей стороне: Absolut Pay не настраивает допустимое окно в вашем приложении. Timestamp относится к конкретной попытке доставки. Старый платёж может иметь старые createdAt и paidAt, но свежее время и подпись уведомления.

Свежесть и подпись не заменяют защиту от дублирования: корректно подписанное уведомление может прийти повторно.

Поля уведомления

ПолеТипЗначение
idstringID платежа Absolut Pay, например ord_10428
orderTypestringpayin
status_idinteger2 — успех, 3 — срок истёк, 4 — отмена
amountdecimal stringИсходная сумма платежа в currency
currencystringКод валюты, например RUB
methodIDinteger1 — СБП, 2 — карта, 3 — QR, 4 — мобильный платёж
ratedecimal stringСохранённый курс: единиц валюты платежа за 1 USDT
feedecimal stringСохранённая ставка комиссии в процентах; это не денежная сумма
clientOrderIDstringВаш merchant_order_id
createdAtintegerUnix-секунды выдачи платёжных инструкций
expiredAtintegerUnix-секунды окончания срока оплаты
successPaiddecimal stringПодтверждённая сумма в currency; присутствует только при успехе
paidAtintegerUnix-секунды подтверждённого времени оплаты; только при успехе
closeReasonstringПричина отмены; присутствует только при status_id: 4

Текущий протокол отправляет события успеха, истечения срока и отмены. Webhook о создании со статусом 1 не отправляется. Публичного event_id в теле нет. Не ожидайте реквизитов, данных плательщика или текущего баланса в этом сообщении.

Суммы, курс и комиссия передаются строками, чтобы сохранить точность. Не преобразуйте их через обычные дробные числа с плавающей точкой. Unix-время означает момент независимо от часового пояса; переводите его для отображения, сохраняя исходное значение.

amount — исходная сумма заказа, а successPaid — фактически подтверждённая оплаченная сумма. Автоматический callback провайдера требует численного совпадения с исходной суммой: "15000" и "15000.00" означают одну сумму; расхождение само по себе не создаёт успешного зачисления. Уполномоченный сотрудник может отдельно подтвердить другую фактическую сумму с доказательством и аудитом. Тогда amount сохраняется, а successPaid может отличаться; начисление рассчитывается по замороженным курсу и комиссии этой заявки, без применения текущих настроек и без повторного зачисления. Это не автоматическая поддержка произвольной частичной оплаты. При расхождении сохраните обе суммы и проверьте результат через API и поддержку; не подменяйте successPaid исходным amount.

Подтверждённая оплата

Пример структуры уведомления, не свидетельство реального платежа:

json
{
  "id": "ord_10428",
  "orderType": "payin",
  "status_id": 2,
  "amount": "15000.00",
  "successPaid": "15000.00",
  "currency": "RUB",
  "methodID": 1,
  "rate": "100.00",
  "fee": "2.00",
  "clientOrderID": "invoice-10428",
  "createdAt": 1791460800,
  "expiredAt": 1791461400,
  "paidAt": 1791460920
}

Для status_id: 3 поля successPaid и paidAt отсутствуют. Для status_id: 4 они также отсутствуют, но добавляется closeReason, например merchant_cancelled. Не ожидайте нулевых значений вместо отсутствующих полей.

Надёжный приём

Обрабатывайте уведомление в таком порядке:

  1. Выберите секрет магазина по серверной конфигурации своего обработчика. Не определяйте секрет по непроверенным данным платежа.
  2. Проверьте заголовки, допустимое время и подпись исходных байтов.
  3. Разберите JSON и сверьте id, clientOrderID, валюту и сумму с сохранённым заказом.
  4. В транзакции запишите событие с уникальным ключом (id, status_id). При общем обработчике нескольких магазинов добавьте идентификатор магазина в локальный ключ и проверьте область доступа.
  5. Обновите состояние заказа и сохраните необходимую внутреннюю задачу на выдачу товара или услуги.
  6. Зафиксируйте транзакцию и верните HTTP 200.

Корректный повтор уже сохранённого события также подтверждайте HTTP 200, не выполняя выдачу повторно. Не используйте timestamp или подпись как ключ события: на повторной попытке они изменяются.

Если выдача услуги требует обращения к другой системе, сохраните внутреннюю задачу вместе с событием и выполняйте её отдельно с защитой от повторов. HTTP 200 нужно отправлять после надёжного сохранения, а не после долгой внешней операции. Если запись не завершена или данные противоречат заказу, не подтверждайте успешный приём; сохраните безопасную диагностическую информацию для сверки.

Поздняя оплата и порядок событий

Срок мог истечь, а провайдер позже подтвердил оплату: тогда после 3 может прийти 2. Подтверждённая оплата также может следовать после отмены с причиной client_declined, merchant_cancelled или admin_cancelled. Отмена уже выданной заявки не доказывает, что деньги не поступят позже.

Обрабатывайте первую подтверждённую оплату один раз. Принятый успех 2 нельзя понизить до 3 или 4 из-за запоздавшего сообщения. Уведомление об истечении срока и уведомление об успехе — отдельные события одной заявки; поэтому дедупликация только по id без статуса ошибочно потеряет поздний успех.

Система упорядочивает отправки одной заявки, однако задержки сети и обработки на вашей стороне всё равно требуют защиты от устаревшего состояния. Если пришли противоречащие сохранённым данным события, проверьте текущий результат подписанным API-запросом.

Подтверждение и повторные попытки

Доставка считается успешной только при HTTP 200. Тело ответа не учитывается. 201, 202, 204, редирект и любой другой код не подтверждают доставку. На один запрос отведено 2 секунды.

ПопыткаВремя, начиная с создания события
10 секунд
210 секунд
330 секунд
460 секунд
5120 секунд

Это сроки доступности попыток, а не гарантия точного времени получения. Предыдущее событие, остановка обработчика доставки или сетевые задержки могут сдвинуть фактическую отправку позже. Отсчёт не начинается заново после каждого неуспешного ответа.

Во всех попытках тело и адрес остаются прежними; timestamp и подпись формируются заново. Доставка допускает повторы: даже после сохранённого вами события ответ HTTP 200 может потеряться. После пяти неудачных попыток автоматическая доставка считается исчерпанной. Ошибка уведомления не отменяет оплату и не откатывает финансовый результат.

Используйте подписанное чтение платежа для сверки при недоступности webhook. Возвращение браузера, отсутствие сообщения или исчерпание доставки сами по себе не определяют финансовый статус.

Ротация секрета

Каждая новая попытка подписывается текущим секретом магазина. Уже начатая отправка может завершиться с предыдущим секретом. Согласуйте ротацию: обновите подпись своих API-запросов новой парой ключей и предусмотрите на приёмнике короткий ограниченный переходный период для старого секрета, если это требуется вашей схемой развёртывания.

Продолжительность переходного периода и удаление старого секрета определяются вашей политикой и проверкой уже начатых отправок; сервер не передаёт в webhook идентификатор ключа и не обещает фиксированное окно совместимости. Проверки свежести, принадлежности заказа и защиты от повторов действуют для обеих допустимых подписей. Старый секрет нельзя сохранять как бессрочный обход ротации.

Отключение приёма новых платежей магазина не отменяет обязанность доставить уведомления по существующим платежам.

Ошибки: что произошло и что делать

Ошибка HTTP возвращается в общей оболочке:

json
{"error":{"code":"authentication_failed","message":"Authentication failed."},"request_id":"example-request-id"}

Обрабатывайте error.code, а не английский message. request_id нужен поддержке; это не номер платежа и не ключ идемпотентности. Все повторы подписывайте заново: новые timestamp, nonce и signature, те же согласованные байты бизнес-запроса.

Справочник errors.json содержит те же объяснения. HTTP 200 в таблице означает вложенный error_code результата lookup, а не ошибку HTTP. У приёма он появляется при state: failed, у выплаты — при status: rejected. processing/creating и review_required требуют дальнейшего чтения или сверки; HTTP 200 сам по себе не означает оплату.

Доступ и формат запроса

Код / HTTPГде возникаетЧто означаетЧто делатьПовтор
authentication_failed / 401Все подписанные операцииСервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.
ip_not_allowed / 403Все подписанные операцииИсходящий IP вашего backend не входит в разрешённый список магазина.Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.После исправления allowlist; для неизвестного POST сначала lookup.
shop_disabled / 403, 422, 200Создание приёма/выплаты; lookup приёмаМагазин или его группа недоступны для создания. HTTP 403 — отказ доступа; HTTP 422 — приём отклонён после резервирования номера; HTTP 200 — причина failed в lookup.Уточнить активацию у менеджера. Сохранённые операции читать действующим ключом. При 422 номер уже занят.Не автоматически. После активации проверить прежний результат; занятый номер не переиспользовать.
invalid_request / 400Приём и банкиНеверный JSON, поля, query или идентификатор: неизвестные/повторные параметры, недопустимый IP/URL либо параметры справочника.Сравнить запрос со схемой конкретной операции. У GET по ID и POST приёма query не допускается; lookup требует один merchant_order_id.После исправления запроса. Если ранее был неизвестный результат POST — сначала lookup.
invalid_amount / 400Создание приёмаСумма не соответствует разрешённому формату или границам.Передать положительную точную decimal-строку согласно схеме; не JSON-число. Не округлять деньги через float.После исправления; неизвестный предыдущий POST сначала проверить lookup.
invalid_payout_request / 400Выплаты и балансНекорректные поля, сумма, реквизиты, bank_id, UUID или query. При выплате bank_id должен указывать на активный канонический банк.Проверить схему выплаты и тип реквизитов, получить bank_id из справочника. У баланса query запрещён. Ошибка может возникнуть и после занятия номера выплаты.После исправления сначала lookup по прежнему номеру; если номер занят, не переиспользовать.
method_not_allowed / 405Маршрутизация /api/v2HTTP-метод не поддерживается по указанному пути.Использовать метод и путь из OpenAPI; заново подписать исправленный запрос.После исправления. Если это повтор после timeout, сначала выяснить исход первой операции.

Поиск и конфликт номера

Код / HTTPГде возникаетЧто означаетЧто делатьПовтор
payin_not_found / 404Получение/lookup приёма; неизвестный маршрут /api/v2Приём или запрос с таким номером не найден в данном магазине; этот код также используется для неизвестного пути v2.Проверить путь, магазин и идентификатор. Пока Payin не сформирован, искать по merchant_order_id. После потерянного ответа повторить чтение и при неопределённости обратиться в поддержку.Повтор чтения допустим с новой подписью. Один 404 не разрешает повтор POST или создание нового номера.
payout_not_found / 404Получение/lookup выплаты; чтение балансаОбъект не найден или не принадлежит магазину ключа. При балансе не найдена строка учёта магазина; это не нулевой баланс.Проверить магазин, UUID/merchant_order_id; при балансе обратиться в поддержку. После потерянного POST выяснить исход по прежнему номеру.Повтор GET допустим. Один 404 после timeout не доказывает, что можно создать перевод снова.
duplicate_merchant_order_id / 409Создание приёма и выплатыНомер бизнес-запроса уже занят в данном магазине, в том числе после отказа. Повтор POST не возвращает старый успешный ответ.Читать /payins/result или /payouts/result по исходному номеру. Не заменять номер автоматически.Повторять lookup, не POST. Новый перевод — только отдельное бизнес-решение после выяснения исхода.
payout_action_unavailable / 409Создание выплаты: конфликт согласованностиПри записи обнаружен конфликт идентичности/состояния выплаты. Общая классификация ошибок БД может вернуть этот код публичному созданию.Проверить lookup по исходному номеру и передать request_id в поддержку. Это не маршрут изменения выплаты мерчантом.Не повторять POST автоматически; сначала сверка.

Условия и отказ формирования

Код / HTTPГде возникаетЧто означаетЧто делатьПовтор
payout_configuration_disabled / 403, 200Создание и lookup выплатыНет активных отдельных условий выплаты для метода/валюты либо магазин/группа отключены. В lookup HTTP 200 это причина rejected.Согласовать и активировать условия выплат. Условия приёма не заменяют условия выплат. Номер может быть уже занят.После исправления проверить lookup; отвергнутый занятый номер остаётся занят.
insufficient_funds / 422, 200Создание и lookup выплатыДоступного USDT недостаточно для резерва основной стоимости и комиссии. В lookup HTTP 200 это причина rejected.Прочитать баланс, учесть reserved и полную стоимость по условиям выплат. Согласовать пополнение доступного остатка; прежний номер уже занят.Не повторять прежний POST после пополнения. Новый запрос только как новое подтверждённое бизнес-действие.
webhook_required / 422, 200Создание и lookup приёмаДо формирования не найден webhook_url ни в запросе, ни в настройках магазина. Номер уже зарезервирован.Настроить публичный HTTPS endpoint для будущих запросов. Проверить failed через lookup. Добавление URL не возобновит этот запрос.Этот POST не повторять; новый номер только для осознанно нового запроса после проверки исхода.
traffic_type_not_allowed / 422, 200Создание и lookup приёмаТип трафика не разрешён условиями магазина. Номер сохранён.Согласовать тип трафика и настройки; не подменять классификацию ради обхода ограничения.Не повторять сохранённый запрос. После согласования возможен отдельный новый бизнес-запрос.
payer_restricted / 422, 200Создание и lookup приёмаДля плательщика действует ограничение. Номер запроса сохранён.Уточнить причину и порядок разблокировки у менеджера; не менять ID плательщика для обхода.Автоматический повтор запрещён; сначала решение по ограничению и сверка результата.
unsupported_payment_configuration / 422, 200Создание и lookup приёмаСочетание метода, валюты и режима не поддерживается действующими условиями магазина.Сверить матрицу и согласованные настройки; наличие значения в enum не гарантирует доступность.После согласования — отдельный новый запрос; прежний зарезервированный номер не повторять.
no_requisite / 422, 200Создание и lookup приёмаКаскад не выдал реквизиты. Сохранённый запрос не стал сформированным Payin и не означает оплату.Проверить failed через lookup, показать пользователю отсутствие реквизитов и возможность обратиться в поддержку.Не повторять POST автоматически; новая попытка только как отдельное согласованное бизнес-действие.
payment_processing_failed / 422, 200Создание и lookup приёмаФормирование не завершилось; внутренняя причина скрыта за общим публичным кодом.Получить lookup по исходному номеру, сохранить request_id и обратиться в поддержку. Не угадывать причину по тексту message.Повтор чтения допустим; POST с занятым номером не повторять.

Неизвестный результат и восстановление

Код / HTTPГде возникаетЧто означаетЧто делатьПовтор
payment_requires_reconciliation / 503Создание приёмаИсход формирования требует сверки; отсутствие Payin в ответе не является доказательством отсутствия операции.Прочитать lookup; при review_required дождаться сверки поддержки по исходному номеру.Не повторять POST и не создавать автоматически новый номер; разрешён polling.
payment_processing_timeout / 504Создание приёмаИстекло ожидание HTTP-ответа; долговечная обработка может продолжаться.Выполнить lookup по исходному merchant_order_id с новой подписью. Для processing продолжать чтение с задержкой.Только чтение до известного результата; автоматический retry POST выключить.
internal_error / 503Все подписанные операцииРезультат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.
creation_requires_review / 200Только error_code в lookup выплатыСоздание выплаты завершилось отклонённым запросом, требующим проверки причины; публичный обработчик не раскрывает внутреннюю ошибку.Проверить status и payout в lookup, передать номер и request_id поддержке. Номер зарезервирован, даже когда payout равен null.Повторять только чтение. Не превращать rejected в автоматическое повторное создание.

Потерян ответ POST: безопасный порядок

  1. Сохраните исходный merchant_order_id, направление, магазин и время попытки.
  2. Выполните соответствующий подписанный GET /api/v2/payins/result?merchant_order_id=... или /api/v2/payouts/result?merchant_order_id=....
  3. Если запрос ещё обрабатывается, продолжайте чтение с задержкой. При требовании сверки обратитесь в поддержку. Один 404 не исключает гонку с незавершённым первым запросом.
  4. Если заявка найдена, обрабатывайте её состояние однократно вместе с webhook. Создание заявки не означает оплату.
  5. При окончательном отказе номер остаётся занят. Новый номер означает новое бизнес-действие, а не транспортный retry: его нельзя назначать автоматически, пока исход прежнего запроса не установлен.

Отключите автоматический retry POST в HTTP-клиенте и балансировщике. Здесь нет обещания универсального интервала polling или Retry-After: используйте согласованные ограничения окружения. Если ответ не соответствует JSON-оболочке (например, ответ внешнего proxy), сохраните безопасные HTTP-метаданные и действуйте как при неизвестном исходе; не придумывайте error.code.

Если подпись не проходит

Используйте secret как выданную строку без декодирования. Подписывайте METHOD в верхнем регистре, path/query без hostname, с неизменными порядком query и percent-encoding. JSON должен уйти ровно теми байтами, которые подписаны, включая пробелы и конечный LF. Для GET тело пустое, последний разделитель LF сохраняется. Проверьте часы и UUID v4; принятый nonce нельзя использовать повторно. Внутренние причины вроде invalid_nonce не публикуются как отдельные коды: снаружи это authentication_failed.

Если webhook не приходит

Неоплаченная заявка и отсутствие уведомления — разные факты. В текущем тестовом приёме доставка merchant webhook не выполняется. В окружении с доставкой проверьте публичный HTTPS URL, сертификат/DNS, отсутствие редиректа и ответ именно HTTP 200 за отведённое время. Не размещайте получатель на localhost или частном IP. После исчерпания попыток сверяйте результат через API.

Подпись входящего webhook отличается от подписи запроса API. Проверяйте исходное тело безопасным сравнением и объединяйте webhook с polling через однократное бизнес-действие.

Данные для поддержки

Передавайте request_id, направление, безопасное имя маршрута, HTTP/code, время и ваш номер заказа. Не отправляйте secret, заголовки авторизации, полные тела с персональными данными или capability-ссылки в общие логи.

Каталог ограничен Merchant API. Коды административных операций с исполнителями, ручной проверкой доказательств, ревизиями и экспортом сюда не перенесены. Внутренние причины live_adapter_required и provider_reconciliation_required скрыты lookup приёма: клиент получает state: review_required без этих кодов.

Публичная платёжная форма и H2H

Режим h2h возвращает реквизиты в requisite, режим payment_form — полный payment_url. Выбор режима не подтверждает деньги и не создаёт второй платёж. Текущий контур приёма возвращает неплатёжные mock-реквизиты.

Каноническая ссылка

Сервер выдаёт полный URL существующей заявки с публичным токеном /p/{token}. Токен отличается от внутреннего ID. Не собирайте адрес из домена документации, API, admin или id. Открытие и повторное открытие ссылки не создают заявку.

Не передавайте capability-ссылку в аналитику, Referer, общие логи или службы сокращения ссылок. Для новой вкладки используйте rel="noopener noreferrer". Не размещайте API-ключи в платёжной форме.

Состояния и реквизиты

Состояние сервераЧто показывать
Создана, срок не истёкВыданные реквизиты и фактический expires_at
ОплаченаРезультат оплаты; повторная отправка денег не предлагается
Истекла или отмененаЗавершённое состояние; сверка при фактической поздней оплате
Ссылка не найденаПонятное сообщение и обращение к мерчанту

Срок не вычисляется заново при открытии. Банковские сведения в ответе приёма описывают выданные реквизиты; они не превращают bank_id в поле создания приёма.

Обработчик hosted form поддерживает «Я оплатил» и загрузку чека, если действия разрешены сохранёнными настройками заявки и форма включена в окружении. Исторические настройки выключают оба действия. Они сохраняют свидетельство плательщика, но не меняют статус оплаты, срок, Ledger, баланс или очередь уведомлений мерчанта. Сервер проверяет исходный срок, состояние и CSRF; точный повтор уже принятого свидетельства не создаёт вторую запись. Фактическое включение формы и действий в production здесь не проверено. Сохранённые redirect_urls не означают автоматический переход текущей формы. Только проверенный API/webhook и однократное бизнес-действие мерчанта завершают интеграцию.

Получить баланс и показать его клиенту

Absolut Pay передаёт мерчанту баланс его магазина через GET /api/v2/balance. Мерчант может получить эти данные на своём сервере и показать их уполномоченному пользователю своего кабинета. Это передача информации об остатке, а не перевод средств.

Под «клиентом» в этом сценарии понимается мерчант или пользователь его кабинета с правом видеть баланс магазина. Этот endpoint не возвращает личный кошелёк плательщика и не вычисляет баланс конкретного payer.id. Если вашему продукту нужен баланс конечного покупателя, он ведётся в вашей системе отдельно; общий остаток магазина нельзя выдавать за его личный баланс.

Что мы возвращаем

Запрос читает один магазин по проверенному API-токену, а не все магазины мерчанта. В запросе нет shop_id, payer_id, currency или других query-параметров. Тело пустое, запрос подписывается четырьмя стандартными заголовками.

http
GET /api/v2/balance
X-API-Token: <токен магазина>
X-Timestamp: <Unix-секунды>
X-Nonce: <новый UUID v4>
X-Signature: <подпись точного запроса>

Пример успешного ответа — HTTP 200:

json
{
  "currency": "USDT",
  "available": "847.00000000",
  "reserved": "153.00000000",
  "total": "1000.00000000"
}
ПолеЧто означаетПодпись в кабинете
currencyВалюта учёта; сейчас всегда USDTUSDT рядом с суммами
availableДоступный остаток магазина, decimal-строкаДоступно: 847 USDT
reservedСредства, удержанные под выплаты, decimal-строкаВ резерве: 153 USDT
totalОбщий остаток, включает резерв: available + reservedВсего: 1 000 USDT

Все три суммы имеют 8 знаков после точки. Сохраняйте исходные строки; форматируйте для отображения средствами точной десятичной арифметики. Не складывайте available и total: резерв уже входит в total. Запрос не изменяет деньги и не резервирует средства.

Как передать баланс в свой кабинет

text
Пользователь кабинета мерчанта
    → ваш backend: вход и проверка права на магазин
    → Absolut Pay: подписанный GET /api/v2/balance
    → ваш backend: проверка ответа и выбор публичных полей
    → кабинет: available / reserved / total / USDT
  1. На вашем backend сохраните выданные API origin, token и secret магазина. Ключи не передаются в браузер, мобильное приложение, Telegram WebView или публичную платёжную форму.
  2. При открытии страницы баланса проверьте вход пользователя и его право видеть конкретный магазин. Связь «пользователь → магазин → ключи» определяется вашим сервером; не выбирайте произвольные ключи по неподтверждённому ID из браузера.
  3. Ваш сервер формирует подписанный GET без тела, получает HTTP 200 и проверяет типы полей. Для каждого запроса используйте новые timestamp, nonce и signature.
  4. Передайте в свой frontend только currency, available, reserved, total. Не передавайте токен, секрет и подписанные заголовки. Если у пользователя нет права на общий баланс магазина, не отдавайте эти данные.
  5. Покажите три значения с разными подписями. Защитите собственный ответ от общего кэширования, например Cache-Control: private, no-store; общий CDN-кэш между клиентами недопустим.

Собственный маршрут вашего кабинета, его авторизация и оформление реализуются у мерчанта. Они не являются дополнительными маршрутами Merchant API Absolut Pay. Для нескольких магазинов используются их отдельные ключи и отдельные запросы; этот endpoint не возвращает агрегат по всем магазинам.

Быстрая проверка с сервера

Используйте скачиваемый клиент Node.js. Задайте ABSOLUT_API_BASE_URL, ABSOLUT_API_TOKEN, ABSOLUT_API_SECRET в серверном окружении и выполните:

sh
node examples/client.mjs balance

Клиент выполняет подписанный GET и выводит HTTP-статус и ответ. Не публикуйте финансовые данные или ключи в общих логах. Описание операции и её ошибок · Подпись запросов.

Пример функции для вашего backend

Пример Node.js 20+ использует prepareRequest из client.mjs. Сохраните функцию рядом с этим файлом. Вызывайте её только после проверки вашей пользовательской сессии и права на магазин. В credentials передавайте конфигурацию из серверного хранилища, а не входной JSON клиента.

javascript
import { randomUUID } from 'node:crypto';
import { prepareRequest } from './client.mjs';

export async function readShopBalance(credentials) {
  const request = prepareRequest({
    baseUrl: credentials.baseUrl,
    token: credentials.token,
    secret: credentials.secret,
    method: 'GET',
    target: '/api/v2/balance',
    rawBody: Buffer.alloc(0),
    timestamp: String(Math.floor(Date.now() / 1000)),
    nonce: randomUUID(),
  });

  const response = await fetch(request.url, {
    ...request.options,
    signal: AbortSignal.timeout(10000),
  });
  if (!response.ok) {
    // Не возвращайте клиенту подписанные заголовки или секреты.
    throw new Error('Balance is temporarily unavailable');
  }

  const data = await response.json();
  const decimal = /^(0|[1-9][0-9]*)\.[0-9]{8}$/;
  if (data?.currency !== 'USDT' ||
      !['available', 'reserved', 'total'].every(
        key => typeof data[key] === 'string' && decimal.test(data[key])
      )) {
    throw new Error('Unexpected balance response');
  }

  // Только эти поля передаются авторизованному frontend.
  return {
    currency: data.currency,
    available: data.available,
    reserved: data.reserved,
    total: data.total,
  };
}

Тайм-аут 10 секунд в примере — выбранный лимит вашего клиента, не обещание SLA Absolut Pay. Обработчик вашего приложения должен перехватить ошибку и вернуть безопасное сообщение. Не заменяйте ошибку нулевым балансом.

Когда обновлять баланс

Получайте баланс при открытии раздела и по действию «Обновить». После подтверждённого события приёма/выплаты можно повторно прочитать баланс, но не рассчитывайте актуальный остаток простым прибавлением суммы из webhook: существуют комиссии, резервы и другие операции.

Отдельного webhook «баланс изменился» в текущем Merchant API нет. Уведомления приёма и выплат не содержат полный снимок баланса. Если нужен фоновый polling, согласуйте частоту для своего окружения; фиксированного общего интервала или квоты документация не обещает.

СитуацияЧто показать пользователю
Первый запрос выполняется«Загружаем баланс…», без выдуманных чисел
HTTP 200 и все суммы равны нулюРеальный нулевой остаток; это корректные данные
Получен успешный ответТри суммы и время последнего получения данных вашим сервером
Ошибка или timeout«Не удалось обновить баланс» и повтор чтения; при наличии прошлого снимка явно показать, что он устарел
Нет права на магазинОтказ доступа без раскрытия его баланса

В ответе Absolut Pay нет updated_at или времени снимка. Метку «Получено в…» формирует ваш backend; она означает время получения ответа, а не время последней денежной операции. Не добавляйте её в наш контракт как якобы существующее поле.

Можно ли на основании баланса отправлять выплату

available помогает отобразить доступные средства, но не гарантирует следующую выплату: между GET и POST остаток мог измениться. Сравнивайте с полной стоимостью выплаты в USDT, а не с суммой клиента в RUB/KZT/другой валюте. Окончательную проверку средств и резервирование сервер выполняет атомарно при создании выплаты.

При insufficient_funds не переотправляйте POST автоматически. Проверяйте сохранённый результат поручения и правила ошибок. При неизвестном результате выплаты сначала выполните её lookup; новое чтение баланса само по себе не доказывает исход конкретного перевода.

Направление курса

Приём: rate — единицы исходной валюты за 1 USDT; fee — проценты. Условия фиксируются при выдаче реквизитов и не берутся из новых настроек при подтверждении. Исходный amount и подтверждённый success_paid сохраняются отдельно; webhook использует имя successPaid.

Выплата: rate_fiat_per_usdt — единицы валюты получателя за 1 USDT. principal_usdt, fee_usdt и total_usdt возвращают сохранённые суммы учёта; комиссия выплаты добавляется к основной стоимости. Резерв при неопределённом результате не означает списание без подтверждения и не освобождается по timeout клиента.

Деньги и курсы храните точными decimal-строками или Decimal. Не используйте JavaScript Number / binary float для денежных итогов. Суммы USDT учитываются с точностью 8 знаков. Не пересчитывайте серверный резерв по текущему курсу.

Рыночный курс для нового формирования проверяется на свежесть: основной до 60 секунд, fallback до 300 секунд того же провайдера, будущее наблюдение не более 30 секунд. Договорной фиксированный курс терминала имеет отдельные правила. Эти проверки не меняют условия уже сформированной заявки.

Трансграничный перевод

Проектируемая возможность. «Трансграничный перевод» (cross_border) получает согласованный числовой method_id: 5 как отдельный метод. ID закреплён в проекте контракта; серверная поддержка ещё не реализована. Метод пока не принимается рабочим Merchant API. Наличие UZS в enum не означает доступность RU → UZ. Не добавляйте предлагаемые поля в запросы приёма или выплат.

Например, отправитель в России платит RUB, а получатель в Узбекистане получает UZS. Это две отдельные суммы и валюты. Сначала определяется направление операции Absolut Pay — приём или выплата, затем способ доставки: карта, счёт, телефон и платёжная система. Отдельный метод трансграничного перевода сохраняет детализацию доставки: rail, тип реквизита и платёжная система передаются отдельно в предлагаемой модели.

Что должно быть определеноПочему это отдельно
Отдельный method_id и код cross_borderСогласованный ID — 5; существующие IDs 1–4 сохраняются
Страны отправления и получения, коридорВалюта не определяет географию
Сумма и валюта отправителя; сумма и валюта получателяRUB и UZS нельзя подменить одним currency
Кто конвертирует и кто гарантирует суммуКурс сам по себе не обещает сумму получателю
Котировка, источник, срок и комиссииУсловия фиксируются до отправки, не меняются незаметно
Курс UZS за RUB и расчётный курс fiat за USDTПеревод клиенту и расчёт с мерчантом/исполнителем — разные расчёты
Терминал, тип реквизита, система и проверенный банкКоридор не означает доступность любого исполнителя

Для этого нужны согласованные изменения API, снимков условий, терминалов, маршрутизации и адаптера провайдера. Старые запросы должны сохранить действующее поведение и ID методов. При неизвестном результате требуется lookup исходной попытки; повторная отправка на другой терминал может создать двойной перевод.

Полный проект расширения включает предлагаемые поля, совместимость, этапы и открытые решения. Он требует согласования и не входит в контракт реализованных операций.

Проверка перед запуском

Документация описывает контракт. Включение реальных платежей требует подтверждённого окружения, ключей и методов магазина. Текущий локальный mock не является доказательством реальной интеграции провайдера.

Контрольные сценарии

ПроверкаОжидаемый результат
Корректная подпись POST и GETОтвет соответствует OpenAPI; каждый запрос имеет новые timestamp/nonce
Получение справочникаПодписанный GET возвращает активные ID и original_name; внутренние сопоставления не выдаются
Неизвестное значение банка от провайдераНет выдачи неподтверждённого банка; нужна сверка по исходной заявке
Переименование после выдачиНазвание старой заявки и формы не меняется
Изменение подписанного тела, query или подписиЗапрос отклонён; он не запускает самостоятельную оплату
Повтор принятого nonceАутентификация отклонена
Повтор merchant_order_id с новой подписью409; повторная заявка не создаётся, результат читается по исходному номеру
Потеря ответа созданияВосстановление через result; нет автоматического нового POST/номера
Статус 1 и state=completedТовар/услуга ещё не выдаётся как оплаченная
Проверенный successСумма проверена, бизнес-действие выполнено один раз
Два одинаковых webhookОба получают 200 после устойчивой обработки; бизнес-действие одно
Сбой после записи webhookВосстановление приводит к обработке; нет потерянного или второго исполнения
Поздний success после overdue/cancelВозможная оплата не теряется; задержанные API-ответы и webhook со статусом 1/3/4 не понижают success
Отсутствующий webhook URL422; занятый номер не возобновляется после изменения настроек
Ключ другого магазинаНет доступа к первому магазину/его платежам
Ротация ключейНовые запросы подписаны новым secret; приём webhook учитывает согласованный переход ключей
ISO даты API и Unix даты webhookПравильно преобразуются; срок берётся из ответа
Несовпадение подтверждённой и исходной суммыНет автоматического успеха/выдачи; расхождение отправляется на сверку. Равенство сравнивается точно, независимо от числа знаков после точки

Для локального контура проверки webhook выполняются подписанными fixtures/примером приёмника. Фактическую доставку, живые реквизиты, финансовое зачисление и провайдерскую сверку проверяют отдельно после подключения соответствующего окружения. Не отмечайте их выполненными только по локальному mock.

Ваш сервер

  • API token и secret доступны только backend; шифрование/хранилище секретов и контроль доступа настроены.
  • Исходящие IP согласованы, часы синхронизированы.
  • JSON подписывается и отправляется одними байтами, query не перестраивается после подписи.
  • Суммы хранятся точно; ответ API и исходный номер сохраняются устойчиво.
  • Webhook проверяется до применения, подтверждается после надёжного сохранения, бизнес-эффект защищён транзакцией/очередью.
  • Все источники результата используют одно правило однократного исполнения: webhook, polling, ручная сверка.
  • Есть очередь/задача сверки неопределённых запросов, оповещение о длительном ожидании и согласованный контакт поддержки.
  • Логи и мониторинг не раскрывают секреты, реквизиты, персональные данные и платёжные capability-ссылки.

Согласовать до реального включения

Адрес и версия API, разрешённые методы/валюты/режимы/трафик, тариф, лимиты и допустимый опрос, webhook/ротация ключей, тестовые сценарии и контакт поддержки. Документация не обещает конкретный SLA, квоты, автоматические возвраты или сроки поддержки, пока они не выданы отдельно. Если вам нужна обработка неполной или избыточной оплаты, согласуйте отдельный процесс: в текущий контракт он не входит.

Выплаты клиентам и чтение баланса магазина входят в API v2; их маршруты, резервирование и статусы описаны в разделе «Выплаты клиентам». Реальная отправка провайдеру требует отдельного подключения и проверки. Сетлы, отчётные API, апелляции и публичная отмена не представлены как доступные операции этого контракта. Получение «Я оплатил», файла чека или браузерного redirect не подтверждает оплату в вашей системе.

Дополнительные проверки выплат

  • Отдельные курс и комиссия активированы для согласованного метода и валюты.
  • bank_id взят из активного справочника этого окружения; телефон, карта и счёт не смешиваются.
  • Создание резервирует основную сумму USDT и комиссию; повтор номера не создаёт нового резерва.
  • Тайм-аут POST восстанавливается через GET результата, без повторной отправки.
  • Неопределённое исполнение удерживает резерв; отменяется только неотправленная часть.
  • Для ручного успеха сохранены фактическая сумма, валюта, доказательство и уникальный номер перевода.
  • Worker доставки включён; приёмник проверяет event_id и revision и подтверждает HTTP 200 после сохранения.
  • Реальный провайдер проверен отдельно; тестовый адаптер не выполняет переводов.

Версии и материалы интеграции

25 октября 2026 · документация 2.4.0

База реализации 82b3e33: денежная цепочка 5514c2c и аудит безопасности. Портал не меняет серверный контракт. Приёмка выпуска по CI и проверки реальных провайдеров остаются отдельными условиями запуска.

Комплект содержит реализованный OpenAPI с подтверждённым API origin https://api.absolutpay.com в servers, Postman collection/environment с пустыми ключами и серверные примеры Node.js/Python. Остальные поля OpenAPI совпадают с серверным контрактом. Доступность бизнес-операций не подтверждена; проверенные GET банков и баланса возвращают 503. Версия OpenAPI сохраняется как в источнике; версия документации не повышает версию API.

Исторические материалы

Маршруты и примеры старого v1-портала не относятся к текущему Merchant API. Внешние руководства использованы для сравнения полноты; их авторизация, банковские коды и денежные единицы не заменяют Absolut Pay.

Работа с Postman

Используйте локальное desktop-окружение с синтетическими ключами и изолированным API. Не сохраняйте реальные ключи в экспортируемом environment или облачной коллекции. Портал не принимает ключи, не выполняет запросы к API и не хранит секреты в браузере. Отправка POST всегда явная; после неизвестного результата используйте lookup с тем же номером заказа.

Техническое сравнение с внешними API

Это сравнительный обзор для планирования интеграции, а не список доступных операций Absolut Pay. Наличие функции в чужой документации не разрешает отправлять её поля или маршруты в наш API. Изменения денежного контракта требуют отдельного согласования.

Сверка выполнена 18.10.2026 по PDF «Руководство по интеграции v5.3.pdf» (32 физические страницы), BasicPay ReDoc и Postman Main API Collection. На странице BasicPay заголовок API — Xpay Merchant API 1.27.3; Postman показывает Main API Collection. Принадлежность этих источников и PDF к Euphoria не подтверждена их прочитанным содержимым, поэтому ниже они названы по наблюдаемым названиям. Euphoria не исключена из ориентиров; нельзя приписывать ей возможности другого источника без подтверждения.

Подтверждённые технические пробелы

Возможность во внешнем источникеЧто реализовано в Absolut PayЧего нам не хватает
PDF, стр.18: /api/orders_stats, количество заказов и оплат за периодЧтение отдельного платежа, lookup по номеру; внутренние отчётыПубличного merchant API агрегатов за период. Нужны разрешённый набор метрик, пагинация/период, ограничения нагрузки и контракт доступности
PDF, стр.24–30: тикеты, создание/ответы, состояния, уведомления, вложения, статистикаПрикрепление свидетельства на платёжной форме не является тикетом; внутренние процессы отдельноПубличного merchant ticket lifecycle, истории переписки и безопасного скачивания вложений. Это отдельный продукт с правами, аудитом и сроками хранения
Xpay: раздел Disputes и операции /v1/merchant-api/disputes; PDF: тикетыНет опубликованного Merchant API споровПубличного dispute workflow. Тикет и спор о деньгах надо определить отдельно; финансовая коррекция требует своего согласованного основания
PDF, стр.13 и31: приём по счёту и банковские реквизиты account_number/БИКПриём: СБП, карта, QR, мобильный платёж. Выплата на счёт есть отдельноМетода приёма по счёту, его реквизитов, банковского mapping и адаптера. Payout account не закрывает этот пробел
PDF, стр.14: повторная выдача активного заказа с is_repeated и repeat_countПостоянная уникальность merchant_order_id, conflict при повторном create и lookup после timeoutОтдельного продукта повторной выдачи активной заявки. Это не дефект идемпотентности: сначала надо согласовать, когда переиспользовать реквизиты безопасно
PDF, стр.13: channel и is_vipНастройки магазина и каскада; поля отсутствуют в merchant requestПубличного выбора канала и VIP-признака с проверенной семантикой. Нельзя передавать эти поля как обход лимитов или антифрода
PDF, стр.14: возврат клиента на success/failure/returnredirect_urls сохраняются; текущая форма не выполняет автоматические переходыПроверенного поведения redirects. Возврат на success URL всё равно не должен подтверждать деньги
PDF, стр.32: merchant response содержит notify_code и notify_timeWebhook доставляется сервером; публичный Payin DTO не содержит эти диагностические поляПубличной диагностики доставки уведомления. Безопасный просмотр попыток или повтор доставки требует отдельного контракта и защиты от дублей
PDF, стр.32: тестовый заказ с эмуляцией оплаты и отправкой merchant callbackЕсть локальный неплатёжный sandbox, проверки подписи, синтетические примеры и backend testsЭквивалентного подготовленного внешнего тестового магазина с проверенным end-to-end уведомлением. Текущий sandbox Payin не доказывает реальную доставку callback и денежное зачисление
Xpay: методы TRANSGRAN_SBP, TRANSGRAN_BANK_CARDcross_border — проект отдельного метода с собственным method_id; согласован method_id: 5, серверная поддержка ожидаетсяРеализованного коридора, двух валют/сумм, quote, гарантии суммы, условий терминала и проверенного провайдера. Чужие IDs не переносим; сервер должен реализовать согласованный ID 5 нашего метода, сохранив отдельные rail, тип реквизита и платёжную систему
Xpay: раздел RatesКурс зафиксирован в конкретной операции; самостоятельного Merchant Rates API нетПубличного API предварительных курсов/котировок. Один лишь раздел внешнего API не доказывает его гарантии; наш quote lifecycle ещё нужно согласовать

Видимые внешние разделы, требующие дополнительной сверки

В навигации Xpay также видны Transactions, P2P, C2C, Deposit, Withdraw, Widgets, Data и Attachments, а в документации — Authentication, Data Types, Webhooks и Testing. Название раздела само по себе не определяет его полный денежный смысл. Поэтому мы не называем Widgets эквивалентом нашей hosted form, Data — capability discovery, а Deposit/Withdraw — физическим переводом активов без чтения конкретного контракта.

В Postman прочитан вводный контракт, таблица ошибок и примеры callback; полный набор операций в этой сверке не извлечён. Видны статусы created, pending, dispute, in_check, finished, canceled, expired, failed и ошибки invalid orderId, no requisites, method/currency, insufficient funds, processing, duplicate. Это подтверждает структуру описания жизненного цикла и ошибок, но не доказывает отсутствия аналогичной возможности у нас только из-за другого названия статуса.

Различия, которые не являются пробелами

ОтличиеРешение Absolut Pay
Bearer в Xpay/PDF, HMAC-SHA256 raw body в Postman, сортированные поля в PDFСохраняем действующую Shop HMAC-SHA512, подпись фактически отправляемых байтов и независимую nonce-защиту. Переход на чужую схему не нужен
Копейки в PDFExact decimal-строки и явные валюты; float-примеры не переносим
Payout update в PDF, стр.22У нас исходная сумма и условия неизменны, прогресс и резерв отдельны. Добавление изменения суммы пересматривает инварианты, а не закрывает косметический пробел
Чужие коды банков и названия статусовНаши канонические bank_id и действующие статусы остаются. Provider mapping не передаётся как merchant bank_id
Подтверждение webhook телом 1 в PDFУ нас HTTP 200 после устойчивого сохранения, общий ключ однократного бизнес-действия для webhook и polling
Обычное сравнение подписи в Postman-примерахНаши клиенты проверяют подпись безопасным сравнением. Чужой пример не является основанием ослаблять проверку

Предлагаемый порядок работ

Сначала подтвердить рабочие окружения, реального провайдера и тестовый end-to-end цикл текущего API. Затем согласовать необходимые мерчанту статистику и диагностику webhook, redirects и способы приёма. Публичные споры/тикеты и трансграничные переводы оформить отдельными проектами с финансовыми правилами. Эта последовательность — предложение для приоритизации, не утверждённый план серверных изменений.

Детальные источники, контрольные суммы и выявленные расхождения зафиксированы в отдельной таблице сверки, переданной владельцу портала. Список ранее исключённых пользователем разделов PDF остаётся неподтверждённым: перечисление чужих возможностей здесь не означает их включения в объём реализации.

ПРИЁМ

Создать платёж

POST/api/v2/payins

Получите реквизиты для оплаты или ссылку на платёжную форму.

Подпись обязательна · Как подписать запрос

Что отправить

Поле и типОбязательноОписание
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч. Больше нуля; до 80 символов; точность и лимиты определяет настроенная пара.Максимум 80
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
method_idintegerДаЧисловой ID метода приёма: 1 — СБП, 2 — карта, 3 — QR, 4 — мобильный платёж.Методы: значения и реквизиты →Значения: 1, 2, 3, 4
modestringДаh2h — вернуть реквизиты; payment_form — вернуть полный URL платёжной формы.Значения: h2h, payment_form
traffic_typestringДаТип трафика: primary или secondary. Используйте согласованный тип, не меняйте его для обхода ограничений.Значения: primary, secondary
payerobjectДаСведения о плательщике. ID должен оставаться устойчивым для одного клиента.
payer.idstringДаУстойчивый ID плательщика в вашей системе. Не создавайте новый ID для каждой оплаты.Минимум 1 · Максимум 128
payer.ipstringДаIPv4 или IPv6 плательщика, без zone identifier. Это не IP вашего сервера в allowlist.
payer.emailstringНетEmail плательщика.Минимум 1 · Максимум 254
payer.phonestringНетТелефон плательщика.Минимум 1 · Максимум 32
payer.user_agentstringНетUser-Agent браузера плательщика.Минимум 1 · Максимум 1024
payer.fingerprintstringНетИдентификатор устройства или браузера плательщика.Минимум 1 · Максимум 256
metadataobjectНетВаш дополнительный JSON-объект, до 16384 UTF-8 байт. Без секретов и лишних персональных данных.
webhook_urlstringНетHTTPS URL вашего серверного обработчика. Если не передан, используется сохранённый адрес магазина. Для приёма отсутствие обоих адресов даёт webhook_required; номер запроса остаётся занят.Максимум 2048 · Формат: uri
redirect_urlsobjectНетАдреса возврата браузера. Сейчас сохраняются, но hosted form не выполняет автоматический переход.
redirect_urls.successstringНетHTTPS URL возврата после успеха.Максимум 2048 · Формат: uri
redirect_urls.failurestringНетHTTPS URL возврата после неуспеха.Максимум 2048 · Формат: uri
redirect_urls.returnstringНетHTTPS URL возврата к мерчанту.Максимум 2048 · Формат: uri

Тело — JSON, Content-Type: application/json. Неизвестные поля и повторяющиеся JSON-ключи отклоняются.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

201 означает, что платёж создан. Это ещё не оплата. Дождитесь status_id = 2 через API или проверенное уведомление.

Все поля успешного ответа
Поле и типНаличиеОписание
idstringДаID, выданный Absolut Pay. Передавайте без изменений.
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
status_idintegerДа1 — создан; 2 — оплачен; 3 — истёк; 4 — отменён. Поздняя подтверждённая оплата может изменить 3/4 на 2.Значения: 1, 2, 3, 4
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
method_idintegerДаЧисловой ID метода приёма: 1 — СБП, 2 — карта, 3 — QR, 4 — мобильный платёж.Методы: значения и реквизиты →Значения: 1, 2, 3, 4
modestringДаh2h — вернуть реквизиты; payment_form — вернуть полный URL платёжной формы.Значения: h2h, payment_form
traffic_typestringДаТип трафика: primary или secondary. Используйте согласованный тип, не меняйте его для обхода ограничений.Значения: primary, secondary
ratestringДаЗафиксированный курс: единиц валюты платежа за 1 USDT.Максимум 80
feestringДаЗафиксированная комиссия в процентах. «2» — это 2%, не 2 USDT.Максимум 80
created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
expires_atstringДаФактический срок оплаты по серверу. Не вычисляйте новый срок при открытии формы.Формат: date-time
is_testbooleanДаТестовая заявка: текущий Payin-контур выдаёт только неплатёжные реквизиты.Всегда: true
requisiteobjectПо условиюВыданные реквизиты в режиме h2h. В текущем контуре — mock, переводить деньги по ним нельзя.
requisite.typestringДаТип реквизита.Всегда: mock
requisite.valuestringДаЗначение реквизита; TEST-ONLY-NOT-PAYABLE не подходит для оплаты.Всегда: TEST-ONLY-NOT-PAYABLE
requisite.bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
requisite.bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
payment_urlstringПо условиюПолный URL платёжной формы с публичным токеном. Не собирайте самостоятельно и не отправляйте в общую аналитику.Формат: uri
success_paidstringПо условиюФактически подтверждённая сумма в валюте платежа; может отличаться от исходного amount после подтверждённой корректировки.Максимум 80
paid_atstringПо условиюВремя подтверждённой оплаты.Формат: date-time
close_reasonstringПо условиюПричина отмены. Поле присутствует при status_id = 4.Минимум 1 · Максимум 100
bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

shop_disabled403 / 422

Магазин или его группа недоступны для создания. HTTP 403 — отказ доступа; HTTP 422 — приём отклонён после резервирования номера; HTTP 200 — причина failed в lookup.

Что делать и можно ли повторить

Уточнить активацию у менеджера. Сохранённые операции читать действующим ключом. При 422 номер уже занят.

Повтор: Не автоматически. После активации проверить прежний результат; занятый номер не переиспользовать.

invalid_request400

Неверный JSON, поля, query или идентификатор: неизвестные/повторные параметры, недопустимый IP/URL либо параметры справочника.

Что делать и можно ли повторить

Сравнить запрос со схемой конкретной операции. У GET по ID и POST приёма query не допускается; lookup требует один merchant_order_id.

Повтор: После исправления запроса. Если ранее был неизвестный результат POST — сначала lookup.

invalid_amount400

Сумма не соответствует разрешённому формату или границам.

Что делать и можно ли повторить

Передать положительную точную decimal-строку согласно схеме; не JSON-число. Не округлять деньги через float.

Повтор: После исправления; неизвестный предыдущий POST сначала проверить lookup.

duplicate_merchant_order_id409

Номер бизнес-запроса уже занят в данном магазине, в том числе после отказа. Повтор POST не возвращает старый успешный ответ.

Что делать и можно ли повторить

Читать /payins/result или /payouts/result по исходному номеру. Не заменять номер автоматически.

Повтор: Повторять lookup, не POST. Новый перевод — только отдельное бизнес-решение после выяснения исхода.

webhook_required422

До формирования не найден webhook_url ни в запросе, ни в настройках магазина. Номер уже зарезервирован.

Что делать и можно ли повторить

Настроить публичный HTTPS endpoint для будущих запросов. Проверить failed через lookup. Добавление URL не возобновит этот запрос.

Повтор: Этот POST не повторять; новый номер только для осознанно нового запроса после проверки исхода.

traffic_type_not_allowed422

Тип трафика не разрешён условиями магазина. Номер сохранён.

Что делать и можно ли повторить

Согласовать тип трафика и настройки; не подменять классификацию ради обхода ограничения.

Повтор: Не повторять сохранённый запрос. После согласования возможен отдельный новый бизнес-запрос.

payer_restricted422

Для плательщика действует ограничение. Номер запроса сохранён.

Что делать и можно ли повторить

Уточнить причину и порядок разблокировки у менеджера; не менять ID плательщика для обхода.

Повтор: Автоматический повтор запрещён; сначала решение по ограничению и сверка результата.

unsupported_payment_configuration422

Сочетание метода, валюты и режима не поддерживается действующими условиями магазина.

Что делать и можно ли повторить

Сверить матрицу и согласованные настройки; наличие значения в enum не гарантирует доступность.

Повтор: После согласования — отдельный новый запрос; прежний зарезервированный номер не повторять.

no_requisite422

Каскад не выдал реквизиты. Сохранённый запрос не стал сформированным Payin и не означает оплату.

Что делать и можно ли повторить

Проверить failed через lookup, показать пользователю отсутствие реквизитов и возможность обратиться в поддержку.

Повтор: Не повторять POST автоматически; новая попытка только как отдельное согласованное бизнес-действие.

payment_processing_failed422

Формирование не завершилось; внутренняя причина скрыта за общим публичным кодом.

Что делать и можно ли повторить

Получить lookup по исходному номеру, сохранить request_id и обратиться в поддержку. Не угадывать причину по тексту message.

Повтор: Повтор чтения допустим; POST с занятым номером не повторять.

payment_requires_reconciliation503

Исход формирования требует сверки; отсутствие Payin в ответе не является доказательством отсутствия операции.

Что делать и можно ли повторить

Прочитать lookup; при review_required дождаться сверки поддержки по исходному номеру.

Повтор: Не повторять POST и не создавать автоматически новый номер; разрешён polling.

payment_processing_timeout504

Истекло ожидание HTTP-ответа; долговечная обработка может продолжаться.

Что делать и можно ли повторить

Выполнить lookup по исходному merchant_order_id с новой подписью. Для processing продолжать чтение с задержкой.

Повтор: Только чтение до известного результата; автоматический retry POST выключить.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

ПРИЁМ

Найти результат запроса

GET/api/v2/payins/result

Восстановите результат создания по своему merchant_order_id, например после timeout.

Подпись обязательна · Как подписать запрос

Что отправить

ПараметрОбязательноОписание
merchant_order_idqueryДаВаш номер исходного запроса. Передайте ровно один параметр merchant_order_id; URL-кодируйте значение до подписи.string · Максимальная длина: 128

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

completed означает, что заявка сформирована. Факт оплаты определяется status_id внутри payin. При processing продолжайте чтение; при review_required нужна сверка.

Все поля успешного ответа
Поле и типНаличиеОписание
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
statestringДаРезультат обработки запроса: processing, completed, failed или review_required; это не статус оплаты.Значения: processing, completed, failed, review_required
payinobjectПо условиюТекущий платёж. Присутствует только при state = completed.
payin.idstringДаID, выданный Absolut Pay. Передавайте без изменений.
payin.merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
payin.status_idintegerДа1 — создан; 2 — оплачен; 3 — истёк; 4 — отменён. Поздняя подтверждённая оплата может изменить 3/4 на 2.Значения: 1, 2, 3, 4
payin.amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
payin.currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
payin.method_idintegerДаЧисловой ID метода приёма: 1 — СБП, 2 — карта, 3 — QR, 4 — мобильный платёж.Методы: значения и реквизиты →Значения: 1, 2, 3, 4
payin.modestringДаh2h — вернуть реквизиты; payment_form — вернуть полный URL платёжной формы.Значения: h2h, payment_form
payin.traffic_typestringДаТип трафика: primary или secondary. Используйте согласованный тип, не меняйте его для обхода ограничений.Значения: primary, secondary
payin.ratestringДаЗафиксированный курс: единиц валюты платежа за 1 USDT.Максимум 80
payin.feestringДаЗафиксированная комиссия в процентах. «2» — это 2%, не 2 USDT.Максимум 80
payin.created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
payin.expires_atstringДаФактический срок оплаты по серверу. Не вычисляйте новый срок при открытии формы.Формат: date-time
payin.is_testbooleanДаТестовая заявка: текущий Payin-контур выдаёт только неплатёжные реквизиты.Всегда: true
payin.requisiteobjectПо условиюВыданные реквизиты в режиме h2h. В текущем контуре — mock, переводить деньги по ним нельзя.
payin.requisite.typestringДаТип реквизита.Всегда: mock
payin.requisite.valuestringДаЗначение реквизита; TEST-ONLY-NOT-PAYABLE не подходит для оплаты.Всегда: TEST-ONLY-NOT-PAYABLE
payin.requisite.bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
payin.requisite.bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
payin.payment_urlstringПо условиюПолный URL платёжной формы с публичным токеном. Не собирайте самостоятельно и не отправляйте в общую аналитику.Формат: uri
payin.success_paidstringПо условиюФактически подтверждённая сумма в валюте платежа; может отличаться от исходного amount после подтверждённой корректировки.Максимум 80
payin.paid_atstringПо условиюВремя подтверждённой оплаты.Формат: date-time
payin.close_reasonstringПо условиюПричина отмены. Поле присутствует при status_id = 4.Минимум 1 · Максимум 100
payin.bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
payin.bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
error_codestringПо условиюПричина отклонения запроса. Расшифровка и действие — в справочнике ошибок.Минимум 1 · Максимум 100

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

shop_disabled200 · error_code

Магазин или его группа недоступны для создания. HTTP 403 — отказ доступа; HTTP 422 — приём отклонён после резервирования номера; HTTP 200 — причина failed в lookup.

Что делать и можно ли повторить

Уточнить активацию у менеджера. Сохранённые операции читать действующим ключом. При 422 номер уже занят.

Повтор: Не автоматически. После активации проверить прежний результат; занятый номер не переиспользовать.

invalid_request400

Неверный JSON, поля, query или идентификатор: неизвестные/повторные параметры, недопустимый IP/URL либо параметры справочника.

Что делать и можно ли повторить

Сравнить запрос со схемой конкретной операции. У GET по ID и POST приёма query не допускается; lookup требует один merchant_order_id.

Повтор: После исправления запроса. Если ранее был неизвестный результат POST — сначала lookup.

payin_not_found404

Приём или запрос с таким номером не найден в данном магазине; этот код также используется для неизвестного пути v2.

Что делать и можно ли повторить

Проверить путь, магазин и идентификатор. Пока Payin не сформирован, искать по merchant_order_id. После потерянного ответа повторить чтение и при неопределённости обратиться в поддержку.

Повтор: Повтор чтения допустим с новой подписью. Один 404 не разрешает повтор POST или создание нового номера.

webhook_required200 · error_code

До формирования не найден webhook_url ни в запросе, ни в настройках магазина. Номер уже зарезервирован.

Что делать и можно ли повторить

Настроить публичный HTTPS endpoint для будущих запросов. Проверить failed через lookup. Добавление URL не возобновит этот запрос.

Повтор: Этот POST не повторять; новый номер только для осознанно нового запроса после проверки исхода.

traffic_type_not_allowed200 · error_code

Тип трафика не разрешён условиями магазина. Номер сохранён.

Что делать и можно ли повторить

Согласовать тип трафика и настройки; не подменять классификацию ради обхода ограничения.

Повтор: Не повторять сохранённый запрос. После согласования возможен отдельный новый бизнес-запрос.

payer_restricted200 · error_code

Для плательщика действует ограничение. Номер запроса сохранён.

Что делать и можно ли повторить

Уточнить причину и порядок разблокировки у менеджера; не менять ID плательщика для обхода.

Повтор: Автоматический повтор запрещён; сначала решение по ограничению и сверка результата.

unsupported_payment_configuration200 · error_code

Сочетание метода, валюты и режима не поддерживается действующими условиями магазина.

Что делать и можно ли повторить

Сверить матрицу и согласованные настройки; наличие значения в enum не гарантирует доступность.

Повтор: После согласования — отдельный новый запрос; прежний зарезервированный номер не повторять.

no_requisite200 · error_code

Каскад не выдал реквизиты. Сохранённый запрос не стал сформированным Payin и не означает оплату.

Что делать и можно ли повторить

Проверить failed через lookup, показать пользователю отсутствие реквизитов и возможность обратиться в поддержку.

Повтор: Не повторять POST автоматически; новая попытка только как отдельное согласованное бизнес-действие.

payment_processing_failed200 · error_code

Формирование не завершилось; внутренняя причина скрыта за общим публичным кодом.

Что делать и можно ли повторить

Получить lookup по исходному номеру, сохранить request_id и обратиться в поддержку. Не угадывать причину по тексту message.

Повтор: Повтор чтения допустим; POST с занятым номером не повторять.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

ПРИЁМ

Получить платёж

GET/api/v2/payins/{id}

Узнайте текущий статус и сохранённые условия платежа по его ID.

Подпись обязательна · Как подписать запрос

Что отправить

ПараметрОбязательноОписание
idpathДаID платежа в формате ord_123, выданный Absolut Pay.string · Шаблон: ^ord_[1-9][0-9]*$

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

Не перезаписывайте уже принятый успех запоздавшим статусом. API и webhook должны использовать один ключ однократной выдачи товара.

Все поля успешного ответа
Поле и типНаличиеОписание
idstringДаID, выданный Absolut Pay. Передавайте без изменений.
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
status_idintegerДа1 — создан; 2 — оплачен; 3 — истёк; 4 — отменён. Поздняя подтверждённая оплата может изменить 3/4 на 2.Значения: 1, 2, 3, 4
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
method_idintegerДаЧисловой ID метода приёма: 1 — СБП, 2 — карта, 3 — QR, 4 — мобильный платёж.Методы: значения и реквизиты →Значения: 1, 2, 3, 4
modestringДаh2h — вернуть реквизиты; payment_form — вернуть полный URL платёжной формы.Значения: h2h, payment_form
traffic_typestringДаТип трафика: primary или secondary. Используйте согласованный тип, не меняйте его для обхода ограничений.Значения: primary, secondary
ratestringДаЗафиксированный курс: единиц валюты платежа за 1 USDT.Максимум 80
feestringДаЗафиксированная комиссия в процентах. «2» — это 2%, не 2 USDT.Максимум 80
created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
expires_atstringДаФактический срок оплаты по серверу. Не вычисляйте новый срок при открытии формы.Формат: date-time
is_testbooleanДаТестовая заявка: текущий Payin-контур выдаёт только неплатёжные реквизиты.Всегда: true
requisiteobjectПо условиюВыданные реквизиты в режиме h2h. В текущем контуре — mock, переводить деньги по ним нельзя.
requisite.typestringДаТип реквизита.Всегда: mock
requisite.valuestringДаЗначение реквизита; TEST-ONLY-NOT-PAYABLE не подходит для оплаты.Всегда: TEST-ONLY-NOT-PAYABLE
requisite.bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
requisite.bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
payment_urlstringПо условиюПолный URL платёжной формы с публичным токеном. Не собирайте самостоятельно и не отправляйте в общую аналитику.Формат: uri
success_paidstringПо условиюФактически подтверждённая сумма в валюте платежа; может отличаться от исходного amount после подтверждённой корректировки.Максимум 80
paid_atstringПо условиюВремя подтверждённой оплаты.Формат: date-time
close_reasonstringПо условиюПричина отмены. Поле присутствует при status_id = 4.Минимум 1 · Максимум 100
bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

invalid_request400

Неверный JSON, поля, query или идентификатор: неизвестные/повторные параметры, недопустимый IP/URL либо параметры справочника.

Что делать и можно ли повторить

Сравнить запрос со схемой конкретной операции. У GET по ID и POST приёма query не допускается; lookup требует один merchant_order_id.

Повтор: После исправления запроса. Если ранее был неизвестный результат POST — сначала lookup.

payin_not_found404

Приём или запрос с таким номером не найден в данном магазине; этот код также используется для неизвестного пути v2.

Что делать и можно ли повторить

Проверить путь, магазин и идентификатор. Пока Payin не сформирован, искать по merchant_order_id. После потерянного ответа повторить чтение и при неопределённости обратиться в поддержку.

Повтор: Повтор чтения допустим с новой подписью. Один 404 не разрешает повтор POST или создание нового номера.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

СПРАВОЧНИКИ

Получить список банков

GET/api/v2/banks

Получите канонические ID банков Absolut Pay для реквизитов выплат.

Подпись обязательна · Как подписать запрос

Что отправить

ПараметрОбязательноОписание
qqueryНетПоиск по части названия original_name, до 200 UTF-8 байт.string · Максимальная длина: 200
country_codequeryНетФильтр страны: ровно две заглавные латинские буквы, например RU. Если фильтр не нужен, опустите параметр; null не передаётся.string · Шаблон: ^[A-Z]{2}$
afterqueryНетКурсор next_after из предыдущего ответа. Сохраните прежние фильтры. На странице не более 100 банков.string · Максимальная длина: 24 · Шаблон: ^bank_[1-9][0-9]*$

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

Наличие банка в справочнике не гарантирует доступность маршрута. При next_after продолжите чтение с этим курсором; null означает конец списка.

Все поля успешного ответа
Поле и типНаличиеОписание
itemsarrayДаСтраница банков, до 100 записей.
items[].idstringДаID, выданный Absolut Pay. Передавайте без изменений.Максимум 24
items[].original_namestringДаНазвание банка для отображения пользователю.Минимум 1 · Максимум 200
items[].country_codestring / nullДаДвухбуквенный код страны; null, если не задан. Это не подтверждение доступного коридора.
items[].kindstringДаТип записи: bank — банк, wallet — кошелёк, mobile — мобильный оператор, other — другое.Значения: bank, wallet, mobile, other
next_afterобъект / nullДаКурсор следующей страницы. Передайте его в after без изменений; null означает конец.

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

invalid_request400

Неверный JSON, поля, query или идентификатор: неизвестные/повторные параметры, недопустимый IP/URL либо параметры справочника.

Что делать и можно ли повторить

Сравнить запрос со схемой конкретной операции. У GET по ID и POST приёма query не допускается; lookup требует один merchant_order_id.

Повтор: После исправления запроса. Если ранее был неизвестный результат POST — сначала lookup.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

ВЫПЛАТЫ

Создать выплату

POST/api/v2/payouts

Создайте поручение на выплату клиенту. Сервер проверит условия и зарезервирует стоимость с комиссией в USDT.

Подпись обязательна · Как подписать запрос

Что отправить

Поле и типОбязательноОписание
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч. Больше нуля, до 18 цифр целой части и до 2 после точки.
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
methodstringДаМетод выплаты: sbp — по номеру телефона, card — на карту, account — на банковский счёт.Методы: значения и реквизиты →Значения: sbp, card, account
bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK. Обязательно для sbp; для card/account — необязательно.Максимум 24
recipientobjectДаПолучатель: имя и ровно один вид реквизитов для выбранного метода.
recipient.card_numberstringПо методуНомер карты: от 12 до 19 цифр, без пробелов.
recipient.phone_numberstringПо методуТелефон с + и международным кодом, от 8 до 15 цифр.
recipient.account_numberstringПо методуНомер счёта: от 8 до 34 заглавных латинских букв/цифр.
recipient.customer_namestringДаИмя получателя.Минимум 1 · Максимум 200
metadataobjectНетВаш дополнительный JSON-объект, до 16384 UTF-8 байт. Без секретов и лишних персональных данных.
webhook_urlstringНетHTTPS URL вашего серверного обработчика. Если не передан, используется сохранённый адрес магазина. Выплату можно создать без webhook и проверять через API; только порт 443, без credentials и fragment.Максимум 2000 · Формат: uri

Тело — JSON, Content-Type: application/json. Неизвестные поля и повторяющиеся JSON-ключи отклоняются.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

201 подтверждает создание и резерв, а не перевод получателю. Проверяйте status, paid_amount и remainder. Не отправляйте второй POST после потери ответа.

Все поля успешного ответа
Поле и типНаличиеОписание
metadataobjectДаВаш дополнительный JSON-объект, до 16384 UTF-8 байт. Без секретов и лишних персональных данных.
idstringДаID, выданный Absolut Pay. Передавайте без изменений.
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
methodstringДаМетод выплаты: sbp — по номеру телефона, card — на карту, account — на банковский счёт.Методы: значения и реквизиты →Значения: sbp, card, account
bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK. Обязательно для sbp; для card/account — необязательно.Максимум 24
bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
recipientobjectДаПолучатель: имя и ровно один вид реквизитов для выбранного метода.
recipient.card_numberstringПо методуНомер карты: от 12 до 19 цифр, без пробелов.
recipient.phone_numberstringПо методуТелефон с + и международным кодом, от 8 до 15 цифр.
recipient.account_numberstringПо методуНомер счёта: от 8 до 34 заглавных латинских букв/цифр.
recipient.customer_namestringДаИмя получателя.Минимум 1 · Максимум 200
statusstringДаСтатус выплаты: queued, processing, unknown, success, failed, cancelled или partial.Значения: queued, processing, unknown, success, failed, cancelled, partial
principal_usdtstringДаОсновная стоимость выплаты в USDT по сохранённому курсу. Decimal-строка с 8 знаками после точки.Максимум 80
fee_usdtstringДаКомиссия выплаты в USDT, добавляется сверху к основной стоимости. Decimal-строка с 8 знаками после точки.Максимум 80
total_usdtstringДаОбщая зарезервированная стоимость: principal_usdt + fee_usdt. Decimal-строка с 8 знаками после точки.Максимум 80
rate_fiat_per_usdtstringДаЗафиксированный курс: единиц валюты выплаты за 1 USDT. Decimal-строка с 8 знаками после точки.Максимум 80
fee_percentstringДаСохранённая ставка комиссии выплаты в процентах.
paid_amountstringДаФактически выплаченная сумма в валюте получателя. Decimal-строка с 2 знаками после точки.Максимум 80
remainderstringДаНевыплаченный остаток в валюте получателя. Не является поручением на новую выплату. Decimal-строка с 2 знаками после точки.Максимум 80
revisionintegerДаВерсия состояния выплаты. Применяйте только более новую версию.От 1
created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
updated_atstringДаВремя последнего изменения в ISO 8601.Формат: date-time

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

shop_disabled403

Магазин или его группа недоступны для создания. HTTP 403 — отказ доступа; HTTP 422 — приём отклонён после резервирования номера; HTTP 200 — причина failed в lookup.

Что делать и можно ли повторить

Уточнить активацию у менеджера. Сохранённые операции читать действующим ключом. При 422 номер уже занят.

Повтор: Не автоматически. После активации проверить прежний результат; занятый номер не переиспользовать.

invalid_payout_request400

Некорректные поля, сумма, реквизиты, bank_id, UUID или query. При выплате bank_id должен указывать на активный канонический банк.

Что делать и можно ли повторить

Проверить схему выплаты и тип реквизитов, получить bank_id из справочника. У баланса query запрещён. Ошибка может возникнуть и после занятия номера выплаты.

Повтор: После исправления сначала lookup по прежнему номеру; если номер занят, не переиспользовать.

payout_not_found404

Объект не найден или не принадлежит магазину ключа. При балансе не найдена строка учёта магазина; это не нулевой баланс.

Что делать и можно ли повторить

Проверить магазин, UUID/merchant_order_id; при балансе обратиться в поддержку. После потерянного POST выяснить исход по прежнему номеру.

Повтор: Повтор GET допустим. Один 404 после timeout не доказывает, что можно создать перевод снова.

duplicate_merchant_order_id409

Номер бизнес-запроса уже занят в данном магазине, в том числе после отказа. Повтор POST не возвращает старый успешный ответ.

Что делать и можно ли повторить

Читать /payins/result или /payouts/result по исходному номеру. Не заменять номер автоматически.

Повтор: Повторять lookup, не POST. Новый перевод — только отдельное бизнес-решение после выяснения исхода.

payout_action_unavailable409

При записи обнаружен конфликт идентичности/состояния выплаты. Общая классификация ошибок БД может вернуть этот код публичному созданию.

Что делать и можно ли повторить

Проверить lookup по исходному номеру и передать request_id в поддержку. Это не маршрут изменения выплаты мерчантом.

Повтор: Не повторять POST автоматически; сначала сверка.

payout_configuration_disabled403

Нет активных отдельных условий выплаты для метода/валюты либо магазин/группа отключены. В lookup HTTP 200 это причина rejected.

Что делать и можно ли повторить

Согласовать и активировать условия выплат. Условия приёма не заменяют условия выплат. Номер может быть уже занят.

Повтор: После исправления проверить lookup; отвергнутый занятый номер остаётся занят.

insufficient_funds422

Доступного USDT недостаточно для резерва основной стоимости и комиссии. В lookup HTTP 200 это причина rejected.

Что делать и можно ли повторить

Прочитать баланс, учесть reserved и полную стоимость по условиям выплат. Согласовать пополнение доступного остатка; прежний номер уже занят.

Повтор: Не повторять прежний POST после пополнения. Новый запрос только как новое подтверждённое бизнес-действие.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

ВЫПЛАТЫ

Найти запрос выплаты

GET/api/v2/payouts/result

Проверьте, создана ли выплата с вашим merchant_order_id после неизвестного результата POST.

Подпись обязательна · Как подписать запрос

Что отправить

ПараметрОбязательноОписание
merchant_order_idqueryДаВаш номер исходного запроса. Передайте ровно один параметр merchant_order_id; URL-кодируйте значение до подписи.string · Максимальная длина: 128

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

creating — результат ещё не установлен; created — объект payout доступен; rejected — запрос отклонён, причина в error_code. Статус регистрации не равен статусу выплаты.

Все поля успешного ответа
Поле и типНаличиеОписание
statusstringДаcreating — запрос ещё создаётся; created — выплата создана; rejected — отказ. Это результат регистрации, не статус перечисления.Значения: creating, created, rejected
payoutobject / nullДаСозданная выплата; null, пока запрос creating или rejected.
payout.metadataobjectДаВаш дополнительный JSON-объект, до 16384 UTF-8 байт. Без секретов и лишних персональных данных.
payout.idstringДаID, выданный Absolut Pay. Передавайте без изменений.
payout.merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
payout.amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
payout.currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
payout.methodstringДаМетод выплаты: sbp — по номеру телефона, card — на карту, account — на банковский счёт.Методы: значения и реквизиты →Значения: sbp, card, account
payout.bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK.Максимум 24
payout.bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
payout.recipientobjectДаПолучатель: имя и ровно один вид реквизитов для выбранного метода.
payout.recipient.card_numberstringПо методуНомер карты: от 12 до 19 цифр, без пробелов.
payout.recipient.phone_numberstringПо методуТелефон с + и международным кодом, от 8 до 15 цифр.
payout.recipient.account_numberstringПо методуНомер счёта: от 8 до 34 заглавных латинских букв/цифр.
payout.recipient.customer_namestringДаИмя получателя.Минимум 1 · Максимум 200
payout.statusstringДаСтатус выплаты: queued, processing, unknown, success, failed, cancelled или partial.Значения: queued, processing, unknown, success, failed, cancelled, partial
payout.principal_usdtstringДаОсновная стоимость выплаты в USDT по сохранённому курсу. Decimal-строка с 8 знаками после точки.Максимум 80
payout.fee_usdtstringДаКомиссия выплаты в USDT, добавляется сверху к основной стоимости. Decimal-строка с 8 знаками после точки.Максимум 80
payout.total_usdtstringДаОбщая зарезервированная стоимость: principal_usdt + fee_usdt. Decimal-строка с 8 знаками после точки.Максимум 80
payout.rate_fiat_per_usdtstringДаЗафиксированный курс: единиц валюты выплаты за 1 USDT. Decimal-строка с 8 знаками после точки.Максимум 80
payout.fee_percentstringДаСохранённая ставка комиссии выплаты в процентах.
payout.paid_amountstringДаФактически выплаченная сумма в валюте получателя. Decimal-строка с 2 знаками после точки.Максимум 80
payout.remainderstringДаНевыплаченный остаток в валюте получателя. Не является поручением на новую выплату. Decimal-строка с 2 знаками после точки.Максимум 80
payout.revisionintegerДаВерсия состояния выплаты. Применяйте только более новую версию.От 1
payout.created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
payout.updated_atstringДаВремя последнего изменения в ISO 8601.Формат: date-time
error_codestringПо условиюПричина отклонения запроса. Расшифровка и действие — в справочнике ошибок.Минимум 1 · Максимум 100

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

invalid_payout_request400

Некорректные поля, сумма, реквизиты, bank_id, UUID или query. При выплате bank_id должен указывать на активный канонический банк.

Что делать и можно ли повторить

Проверить схему выплаты и тип реквизитов, получить bank_id из справочника. У баланса query запрещён. Ошибка может возникнуть и после занятия номера выплаты.

Повтор: После исправления сначала lookup по прежнему номеру; если номер занят, не переиспользовать.

payout_not_found404

Объект не найден или не принадлежит магазину ключа. При балансе не найдена строка учёта магазина; это не нулевой баланс.

Что делать и можно ли повторить

Проверить магазин, UUID/merchant_order_id; при балансе обратиться в поддержку. После потерянного POST выяснить исход по прежнему номеру.

Повтор: Повтор GET допустим. Один 404 после timeout не доказывает, что можно создать перевод снова.

payout_configuration_disabled200 · error_code

Нет активных отдельных условий выплаты для метода/валюты либо магазин/группа отключены. В lookup HTTP 200 это причина rejected.

Что делать и можно ли повторить

Согласовать и активировать условия выплат. Условия приёма не заменяют условия выплат. Номер может быть уже занят.

Повтор: После исправления проверить lookup; отвергнутый занятый номер остаётся занят.

insufficient_funds200 · error_code

Доступного USDT недостаточно для резерва основной стоимости и комиссии. В lookup HTTP 200 это причина rejected.

Что делать и можно ли повторить

Прочитать баланс, учесть reserved и полную стоимость по условиям выплат. Согласовать пополнение доступного остатка; прежний номер уже занят.

Повтор: Не повторять прежний POST после пополнения. Новый запрос только как новое подтверждённое бизнес-действие.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

creation_requires_review200 · error_code

Создание выплаты завершилось отклонённым запросом, требующим проверки причины; публичный обработчик не раскрывает внутреннюю ошибку.

Что делать и можно ли повторить

Проверить status и payout в lookup, передать номер и request_id поддержке. Номер зарезервирован, даже когда payout равен null.

Повтор: Повторять только чтение. Не превращать rejected в автоматическое повторное создание.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

ВЫПЛАТЫ

Получить выплату

GET/api/v2/payouts/{id}

Получите статус исполнения, подтверждённую сумму, остаток и сохранённые финансовые условия.

Подпись обязательна · Как подписать запрос

Что отправить

ПараметрОбязательноОписание
idpathДаUUID v4 выплаты, выданный Absolut Pay.string · Шаблон: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

unknown сохраняет резерв и требует сверки. partial не разрешает автоматически отправлять remainder ещё раз.

Все поля успешного ответа
Поле и типНаличиеОписание
metadataobjectДаВаш дополнительный JSON-объект, до 16384 UTF-8 байт. Без секретов и лишних персональных данных.
idstringДаID, выданный Absolut Pay. Передавайте без изменений.
merchant_order_idstringДаВаш уникальный номер запроса внутри магазина. После принятия номер нельзя использовать заново, в том числе после отказа.Минимум 1 · Максимум 128
amountstringДаСумма в указанной валюте. Передавайте строкой, без экспоненты, запятой и разделителей тысяч.Максимум 80
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Значения: RUB, AZN, KZT, KGS, UZS
methodstringДаМетод выплаты: sbp — по номеру телефона, card — на карту, account — на банковский счёт.Методы: значения и реквизиты →Значения: sbp, card, account
bank_idstringПо условиюКанонический ID Absolut Pay из GET /api/v2/banks. Не код шлюза и не номер NSPK. Обязательно для sbp; для card/account — необязательно.Максимум 24
bank_namestringПо условиюСохранённое название канонического банка.Минимум 1 · Максимум 200
recipientobjectДаПолучатель: имя и ровно один вид реквизитов для выбранного метода.
recipient.card_numberstringПо методуНомер карты: от 12 до 19 цифр, без пробелов.
recipient.phone_numberstringПо методуТелефон с + и международным кодом, от 8 до 15 цифр.
recipient.account_numberstringПо методуНомер счёта: от 8 до 34 заглавных латинских букв/цифр.
recipient.customer_namestringДаИмя получателя.Минимум 1 · Максимум 200
statusstringДаСтатус выплаты: queued, processing, unknown, success, failed, cancelled или partial.Значения: queued, processing, unknown, success, failed, cancelled, partial
principal_usdtstringДаОсновная стоимость выплаты в USDT по сохранённому курсу. Decimal-строка с 8 знаками после точки.Максимум 80
fee_usdtstringДаКомиссия выплаты в USDT, добавляется сверху к основной стоимости. Decimal-строка с 8 знаками после точки.Максимум 80
total_usdtstringДаОбщая зарезервированная стоимость: principal_usdt + fee_usdt. Decimal-строка с 8 знаками после точки.Максимум 80
rate_fiat_per_usdtstringДаЗафиксированный курс: единиц валюты выплаты за 1 USDT. Decimal-строка с 8 знаками после точки.Максимум 80
fee_percentstringДаСохранённая ставка комиссии выплаты в процентах.
paid_amountstringДаФактически выплаченная сумма в валюте получателя. Decimal-строка с 2 знаками после точки.Максимум 80
remainderstringДаНевыплаченный остаток в валюте получателя. Не является поручением на новую выплату. Decimal-строка с 2 знаками после точки.Максимум 80
revisionintegerДаВерсия состояния выплаты. Применяйте только более новую версию.От 1
created_atstringДаВремя создания/выдачи в ISO 8601 с часовым поясом.Формат: date-time
updated_atstringДаВремя последнего изменения в ISO 8601.Формат: date-time

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

invalid_payout_request400

Некорректные поля, сумма, реквизиты, bank_id, UUID или query. При выплате bank_id должен указывать на активный канонический банк.

Что делать и можно ли повторить

Проверить схему выплаты и тип реквизитов, получить bank_id из справочника. У баланса query запрещён. Ошибка может возникнуть и после занятия номера выплаты.

Повтор: После исправления сначала lookup по прежнему номеру; если номер занят, не переиспользовать.

payout_not_found404

Объект не найден или не принадлежит магазину ключа. При балансе не найдена строка учёта магазина; это не нулевой баланс.

Что делать и можно ли повторить

Проверить магазин, UUID/merchant_order_id; при балансе обратиться в поддержку. После потерянного POST выяснить исход по прежнему номеру.

Повтор: Повтор GET допустим. Один 404 после timeout не доказывает, что можно создать перевод снова.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

СПРАВОЧНИКИ

Получить баланс

GET/api/v2/balance

Получите баланс магазина через ваш backend и покажите доступный, зарезервированный и общий остаток в кабинете мерчанта.

Подпись обязательна · Как подписать запрос

Что отправить

Тело запроса пустое. Не отправляйте JSON {}: при подписи GET используются нулевые байты тела.

Заголовки запроса
ЗаголовокОбязательноЗначение
X-API-TokenДаAPI-токен магазина. Берётся из серверного хранилища секретов.
X-TimestampДаТекущее Unix-время в секундах. Допустимое отклонение ±300 секунд.
X-NonceДаНовый UUID v4 на каждый HTTP-запрос, включая повторное чтение.
X-SignatureДаHMAC-SHA512, 128 строчных hex-символов. Подписываются точные отправляемые байты.

Как понять результат

Все значения — decimal-строки в USDT. total = available + reserved. Чтение баланса не гарантирует, что следующая выплата пройдёт: резерв проверяется атомарно при создании.

Как передать баланс в свой кабинет: схема и код backend →

Все поля успешного ответа
Поле и типНаличиеОписание
currencystringДаКод валюты операции. Доступная пара метода и валюты определяется условиями магазина.Валюты: названия, суммы и подключение →Всегда: USDT
availablestringДаДоступный остаток магазина в USDT. Decimal-строка с 8 знаками после точки.Максимум 80
reservedstringДаОстаток, зарезервированный под выплаты, в USDT. Decimal-строка с 8 знаками после точки.Максимум 80
totalstringДаОбщий остаток магазина: доступный + зарезервированный, в USDT. Decimal-строка с 8 знаками после точки.Максимум 80

Возможные ошибки

authentication_failed401

Сервер не подтвердил подлинность запроса: заголовки, ключ, подпись, время или nonce не прошли проверку. Точная причина намеренно не раскрывается.

Что делать и можно ли повторить

Проверить выданный ключ, часы, UUID v4 и подпись реально отправленных байтов. После потери ответа POST сначала выполнить lookup.

Повтор: Только после исправления; новые timestamp, nonce и signature. Не повторять старые заголовки.

ip_not_allowed403

Исходящий IP вашего backend не входит в разрешённый список магазина.

Что делать и можно ли повторить

Согласовать фактический адрес с учётом NAT/proxy. Не менять IP для обхода ограничений.

Повтор: После исправления allowlist; для неизвестного POST сначала lookup.

invalid_payout_request400

Некорректные поля, сумма, реквизиты, bank_id, UUID или query. При выплате bank_id должен указывать на активный канонический банк.

Что делать и можно ли повторить

Проверить схему выплаты и тип реквизитов, получить bank_id из справочника. У баланса query запрещён. Ошибка может возникнуть и после занятия номера выплаты.

Повтор: После исправления сначала lookup по прежнему номеру; если номер занят, не переиспользовать.

payout_not_found404

Объект не найден или не принадлежит магазину ключа. При балансе не найдена строка учёта магазина; это не нулевой баланс.

Что делать и можно ли повторить

Проверить магазин, UUID/merchant_order_id; при балансе обратиться в поддержку. После потерянного POST выяснить исход по прежнему номеру.

Повтор: Повтор GET допустим. Один 404 после timeout не доказывает, что можно создать перевод снова.

internal_error503

Результат временно недоступен: хранилище, аудит, ключи или обработчик не позволили завершить ответ. Это не финальный денежный статус.

Что делать и можно ли повторить

Для POST сначала lookup по сохранённому номеру. Для GET повторить чтение с задержкой; при сохранении ошибки передать request_id поддержке.

Повтор: GET — с ограниченным backoff и новой подписью. POST — без автоматического повтора при неизвестном исходе.

Все ошибки, повторы и восстановление →

Подробнее: состояния и правила операции →

Схемы для инструментов

Для обычной интеграции используйте страницы операций: там поля и ошибки описаны словами. Здесь точные JSON Schema из OpenAPI.

CreatePayin
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
      "maxLength": 80,
      "not": {
        "pattern": "^0(\\.0+)?$"
      }
    },
    "currency": {
      "enum": [
        "RUB",
        "AZN",
        "KZT",
        "KGS",
        "UZS"
      ]
    },
    "method_id": {
      "type": "integer",
      "enum": [
        1,
        2,
        3,
        4
      ]
    },
    "mode": {
      "enum": [
        "h2h",
        "payment_form"
      ]
    },
    "traffic_type": {
      "enum": [
        "primary",
        "secondary"
      ]
    },
    "payer": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "ip": {
          "type": "string",
          "description": "IPv4 or IPv6 address without a zone.",
          "anyOf": [
            {
              "format": "ipv4"
            },
            {
              "format": "ipv6"
            }
          ]
        },
        "email": {
          "type": "string",
          "minLength": 1,
          "maxLength": 254
        },
        "phone": {
          "type": "string",
          "minLength": 1,
          "maxLength": 32
        },
        "user_agent": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1024
        },
        "fingerprint": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256
        }
      },
      "required": [
        "id",
        "ip"
      ]
    },
    "metadata": {
      "type": "object",
      "description": "At most 16384 UTF-8 bytes; duplicate keys are forbidden."
    },
    "webhook_url": {
      "type": "string",
      "format": "uri",
      "pattern": "^https://",
      "maxLength": 2048,
      "description": "Optional HTTPS override; takes priority over the Shop default captured during preparation. If neither destination exists, the accepted request fails with HTTP 422 webhook_required before gateway dispatch or Payin formation. Its merchant_order_id remains permanently reserved; later Shop edits never restart it."
    },
    "redirect_urls": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "success": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://",
          "maxLength": 2048
        },
        "failure": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://",
          "maxLength": 2048
        },
        "return": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://",
          "maxLength": 2048
        }
      },
      "required": []
    }
  },
  "required": [
    "merchant_order_id",
    "amount",
    "currency",
    "method_id",
    "mode",
    "traffic_type",
    "payer"
  ]
}
MockRequisite
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "type": {
      "const": "mock"
    },
    "value": {
      "const": "TEST-ONLY-NOT-PAYABLE"
    },
    "bank_id": {
      "type": "string",
      "pattern": "^bank_[1-9][0-9]*$",
      "maxLength": 24,
      "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
    },
    "bank_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Frozen original_name of the issued canonical bank; not the raw provider alias."
    }
  },
  "required": [
    "type",
    "value"
  ],
  "dependentRequired": {
    "bank_id": [
      "bank_name"
    ],
    "bank_name": [
      "bank_id"
    ]
  }
}
Payin
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^ord_[1-9][0-9]*$"
    },
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "status_id": {
      "type": "integer",
      "enum": [
        1,
        2,
        3,
        4
      ],
      "description": "1 created; 2 success; 3 overdue; 4 cancel."
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
      "maxLength": 80,
      "not": {
        "pattern": "^0(\\.0+)?$"
      }
    },
    "currency": {
      "enum": [
        "RUB",
        "AZN",
        "KZT",
        "KGS",
        "UZS"
      ]
    },
    "method_id": {
      "type": "integer",
      "enum": [
        1,
        2,
        3,
        4
      ]
    },
    "mode": {
      "enum": [
        "h2h",
        "payment_form"
      ]
    },
    "traffic_type": {
      "enum": [
        "primary",
        "secondary"
      ]
    },
    "rate": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
      "maxLength": 80,
      "not": {
        "pattern": "^0(\\.0+)?$"
      }
    },
    "fee": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
      "maxLength": 80,
      "description": "Frozen fee percentage, not a currency amount."
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "expires_at": {
      "type": "string",
      "format": "date-time"
    },
    "is_test": {
      "const": true
    },
    "requisite": {
      "$ref": "#/components/schemas/MockRequisite"
    },
    "payment_url": {
      "type": "string",
      "format": "uri",
      "description": "Platform capability URL; never a provider URL."
    },
    "success_paid": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
      "maxLength": 80,
      "not": {
        "pattern": "^0(\\.0+)?$"
      }
    },
    "paid_at": {
      "type": "string",
      "format": "date-time"
    },
    "close_reason": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "bank_id": {
      "type": "string",
      "pattern": "^bank_[1-9][0-9]*$",
      "maxLength": 24,
      "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
    },
    "bank_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Frozen original_name of the issued canonical bank; not the raw provider alias."
    }
  },
  "required": [
    "id",
    "merchant_order_id",
    "status_id",
    "amount",
    "currency",
    "method_id",
    "mode",
    "traffic_type",
    "rate",
    "fee",
    "created_at",
    "expires_at",
    "is_test"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "mode": {
            "const": "h2h"
          }
        }
      },
      "then": {
        "required": [
          "requisite"
        ],
        "not": {
          "required": [
            "payment_url"
          ]
        }
      },
      "else": {
        "required": [
          "payment_url"
        ],
        "not": {
          "required": [
            "requisite"
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "status_id": {
            "const": 2
          }
        }
      },
      "then": {
        "required": [
          "success_paid",
          "paid_at"
        ]
      },
      "else": {
        "not": {
          "anyOf": [
            {
              "required": [
                "success_paid"
              ]
            },
            {
              "required": [
                "paid_at"
              ]
            }
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "status_id": {
            "const": 4
          }
        }
      },
      "then": {
        "required": [
          "close_reason"
        ]
      },
      "else": {
        "not": {
          "required": [
            "close_reason"
          ]
        }
      }
    }
  ],
  "dependentRequired": {
    "bank_id": [
      "bank_name"
    ],
    "bank_name": [
      "bank_id"
    ]
  }
}
RequestResult
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "state": {
      "enum": [
        "processing",
        "completed",
        "failed",
        "review_required"
      ]
    },
    "payin": {
      "$ref": "#/components/schemas/Payin"
    },
    "error_code": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Merchant-safe failure code. webhook_required is a terminal rejection without Payin; adding a destination later does not retry the reserved request."
    }
  },
  "required": [
    "merchant_order_id",
    "state"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "state": {
            "const": "completed"
          }
        }
      },
      "then": {
        "required": [
          "payin"
        ]
      },
      "else": {
        "not": {
          "required": [
            "payin"
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "state": {
            "const": "failed"
          }
        }
      },
      "then": {
        "required": [
          "error_code"
        ]
      },
      "else": {
        "not": {
          "required": [
            "error_code"
          ]
        }
      }
    }
  ]
}
Error
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "error": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "code": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        },
        "message": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        }
      },
      "required": [
        "code",
        "message"
      ]
    },
    "request_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    }
  },
  "required": [
    "error",
    "request_id"
  ]
}
Bank
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^bank_[1-9][0-9]*$",
      "maxLength": 24,
      "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
    },
    "original_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "country_code": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[A-Z]{2}$"
    },
    "kind": {
      "type": "string",
      "enum": [
        "bank",
        "wallet",
        "mobile",
        "other"
      ]
    }
  },
  "required": [
    "id",
    "original_name",
    "country_code",
    "kind"
  ]
}
BankPage
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "items": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "$ref": "#/components/schemas/Bank"
      }
    },
    "next_after": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^bank_[1-9][0-9]*$",
          "maxLength": 24,
          "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "items",
    "next_after"
  ]
}
PayoutRecipient
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "card_number": {
      "type": "string",
      "pattern": "^[0-9]{12,19}$"
    },
    "phone_number": {
      "type": "string",
      "pattern": "^\\+[1-9][0-9]{7,14}$"
    },
    "account_number": {
      "type": "string",
      "pattern": "^[A-Z0-9]{8,34}$"
    },
    "customer_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "customer_name"
  ],
  "description": "Exactly one destination field is accepted for the selected payout method; account payouts execute manually until a reviewed provider protocol supports them."
}
CreatePayout
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]{0,17})(\\.[0-9]{1,2})?$",
      "not": {
        "pattern": "^0(\\.0+)?$"
      }
    },
    "currency": {
      "enum": [
        "RUB",
        "AZN",
        "KZT",
        "KGS",
        "UZS"
      ]
    },
    "method": {
      "enum": [
        "sbp",
        "card",
        "account"
      ]
    },
    "bank_id": {
      "type": "string",
      "pattern": "^bank_[1-9][0-9]*$",
      "maxLength": 24,
      "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
    },
    "recipient": {
      "$ref": "#/components/schemas/PayoutRecipient"
    },
    "metadata": {
      "type": "object",
      "description": "At most 16384 UTF-8 bytes. Duplicate JSON keys are forbidden; nesting is at most 32 levels."
    },
    "webhook_url": {
      "type": "string",
      "format": "uri",
      "pattern": "^https://",
      "maxLength": 2000,
      "description": "Optional HTTPS destination override, port 443 only, without credentials or fragment. The Shop default is captured when omitted. A payout can be created without a webhook destination; poll its signed result instead."
    }
  },
  "required": [
    "merchant_order_id",
    "amount",
    "currency",
    "method",
    "recipient"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "method": {
            "const": "sbp"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "phone_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "card_number"
                  ]
                },
                {
                  "required": [
                    "account_number"
                  ]
                }
              ]
            }
          }
        },
        "required": [
          "bank_id"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "method": {
            "const": "card"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "card_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "phone_number"
                  ]
                },
                {
                  "required": [
                    "account_number"
                  ]
                }
              ]
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "method": {
            "const": "account"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "account_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "phone_number"
                  ]
                },
                {
                  "required": [
                    "card_number"
                  ]
                }
              ]
            }
          }
        }
      }
    }
  ],
  "description": "Shop identity comes from the signed API token. Rate and commission come from the separate active payout configuration and cannot be supplied by the client. Principal plus commission is atomically reserved in USDT. Merchant order numbers remain permanently reserved even after creation rejection or loss of response. Never create another payout to recover an unknown transfer."
}
Payout
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "metadata": {
      "type": "object",
      "description": "At most 16384 UTF-8 bytes. Duplicate JSON keys are forbidden; nesting is at most 32 levels."
    },
    "id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "Permanent UUID v4 issued by Absolut Pay."
    },
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places.",
      "not": {
        "const": "0.00"
      }
    },
    "currency": {
      "enum": [
        "RUB",
        "AZN",
        "KZT",
        "KGS",
        "UZS"
      ]
    },
    "method": {
      "enum": [
        "sbp",
        "card",
        "account"
      ]
    },
    "bank_id": {
      "type": "string",
      "pattern": "^bank_[1-9][0-9]*$",
      "maxLength": 24,
      "description": "Immutable Absolut Pay bank ID from GET /api/v2/banks. Not an NSPK ID or provider code."
    },
    "bank_name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Frozen original_name of the selected canonical bank."
    },
    "recipient": {
      "$ref": "#/components/schemas/PayoutRecipient"
    },
    "status": {
      "enum": [
        "queued",
        "processing",
        "unknown",
        "success",
        "failed",
        "cancelled",
        "partial"
      ],
      "description": "unknown retains the reserve; partial means some parts succeeded or the final part outcomes differ. Remainder includes unpaid failed/cancelled parts and is not automatically reissued."
    },
    "principal_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places.",
      "not": {
        "const": "0.00000000"
      }
    },
    "fee_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places."
    },
    "total_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places.",
      "not": {
        "const": "0.00000000"
      }
    },
    "rate_fiat_per_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Frozen source-currency units per USDT; subsequent configuration edits cannot recalculate an existing payout.",
      "not": {
        "const": "0.00000000"
      }
    },
    "fee_percent": {
      "type": "string",
      "pattern": "^(?:(?:0|[1-9][0-9]?)\\.[0-9]{8}|100\\.00000000)$",
      "description": "Frozen percentage, not a currency amount."
    },
    "paid_amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places."
    },
    "remainder": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places."
    },
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "updated_at": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "metadata",
    "id",
    "merchant_order_id",
    "amount",
    "currency",
    "method",
    "recipient",
    "status",
    "principal_usdt",
    "fee_usdt",
    "total_usdt",
    "rate_fiat_per_usdt",
    "fee_percent",
    "paid_amount",
    "remainder",
    "revision",
    "created_at",
    "updated_at"
  ],
  "dependentRequired": {
    "bank_id": [
      "bank_name"
    ],
    "bank_name": [
      "bank_id"
    ]
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "method": {
            "const": "sbp"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "phone_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "card_number"
                  ]
                },
                {
                  "required": [
                    "account_number"
                  ]
                }
              ]
            }
          }
        },
        "required": [
          "bank_id",
          "bank_name"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "method": {
            "const": "card"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "card_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "phone_number"
                  ]
                },
                {
                  "required": [
                    "account_number"
                  ]
                }
              ]
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "method": {
            "const": "account"
          }
        },
        "required": [
          "method"
        ]
      },
      "then": {
        "properties": {
          "recipient": {
            "required": [
              "account_number"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "phone_number"
                  ]
                },
                {
                  "required": [
                    "card_number"
                  ]
                }
              ]
            }
          }
        }
      }
    }
  ],
  "description": "Merchant-safe payout projection. It excludes provider keys, references, dispatch attempts, internal part/accounting IDs, staff details and execution evidence. Amounts remain strings."
}
PayoutRequestResult
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "status": {
      "enum": [
        "creating",
        "created",
        "rejected"
      ]
    },
    "payout": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/Payout"
        },
        {
          "type": "null"
        }
      ]
    },
    "error_code": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    }
  },
  "required": [
    "status",
    "payout"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "status": {
            "const": "created"
          }
        },
        "required": [
          "status"
        ]
      },
      "then": {
        "properties": {
          "payout": {
            "$ref": "#/components/schemas/Payout"
          }
        }
      },
      "else": {
        "properties": {
          "payout": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "status": {
            "const": "rejected"
          }
        },
        "required": [
          "status"
        ]
      },
      "then": {
        "required": [
          "error_code"
        ]
      },
      "else": {
        "not": {
          "required": [
            "error_code"
          ]
        }
      }
    }
  ],
  "description": "Durable creation outcome, distinct from payout execution status. Retrieve this after timeout/503/duplicate; creating or rejected does not mean permission to submit the same number with changed parameters."
}
PayoutWebhook
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "Permanent UUID v4 issued by Absolut Pay."
    },
    "event_type": {
      "const": "payout.updated"
    },
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "payout_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "Permanent UUID v4 issued by Absolut Pay."
    },
    "merchant_order_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "status": {
      "enum": [
        "queued",
        "processing",
        "unknown",
        "success",
        "failed",
        "cancelled",
        "partial"
      ],
      "description": "unknown retains the reserve; partial means some parts succeeded or the final part outcomes differ. Remainder includes unpaid failed/cancelled parts and is not automatically reissued."
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places.",
      "not": {
        "const": "0.00"
      }
    },
    "currency": {
      "enum": [
        "RUB",
        "AZN",
        "KZT",
        "KGS",
        "UZS"
      ]
    },
    "paid_amount": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places."
    },
    "remainder": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{2}$",
      "maxLength": 80,
      "description": "Exact source-currency amount with two decimal places."
    },
    "principal_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places.",
      "not": {
        "const": "0.00000000"
      }
    },
    "fee_usdt": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places."
    },
    "updated_at": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "event_id",
    "event_type",
    "revision",
    "payout_id",
    "merchant_order_id",
    "status",
    "amount",
    "currency",
    "paid_amount",
    "remainder",
    "principal_usdt",
    "fee_usdt",
    "updated_at"
  ],
  "description": "At-least-once committed workflow update. Deduplicate event_id and apply only a newer revision for the payout. A payout notification cannot authorize a new transfer. Verify HMAC-SHA512 over X-Webhook-Timestamp + \".\" + exact body bytes using the Shop API secret, then return HTTP 200 only after durable application."
}
Balance
json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "currency": {
      "const": "USDT"
    },
    "available": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places."
    },
    "reserved": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places."
    },
    "total": {
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)\\.[0-9]{8}$",
      "maxLength": 80,
      "description": "Exact USDT amount with eight decimal places."
    }
  },
  "required": [
    "currency",
    "available",
    "reserved",
    "total"
  ],
  "description": "One Shop only, selected by the signed API token. Total equals available plus reserved, including pending payout principal and fee."
}