Venta

Venta · API

Один API — все способы оплаты. Подключение — через оператора: напишите нам, после проверки проекта вы получите пару ключей — vnt_live_pub_… (публичный) и vnt_live_sec_… (секрет подписи, по сети не передаётся). Сначала включается песочница: полный цикл — платёж, статусы, вебхуки — без реальных денег; боевой режим включаем после успешного теста интеграции.

Подпись запросов

Каждый запрос подписывается HMAC-SHA256 от канонической строки: поля запроса (кроме signature и пустых) сортируются по имени ключа, склеиваются как key=value через &.

const crypto = require('crypto')
function sign(params, secret) {
    const s = Object.keys(params)
        .filter(k => k !== 'signature' && params[k] !== undefined && params[k] !== null && params[k] !== '')
        .sort()
        .map(k => `${k}=${typeof params[k] === 'object' ? JSON.stringify(params[k]) : params[k]}`)
        .join('&')
    return crypto.createHmac('sha256', secret).update(s).digest('hex')
}

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

POST https://ventapay.click/api/v1/payment/create
Content-Type: application/json

{
  "api_key": "vnt_live_pub_…",
  "order_id": "order-123",          // ОБЯЗАТЕЛЕН, до 64 символов; повтор вернёт тот же платёж
  "amount": 299.00,                 // ₽, здесь — В РУБЛЯХ
  "method": "sbp",                  // sbp | card | crypto
  "description": "Подписка на месяц",
  "customer": "tg:123456789",       // опционально
  "signature": "…"                  // подпись всех полей выше
}

Ответ: { payment_id, status, amount, payment_url, provider, is_test } — покупателя редиректите на payment_url.

Почему order_id обязателен: без него один и тот же подписанный запрос можно переслать повторно и получить сколько угодно платежей. С ним повтор возвращает duplicate: true и тот же payment_id — это защита и от повторов, и от двойного списания при ретраях вашей стороны.

Лимиты суммы: от 10 до 300000 ₽. Частота: до 60 запросов в минуту с адреса.

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

GET https://ventapay.click/api/v1/payment/{payment_id}?api_key=vnt_live_pub_…&signature=…

Подписываются параметры api_key и id (id — из пути). Статусы: created → pending → paid | failed | expired, после возврата — refunded.

Возврат

POST https://ventapay.click/api/v1/payment/{payment_id}/refund
{
  "api_key": "vnt_live_pub_…",
  "amount": 100.00,                 // ₽; без поля возвращается весь остаток
  "reason": "покупатель отказался",
  "external_id": "your-refund-1",   // опционально, ваш ключ идемпотентности
  "signature": "…"                  // подписываются поля выше + id из пути
}

Ответ: { refund_id, refunded_total, status, refund_status, debt_added }. refund_status — состояние самого возврата: pending означает, что сумма уже удержана с вашего баланса, а покупателю уйдёт после обработки оператором; done — деньги покупателю отправлены. Если возврат не удастся, придёт вебхук payment.refund_failed, удержание снимется и платёж снова можно будет вернуть.

Комиссия платформы при возврате не возвращается: покупателю уходит вся сумма платежа, а получали вы её за вычетом комиссии — разница остаётся на вас. Если доступного остатка не хватило, разница попадает в debt_added и гасится из следующих поступлений.

Повтор того же запроса безопасен. Одинаковые байты считаются одним и тем же запросом: ретрай по таймауту вернёт тот же refund_id, а не спишет с вас второй раз. Чтобы провести ДВА одинаковых возврата намеренно, передайте разные external_id — поле входит в подпись, и запросы перестанут быть одинаковыми.

Вебхук об оплате

POST на ваш Webhook URL при каждой смене статуса:

{
  "event": "payment.paid",
  "payment_id": 42,
  "order_id": "order-123",
  "amount": 29900,                  // ЗДЕСЬ — В КОПЕЙКАХ
  "currency": "RUB",
  "status": "paid",
  "method": "sbp",
  "provider": "…",
  "is_test": false,
  "refunded": 0,                    // сколько всего возвращено по платежу
  "paid_at": "2026-08-03T12:00:00.000Z",
  "signature": "…"
}
Обязательно: проверяйте подпись тем же алгоритмом (секрет — ваш vnt_live_sec_…) и сравнивайте amount с суммой заказа. Отвечайте HTTP 200 — иначе повторим доставку по расписанию 1 мин → 5 мин → 15 мин → 1 ч → 3 ч → 6 ч.

Зачисляйте заказ только по event: "payment.paid". У возврата своё событие payment.refunded с полями refund_id и refund_amount; при частичном возврате status остаётся paid, а amount — исходной суммой платежа, потому что платёж не изменился. Сколько денег вернулось, показывает refunded.

Песочница

До включения боевого режима payment_url ведёт на тестовую страницу с кнопками «Оплатить»/«Отклонить» — прогоните полный цикл, включая вебхук, без реальных денег. Тестовые платежи помечены is_test: true и в статистику не попадают.

Выплаты

Баланс и заявки на вывод — в кабинете по персональной ссылке /cabinet?t=…, которую выдаёт оператор. Выплата уходит только на реквизиты, сохранённые оператором при проверке — сменить их через API нельзя. Минимальная сумма — 100 ₽.

Выплаты по API

Чтение — обычной подписью vnt_live_sec_…, эти ручки денег не двигают:

GET https://ventapay.click/api/v1/balance?api_key=vnt_live_pub_…&signature=…
→ { available_rub, debt_rub, held_rub, in_reserve_rub, min_payout_rub,
    payout_requisites, payout_api_enabled }

GET https://ventapay.click/api/v1/payouts?api_key=vnt_live_pub_…&signature=…

Создание заявки на вывод подписывается отдельным ключом vnt_out_…, а не vnt_live_sec_…:

POST https://ventapay.click/api/v1/payout
{ "api_key": "vnt_live_pub_…", "amount": 500, "signature": "<подпись ключом vnt_out_…>" }
Почему отдельный ключ. vnt_live_sec_… лежит в коде вашей интеграции и участвует в каждом запросе о платежах — его утечка не должна открывать доступ к деньгам. Ключ vnt_out_… выдаёт оператор по запросу, по умолчанию его нет, и отозвать его можно, не трогая рабочую интеграцию. Деньги в любом случае уходят только на реквизиты, сохранённые оператором.

Три разных ключа — и это важно

КлючДля чегоГде живёт
vnt_live_pub_… + vnt_live_sec_…создание платежей, возвраты, чтение баланса, проверка подписи вебхукав коде вашего сервиса
vnt_out_…только заявки на вывод по APIв коде бота, если выплаты автоматизированы
vnt_cab_… (токен кабинета)весь кабинет: настройки, ключи, выплатытолько у владельца, не в коде

Разделение сделано намеренно: pk/sk участвуют в каждой операции и рискуют утечь вместе с исходниками или логами. Утечка этих ключей позволит создавать платежи, но не даст вывести деньги — для вывода нужен отдельный токен кабинета. При любом подозрении на утечку сообщите оператору: ключи перевыпускаются мгновенно, старые сразу перестают работать.