Как мерчанту подключить платежи
Базовый адрес API: https://api-app.solvr.online.Ниже только то, что делает мерчант. Кабинет администратора, трейдеры и внутренние сервисы сюда не входят.1. Что вам выдают до первой оплаты#
Администратор платформы создаёт мерчанта и передаёт два секрета.1.
Ключ API. Он начинается с sk_. Им подписываются ваши запросы. Ключ показывают один раз. Если он утёк, старый отзывают и выпускают новый.
2.
Секрет вебхука. Им вы проверяете, что уведомление прислала платформа, а не посторонний. Секрет тоже показывают один раз. Его можно сменить, после смены старые подписи перестанут подходить.
Ключ и секрет вебхука это разные строки. Ключ кладётся в заголовок Authorization. Секрет вебхука наружу в запросах не отправляется, он живёт только у вас и у платформы.Вам также называют код мерчанта. В ответах API он приходит как merchantId. Сами вы его в запросы не подставляете: платформа узнаёт мерчанта по ключу.2. Как устроен каждый запрос#
Для загрузки файла заголовок Content-Type другой, это написано в шаге про чек.Ответ с ошибкой всегда выглядит так:{
"code": "VALIDATION_ERROR",
"message": "validation failed",
"errors": {
"amount": ["must be a positive decimal"]
}
}
Поле errors есть только когда не прошли конкретные поля.Суммы пишите строкой: "1500", "1500.50". Не числом 1500. Дробная часть через точку.Валюта из трёх букв. Платформа сама сделает её заглавной, rub и RUB это одно и то же.3. Два вида платежа#
in это приём. Клиент платит вам. Вы показываете ему реквизиты или страницу оплаты. Когда деньги дошли, вам приходит вебхук со статусом оплаты.out это выплата. Вы отправля ете деньги клиенту. Перед этим платформа держит нужную сумму USDT на балансе мерчанта. Если USDT не хватает, выплата не начнётся или баланс уйдёт в минус (зависит от настроек).4. Приём денег, шаг за шагом#
Шаг 1. Генерация merchantTransactionId#
merchantTransactionId это любая уникальная строка, по которой вам будет удобно идентифицировать платёж. Один и тот же номер можно отправить в API только как один платёж. Повтор не создаст второй.Шаг 2. Создание платежа#
{
"type": "in",
"amount": "1500",
"currency": "RUB",
"method": "sbp",
"geo": "RU",
"merchantTransactionId": "order-1001",
"clientId": "user-42",
"webhookUrl": "https://shop.example/hooks/solvr",
"returnUrl": "https://shop.example/orders/1001",
"payer": {
"name": "Иван Иванов"
}
}
Обязательные поля: type, amount, currency, method, geo, merchantTransactionId.method это способ оплаты. geo это регион этого способа. Для российских переводов geo равен RU. Список кодов в конце страницы.webhookUrl это адрес, куда платформа будет сообщать о смене статуса. Если поле не передать, используется глобальный адрес, который администратор задал при онбординге. Переданный webhookUrl имеет приоритет над глобальным. Если нет ни того ни другого, уведомлений не будет, статус придётся забирать самим через GET /v1/payments/{id}.returnUrl это куда вернуть человека после страницы оплаты. Можно не передавать.clientId это ваш идентификатор покупателя. Можно не передавать.payer это любые данные о плательщике. Платформа сохранит их как есть и вернёт в ответе.amountUsd можно посчитать самим и передать. Если не передать, платформа посчитает сумму в долларах и курс сама. Курс для мерчанта потом виден в поле merchantRate. В вебхуке курса нет, он есть только в ответе API.ttlSec это сколько секунд живёт платёж. Если не передать, срок берётся из настроек.autoAmountLimit и autoAmountStep нужны редко. Если на точную сумму нет свободного реквизита, платформа может попробовать соседние суммы. Не передавайте эти поля, если вам не сказали иное.Шаг 3. Ответ с кодом 201#
Успех это код 201 и JSON платежа. Важные поля:id сохраните. Дальше все запросы идут по нему.
status в начале обычно process. Это значит, что платёж ещё не оплачен.
requisiteDetails это то, что нужно показать клиенту.
form.url это путь страницы оплаты, например /v1/form/токен. Полный адрес: https://api-app.solvr.online плюс этот путь.
form.token нужен, если страницу запрашиваете отдельно.
expiresAt это момент, после которого неоплаченный платёж станет expired.
merchantRate это курс мерчанта на момент создания.
Пример реквизитов для СБП:{
"phone_number": "79991234567",
"owner_name": "Петр Петров",
"bank_name": "Т-Банк",
"country_name": "РФ"
}
{
"card_number": "2200123412341234",
"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 этот статус может прийти строкой 9. Это то же самое. |
paid | Деньги пришли ровно на сумму заказа. Заказ можно закрывать как оплаченный. |
underpaid | Пришло меньше, чем просили. Смотрите paidAmount. Сами решите, добирать разницу или нет. |
overpaid | Пришло больше. Смотрите paidAmount. |
expired | Время вышло, оплаты нет. Заказ не оплачен. |
cancel | Платёж отменён. |
error | Провести не удалось. Смотрите причину в истории или в вебхуке. |
chargeback | Уже после успеха оплату оспорили. Заказ больше нельзя считать успешно оплаченным. |
Заказ у себя отмечайте оплаченным только на paid. underpaid и overpaid это не полная оплата нужной суммы.Шаг 6. Запрос подтверждения#
Вебхук придёт со статусом confirmation_requested и пр ичиной confirmation_requested. В нём будут поля confirmationKind и иногда confirmationNote.confirmationKind говорит, что прислать:other другой файл, смотрите текст в confirmationNote
file сам файл, обязателен
kind одно из receipt, video, other
note короткая подпись, можно пусто
Если файл уже лежит по ссылке, можно не загружать байты:{
"fileUrl": "https://shop.example/receipts/1001.jpg",
"note": "Чек клиента"
}
Список уже приложенных файлов:После проверки статус снова сменится, и придёт новый вебхук.5. Выплата клиенту#
Шаг 1. Проверка баланса#
{
"currency": "USDT",
"balance": "1000",
"held": "16.04",
"available": "983.96"
}
available это сколько ещё можно отправить новыми выплатами. В большинстве случаев баланс может уходить в минус (зависит от настроек и условий). Если счёта нет, все три числа будут "0", это не ошибка.Другая валюта учёта, если вам её назвали:Шаг 2. Создание выплаты#
Тело такое же, как у приёма, но type равен out. В payer или в договорённых полях передайте реквизиты получателя так, как описано ниже. Способ и регион обязательны.{
"type": "out",
"amount": "1500",
"currency": "RUB",
"method": "sbp",
"geo": "RU",
"merchantTransactionId": "payout-1001",
"webhookUrl": "https://shop.example/hooks/solvr",
"payer": {
"phone_number": "79991234567",
"bank_name": "Т-Банк",
"owner_name": "Иван Иванов"
}
}
Платформа удерживает USDT на балансе мерчанта и только потом ищет, кто отправит рубли.Если USDT не хватает, выплата не начнётся или баланс уйдёт в минус (зависит от настроек).Если получателя некому отправить, ответ 503 и код NO_LIQUIDITY или NO_REQUISITES. Платёж при этом может уже существовать. Повторите тот же запрос с тем же merchantTransactionId: платформа попробует найти исполнителя снова.Шаг 3. Конечный статус#
Для выплаты конечные хорошие статусы те же: paid, underpaid, overpaid. Пока идёт отправка, статус process. Отмена возможна только пока выплата ещё не ушла в конечный статус, см. раздел про отмену.Страницы оплаты у выплаты обычно нет. Поле form в ответе будет отсутствовать.6. Повтор того же номера заказа#
merchantTransactionId это ключ идемпотентности. Он уникален для вашей платформы.Если сеть оборвалась и вы не уверены, создался ли платёж, отправьте тот же JSON ещё раз.Второй платёж не появится.
Ответ снова 201, внутри тот же id.
Если первый раз реквизит не нашёлся, повтор попробует подобрать его снова.
Если платёж уже в ошибке, повтор вернёт его как есть и сам ошибку не исправит.
Поэтому номер заказа нельзя переиспользовать для новой оплаты. Новый заказ это новый merchantTransactionId.7. Отмена#
Отменить можно платёж в статусе process или confirmation_requested.Тело не нужно. Успех это 200 и платёж со статусом cancel.Если платёж уже оплачен, истёк или в ошибке, придёт 409 и код INVALID_STATE. Такой платёж отменой не вернуть. Для спора после оплаты используйте апелляцию.8. Как узнать, что было с платежом#
Фильтр status принимает: process, paid, underpaid, overpaid, expired, cancel, chargeback, error.Каждая запись содержит fromStatus, toStatus, reason и createdAt. reason это короткая машинная фраза, та же, что в вебхуке.Чужой id даст 403 с кодом FORBIDDEN. Несуществующий даст 404 с кодом NOT_FOUND.9. Спор#
Спор нужен, когда вы не согласны с итогом. Например клиент показывает чек, а статус так и остался process, или сумма зачислена не та.{
"paymentId": "6f1c2a40-1b2e-4c3d-9a8b-0e7d6c5b4a39",
"reason": "Клиент оплатил, статус остался process",
"evidence": {
"receiptUrl": "https://shop.example/receipts/1001.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 | На выплату не хватило USDT. Пополните баланс. |
| 503 | NO_LIQUIDITY | Сейчас некого назначить. Повторите тот же запрос позже. |
| 503 | NO_REQUISITES | Нет подходящих реквизитов. Повторите позже или смените метод. |
| 503 | SERVICE_UNAVAILABLE | Часть платформы недоступна. Повторите тот же запрос. |
| 500 | INTERNAL | Сбой. Повторите тот же merchantTransactionId, не создавайте новый номер, пока не проверите GET. |
Повторять безопасно именно создание с тем же merchantTransactionId. Новый номер заводите только когда уверены, что старый платёж вам больше не нужен.11. Коды способов для России#
geo для всех этих кодов: RU. Валюта приёма: RUB.Телефон и СБП: sbp, phone_transfer, tbank, sberbank, alfa, vtb, gazprombank, psb, sovcombank, raiffeisen, rshb, mts_bank, ozon_bank, yandex_bank, wb_bank, otkritie, pochta, rosbank, mkb, tochka, modulbank, akbars, uralsib, domrf, spb, sinara.Карта: c2c, card, card_transfer.Платформа примет и другой код, если он заведён у вас в настройках. Если по коду нет живых реквизитов, создание вернёт NO_REQUISITES или NO_LIQUIDITY.12. Минимальный порядок запуска#
1.
Получить sk_ и секрет вебхука.
2.
Поднять у себя POST на webhookUrl, который проверяет подпись и отвечает 200. Подробности в vebhuki.md. 3.
Создать тестовый приём на маленькую сумму.
4.
Убедиться, что в ответе есть id, requisiteDetails и form.url.
5.
Дождаться вебхука payment.created, затем вебхука со статусом paid или другим конечным.
6.
Сверить GET /v1/payments/{id} с тем, что пришло в вебхуке.
7.
Только после этого включать выплаты. Перед первой выплатой проверить GET /v1/balance.
Modified at 2026-10-23 12:16:27