Адрес API: https://api-app.solvr.online.Документ описывает API мерчанта: платежи, вебхуки, споры и баланс.1.
Получить ключ sk_ и секрет вебхука.
2.
Принять у себя POST на webhookUrl, проверить подпись и ответить 200.
3.
Создать тестовый приём на небольшую сумму.
4.
Сверить вебхук с GET /v1/payments/{id}.
5.
Выплаты включать после проверки GET /v1/balance.
1. Ключи#
Администратор платформы создаёт мерчанта и передаёт секрет.1.
Ключ API. Начинается с sk_ и подписывает ваши запросы.
Ключ уходит в заголовок Authorization.Вам также называют код мерчанта. В ответах он приходит как merchantId. В запросы его подставлять не нужно: платформа узнаёт мерчанта по ключу.2. Формат запросов#
Для загрузки файла Content-Type другой, это в шаге про чек.{
"code": "VALIDATION_ERROR",
"message": "validation failed",
"errors": {
"amount": ["must be a positive decimal"]
}
}
Поле errors есть, когда не прошли конкретные поля.Суммы пишите строкой: "1500", "1500.50". Дробная часть через точку.Валюта из трёх букв. Платформа приводит её к верхнему регистру: rub и RUB совпадают.3. Приём и выплата#
type принимает два значения.in - клиент платит вам. Вы показываете ему реквизиты или страницу оплаты. Когда деньги дошли, приходит вебхук со статусом оплаты.out - выплата клиенту. Перед поиском исполнителя платформа удерживает amount на счёте мерчанта в валюте платежа. Выплата 1500 RUB держит 1500 на счёте RUB. Если доступного остатка в этой валюте не хватает, удержание не создаётся: платёж переходит в error, а первый ответ создания приходит с кодом 422 и INSUFFICIENT_FUNDS, без тела платежа. Баланс в минус не уходит. Повтор с тем же merchantTransactionId вернёт этот платёж уже с кодом 201. В вебхуке reason начинается с ledger hold failed:.4. Приём#
Шаг 1. Номер заказа#
merchantTransactionId - ваш номер заказа. Повтор с той же строкой возвращает тот же платёж и второй не создаёт. Новый заказ получает новый номер. Как устроен повтор, в разделе 6.Шаг 2. Создание#
{
"type": "in",
"amount": "1500",
"currency": "RUB",
"method": "sbp",
"geo": "RU",
"merchantTransactionId": "order-1842",
"clientId": "user-18421",
"webhookUrl": "https://shop.example/hooks/solvr",
"returnUrl": "https://shop.example/orders/1842",
"payer": {
"name": "Марина Соколова"
}
}
Обязательны type, amount, currency, method, geo, merchantTransactionId.method - код способа, geo - регион этого способа. Для переводов по России geo равен RU. Коды лежат в openapi.yaml, схема PaymentMethod.webhookUrl - куда слать вебхуки по этому платежу. Пустое поле заменяется адресом, который администратор указал при подключении. Если не задан и он, уведомлений не будет: статус забирайте через GET /v1/payments/{id}.returnUrl - адрес возврата со страницы оплаты, clientId - ваш идентификатор покупателя. Оба поля можно опустить.payer - данные плательщика. Платформа сохраняет объект и возвращает его в платеже.amountUsd можно посчитать самим. Пустое поле платформа заполнит сама, курс потом виден в merchantRate. В вебхуке курса нет, он есть только в ответе API.ttlSec - сколько секунд живёт платёж. Пустое поле берёт срок из настроек мерчанта.autoAmountLimit и autoAmountStep задают, сколько соседних сумм перебрать, если на точную нет свободного реквизита, и шаг между ними. Пустые поля читаются из настроек мерчанта. По умолчанию оба равны 1: ищется только запрошенная сумма. Клиент может увидеть сумму, которую подобрала платформа.Шаг 3. Ответ 201#
Успех - код 201 и JSON платежа.id сохраните. Дальше все запросы идут по нему.
status в начале чаще всего process: платёж ещё не оплачен.
requisiteDetails показываете клиенту.
form.url - путь страницы оплаты, например /v1/form/токен. Полный адрес: https://api-app.solvr.online плюс этот путь.
form.token нужен, если страницу запрашиваете отдельно.
expiresAt - момент, после которого неоплаченный платёж станет expired.
merchantRate - курс мерчанта на момент создания.
Пример реквизитов для СБП:{
"phone_number": "79031234567",
"owner_name": "Алексей Морозов",
"bank_name": "Т-Банк",
"country_name": "РФ"
}
{
"card_number": "2200770012345678",
"owner_name": "Алексей Морозов",
"bank_name": "Сбербанк"
}
{
"qr_data": "https://qr.example/pay/abc",
"qr_image": "https://cdn.example/qr/abc.png",
"owner_name": "Алексей Морозов",
"bank_name": "Т-Банк"
}
Набор полей зависит от способа. Показывайте клиенту то, что пришло.Если requisiteDetails пустой и статус process, реквизит ещё не подобран. Повторите создание с тем же merchantTransactionId или запросите GET /v1/payments/{id} позже.Шаг 4. Страница оплаты#
Своя страница. Сумму, банк, телефон или карту берёте из requisiteDetails, таймер - из expiresAt.Страница платформы. Отправьте клиента на https://api-app.solvr.online + form.url. Страница открывается без ключа API. Когда время выйдет или человек закончит, его можно вернуть на ваш returnUrl.Проверить страницу по токену:Ключ не нужен. Неизвестный или истёкший токен даёт 404.Посмотреть страницу по id платежа, уже с ключом:Шаг 5. Оплата#
На каждое изменение статуса приходит вебхук.Смотрите status и paidAmount.| Статус | Что сделать у себя |
|---|
process | Ждать. Клиент ещё платит или деньги ещё не зачислены. |
confirmation_requested | Платформа просит файл. Загрузите чек. В вебхуке статус приходит этим именем. В ответе API то же состояние лежит в status строкой 9. |
paid | Деньги пришли ровно на сумму заказа. Заказ можно закрывать как оплаченный. |
underpaid | Пришло меньше, чем просили. Смотрите paidAmount и решите, добирать разницу или нет. |
overpaid | Пришло больше. Смотрите paidAmount. |
expired | Время вышло, оплаты нет. Заказ не оплачен. |
cancel | Платёж отменён. |
error | Провести не удалось. Причина лежит в истории и в reason вебхука. |
chargeback | Уже после успеха оплату оспорили. Заказ больше нельзя считать успешно оплаченным. |
Оплаченным заказ отмечайте только на paid. underpaid и overpaid означают другую сумму.Шаг 6. Подтверждение#
Вебхук придёт со статусом confirmation_requested, причиной confirmation_requested, полем confirmationKind и иногда confirmationNote.other - другой файл, текст в confirmationNote
file - сам файл, обязателен
kind - receipt, video или other
note - короткая подпись, можно пустой
Если файл уже лежит по ссылке:{
"fileUrl": "https://shop.example/receipts/1842.jpg",
"note": "Чек клиента"
}
После проверки статус снова сменится, и придёт новый вебхук.5. Выплата#
Шаг 1. Баланс#
{
"currency": "USDT",
"balance": "1000",
"held": "0",
"available": "1000"
}
Без параметра currency ответ относится к счёту USDT. Удержание выплаты лежит на счёте в валюте платежа, для рублёвой выплаты это RUB:{
"currency": "RUB",
"balance": "50000",
"held": "1500",
"available": "48500"
}
available равен balance минус held. Его сравнивайте с суммой выплаты в той же валюте. Если счёта в этой валюте ещё нет, все три числа придут "0", код ответа 200.Шаг 2. Создание#
Тело такое же, как у приёма, но type равен out. Реквизиты получателя передайте в payer. Способ и регион обязательны.{
"type": "out",
"amount": "1500",
"currency": "RUB",
"method": "sbp",
"geo": "RU",
"merchantTransactionId": "payout-1842",
"webhookUrl": "https://shop.example/hooks/solvr",
"payer": {
"phone_number": "79031234567",
"bank_name": "Т-Банк",
"owner_name": "Марина Соколова"
}
}
Платформа удерживает сумму выплаты и после этого ищет, кто отправит деньги.Если получателя некому отправить, ответ 503 и код NO_LIQUIDITY или NO_REQUISITES. Платёж при этом может уже существовать. Повторите тот же запрос с тем же merchantTransactionId: платформа попробует найти исполнителя снова.Шаг 3. Конечный статус#
Для выплаты успешные конечные статусы те же: paid, underpaid, overpaid. Пока идёт отправка, статус process. Отмена возможна, пока выплата ещё не в конечном статусе, см. раздел 7.Страницы оплаты у выплаты нет. Поля form в ответе не будет.6. Повтор номера заказа#
merchantTransactionId - ключ идемпотентности среди платежей этого мерчанта.Если сеть оборвалась и вы не знаете, создался ли платёж, отправьте тот же JSON ещё раз.Второй платёж не появится.
Ответ снова 201, внутри тот же id.
Если первый раз реквизит не нашёлся, повтор попробует подобрать его снова.
Если платёж уже в error, повтор вернёт его как есть и ошибку не исправит.
Номер заказа нельзя переиспользовать для новой оплаты.7. Отмена#
Отменить можно платёж в статусе process или confirmation_requested. В ответе API второй из них приходит строкой 9, см. таблицу статусов выше.Тело не нужно. Успех - 200 и платёж со статусом cancel.Уже оплаченный, истёкший или ошибочный платёж даёт 409 и код INVALID_STATE. После оплаты разногласие оформляют спором, раздел 9.8. Чтение платежей#
Фильтр status принимает process, paid, underpaid, overpaid, expired, cancel, chargeback, error. Строку 9 и имя confirmation_requested фильтр не принимает. В списке без фильтра такой платёж всё равно может встретиться, у него status равен "9".Записи идут от старых к новым: сначала createdAt, при равном времени - id. В записи есть fromStatus, toStatus, reason и createdAt. reason - та же строка, что в вебхуке.Чужой id даёт 403 с кодом FORBIDDEN. Несуществующий даёт 404 с кодом NOT_FOUND.9. Спор#
Спор нужен, когда вы не согласны с итогом. Например клиент показывает чек, а статус так и остался process, или сумма зачислена не та.{
"paymentId": "7c2a9e14-8b3f-4d61-a5e0-6f18c2d94b70",
"reason": "Клиент оплатил, статус остался process",
"evidence": {
"receiptUrl": "https://shop.example/receipts/1842.jpg"
}
}
paymentId и reason обязательны. evidence - произвольный JSON: ссылки, текст, номера.Ответ 201. Статус нового спора process.Решение принимает администратор платформы. Итог читается так:| Статус | Смысл |
|---|
process | Спор ещё смотрят. |
success | Спор закрыт как оплата. |
canceled | Спор отклонён. |
Спор не заменяет вебхук платежа. После решения статус самого платежа может измениться отдельно, и тогда придёт обычный вебхук платежа.10. Коды ошибок#
| HTTP | code | Что делать |
|---|
| 401 | UNAUTHORIZED | Ключа нет или он неверный. Проверьте заголовок Authorization: Bearer sk_.... |
| 403 | MERCHANT_REQUIRED | Ключ не привязан к мерчанту. Нужен другой ключ. |
| 403 | FORBIDDEN | Этот платёж или спор не ваш. |
| 404 | NOT_FOUND | Нет такого id или токена страницы. |
| 409 | IDEMPOTENCY_CONFLICT | Запись с таким ключом уже есть и конфликтует. |
| 409 | INVALID_STATE | Действие не подходит к текущему статусу. Например отмена уже оплачен ного. |
| 422 | VALIDATION_ERROR | Поправьте поля из errors и отправьте снова. |
| 422 | INSUFFICIENT_FUNDS | На выплату не хватило остатка в валюте платежа. Пополните этот счёт и смотрите правило в разделе 3. |
| 503 | NO_LIQUIDITY | Сейчас некого назначить. Повторите тот же запрос позже. |
| 503 | NO_REQUISITES | Нет подходящих реквизитов. Повторите позже или смените метод. |
| 503 | SERVICE_UNAVAILABLE | Часть платформы недоступна. Повторите тот же запрос. |
| 500 | INTERNAL | Сбой. Повторите тот же merchantTransactionId. Новый номер заводите после того, как проверите платёж через GET. |
Повторять безопасно создание с те м же merchantTransactionId. Новый номер заводите, когда старый платёж вам больше не нужен.11. Способы оплаты#
Для российских способов geo равен RU, валюта приёма - RUB. Полный список кодов, включая банки, карты и QR, лежит в openapi.yaml, схема PaymentMethod.Платформа примет и другой код, если он заведён в настройках мерчанта. Если реквизитов по коду нет, создание вернёт NO_REQUISITES или NO_LIQUIDITY. Modified at 2026-10-24 01:14:28