Solvr API для мерчанта
    • Начало работы
    • Вебхуки
    • API документация
      • Платежи
        • Создать платёж
        • Список платежей мерчанта
        • Один платёж
        • Отменить платёж
        • История статусов
      • Страница оплаты
        • Страница оплаты по id платежа
        • Страница оплаты по токену
      • Чеки
        • Список чеков и файлов по платежу
        • Приложить чек по ссылке
        • Загрузить файл подтверждения
      • Баланс
        • Баланс мерчанта
      • Споры
        • Открыть спор по платежу
        • Список споров мерчанта
        • Один спор
      • Schemas
        • PaymentMethod
        • PaymentType
        • PaymentStatus
        • CreatePayment
        • Payment
        • FormPage
        • StatusHistory
        • Receipt
        • Appeal
        • Balance
        • Error

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

    Адрес 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:
    {
      "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.
    confirmationKind:
    receipt - чек или скрин
    video - видео оплаты
    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. Коды ошибок#

    HTTPcodeЧто делать
    401UNAUTHORIZEDКлюча нет или он неверный. Проверьте заголовок Authorization: Bearer sk_....
    403MERCHANT_REQUIREDКлюч не привязан к мерчанту. Нужен другой ключ.
    403FORBIDDENЭтот платёж или спор не ваш.
    404NOT_FOUNDНет такого id или токена страницы.
    409IDEMPOTENCY_CONFLICTЗапись с таким ключом уже есть и конфликтует.
    409INVALID_STATEДействие не подходит к текущему статусу. Например отмена уже оплаченного.
    422VALIDATION_ERRORПоправьте поля из errors и отправьте снова.
    422INSUFFICIENT_FUNDSНа выплату не хватило остатка в валюте платежа. Пополните этот счёт и смотрите правило в разделе 3.
    503NO_LIQUIDITYСейчас некого назначить. Повторите тот же запрос позже.
    503NO_REQUISITESНет подходящих реквизитов. Повторите позже или смените метод.
    503SERVICE_UNAVAILABLEЧасть платформы недоступна. Повторите тот же запрос.
    500INTERNALСбой. Повторите тот же merchantTransactionId. Новый номер заводите после того, как проверите платёж через GET.
    Повторять безопасно создание с тем же merchantTransactionId. Новый номер заводите, когда старый платёж вам больше не нужен.

    11. Способы оплаты#

    Для российских способов geo равен RU, валюта приёма - RUB. Полный список кодов, включая банки, карты и QR, лежит в openapi.yaml, схема PaymentMethod.
    Платформа примет и другой код, если он заведён в настройках мерчанта. Если реквизитов по коду нет, создание вернёт NO_REQUISITES или NO_LIQUIDITY.
    Modified at 2026-10-24 01:14:28
    Next
    Вебхуки
    Built with