Руководство интегратора

API Платежей

Введение в Payments API

Для новых интеграций используйте актуальные группы API из Swagger v2. Источник правды по полям:

Swagger: sbp-qr · b-gate · e-gate · khm-payouts. MD по QR СБП: /api/docs/integrator/sbp-qr.md.

B-gate (B_GATE): приём СБП RUB→USDT на баланс торговой точки — эндпоинты /api/v2/b-gate/... (KYC, quote, create, status). Лимиты: GET /api/v2/b-gate/limits. Подробности — вкладка «B-gate».

E-gate (E_GATE): энергия TRON, авто-энергия и AML — /api/v2/e-gate/..., списание с баланса торговой точки в USDT (себестоимость + fee). Подробности — вкладка «E-gate».

Оплата по QR СБП: оплата уже существующего QR через /api/v2/sbp-qr/.... Подробности — вкладка «Оплата по QR СБП».

  • B-gate (B_GATE): KYC — POST /api/v2/b-gate/kyc; quote — POST /api/v2/b-gate/quote; платёж — POST /api/v2/b-gate/payments; статус — GET /api/v2/b-gate/payments/{payment_id}.
  • E-gate (E_GATE): quote — POST /api/v2/e-gate/energy/quote; заказ — POST /api/v2/e-gate/energy/orders (+ X-Idempotency-Key); AML — POST /api/v2/e-gate/aml/check; авто-энергия — /api/v2/e-gate/auto-energy/addresses.
  • Оплата по QR СБП: POST /api/v2/sbp-qr/payments (+ confirm/amount при необходимости); статус — webhook или GET /api/v2/sbp-qr/payments/{payment_id}.
  • Выплаты (KHM): POST /api/v2/payouts/khm — вкладка «Выплаты (KHM)».

Какой метод у торговой точки

GET /api/v2/payments/methods?merchant_site_id=<UUID>

Требуется X-API-Key (readonly или full_access). В ответе — code, is_enabled, комиссии и лимиты точки.

curl -sS "https://api.v-dar.ru/api/v2/payments/methods?merchant_site_id=YOUR_SITE_UUID" \
  -H "X-API-Key: YOUR_KEY"

B-gate — СБП RUB→USDT

Код метода на точке: B_GATE. Аутентификация: X-API-Key (full_access). Swagger: b-gate.

Обязательные данные плательщика
  • email — строго обязателен для верификации.
  • телефон (+7…) — обязателен: без него банк не сформирует QR СБП. Оплата QR должна идти с того же номера, что передан при создании; иначе ошибка, списания не будет.
  • Паспорт / ФИО собирает виджет верификации по kyc_url (не передавать в API мерчанта).
UX: открытие kyc_url (особенно iOS)

Открывайте ссылку верификации в новой вкладке (target="_blank", rel="noopener"). Не используйте iframe, модалку-фрейм и «тихий» pop-up без жеста пользователя — на многих iPhone Safari блокирует всплывающие окна. Исходную страницу оставьте открытой и поллите GET /clients/{payer_ref} или кнопку «Проверить статус». Ссылка kyc_url одноразовая.

Рекомендуемый User Flow
  1. Соберите email и телефон плательщика в своём приложении.
  2. Инициация: кнопка «Оплатить через СБП» → при отсутствии верификации POST /api/v2/b-gate/kyc → открыть kyc_url в новой вкладке.
  3. Виджет верификации (SumSub): документы и селфи; экран мерчанта ждёт статус.
  4. После is_verified_for_sbp=true → POST /api/v2/b-gate/payments с тем же телефоном → показать QR → оплата в банке с этого номера.
Hosted checkout (рекомендуется для витрины / бота)
  1. POST /api/v2/b-gate/checkout-sessions — body: amount_rub, опционально payer_ref, description, callback_url, merchant_site_id.
  2. Ответ: id, auth_url (= https://api.v-dar.ru/b_gate/auth/{hash}), expires_at.
  3. Клиент открывает auth_url: телефон + email → при необходимости KYC-виджет (новая вкладка) → QR СБП.
  4. Мерчант опрашивает GET /api/v2/b-gate/checkout-sessions/{id} или принимает webhook на callback_url / merchant.callback_url при PAID/FAILED.
Server-to-server (без hosted page)
  1. GET /api/v2/b-gate/limits — лимиты канала.
  2. POST /api/v2/b-gate/kyc — email и phone обязательны; в ответе kyc_url.
  3. GET /api/v2/b-gate/clients/{payer_ref} — is_verified_for_sbp.
  4. POST /api/v2/b-gate/quote → POST /api/v2/b-gate/payments (тот же phone) → GET /api/v2/b-gate/payments/{id}.

fee_percent в GET /api/v2/payments/methods?merchant_site_id=… = сумма комиссий точки (агенты + V-DAR), совпадает с quote merchant_fee_percent.

cURL: checkout-session
curl -sS -X POST "https://api.v-dar.ru/api/v2/b-gate/checkout-sessions" \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"amount_rub":"1000","description":"demo"}'

# статус сессии
curl -sS "https://api.v-dar.ru/api/v2/b-gate/checkout-sessions/SESSION_UUID" \
  -H "X-API-Key: YOUR_KEY"
Замечания: email и телефон обязательны; оплата QR только с переданного телефона; лимит неоплаченных QR на стороне канала; DEV-ключи канала не работают на PROD-хосте провайдера. Зачисление USDT — только при статусе captured (не при authorized).

E-gate — энергия TRON, авто-энергия, AML

Код канала: E_GATE. Аутентификация: X-API-Key (full_access). Swagger: e-gate.

Списание с баланса торговой точки в USDT = себестоимость (TRX→USDT) + fee. Для мутирующих запросов обязателен заголовок X-Idempotency-Key.

Энергия
  1. POST /api/v2/e-gate/energy/quote — расчёт price_usdt без списания.
  2. POST /api/v2/e-gate/energy/orders — аренда (списывает USDT, делегирует энергию).
  3. GET /api/v2/e-gate/energy/orders/{id} — статус.
  4. GET /api/v2/e-gate/energy/rentals?merchant_site_id=… / DELETE …/rentals/{id} — многочасовые аренды.
Авто-энергия
  1. POST /api/v2/e-gate/auto-energy/addresses — подключить адрес (connect fee).
  2. GET /api/v2/e-gate/auto-energy/addresses?merchant_site_id=… — список.
  3. DELETE /api/v2/e-gate/auto-energy/addresses/{id}?merchant_site_id=… — отключить.

Суточные и per-transfer начисления списываются автоматически с баланса точки.

AML
  1. POST /api/v2/e-gate/aml/check — проверка адреса (фикс. цена из прайса). В result — полный отчёт: risk, score, sources, behavior, flags, connections и т.д.
  2. GET /api/v2/e-gate/aml/checks?merchant_site_id=… — история (в каждом item тот же полный result).
  3. GET /api/v2/e-gate/aml/checks/{order_id} — одна проверка по id заказа.
cURL: quote + order
curl -sS -X POST "https://api.v-dar.ru/api/v2/e-gate/energy/quote" \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"merchant_site_id":"YOUR_SITE_UUID","address":"T…","quantity":65000,"hours":1}'

curl -sS -X POST "https://api.v-dar.ru/api/v2/e-gate/energy/orders" \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -d '{"merchant_site_id":"YOUR_SITE_UUID","address":"T…","quantity":65000,"hours":1}'

Примеры: create + status (СБП без авторизации)

Замените YOUR_KEY, YOUR_SITE_UUID, PAYMENT_ID.

# create
curl -sS -X POST "https://api.v-dar.ru/api/v2/payments/create" \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"merchant_site_id":"YOUR_SITE_UUID","operation_id":"order-'"$(date +%s)"'","amount":1100,"description":"11 RUB","sbp_redirectURL":"https://example.com/ok"}'

# status
curl -sS "https://api.v-dar.ru/api/v2/payments/PAYMENT_ID/status" \
  -H "X-API-Key: YOUR_KEY"
import requests, uuid
B = "https://api.v-dar.ru/api/v2/payments"
H = {"X-API-Key": "YOUR_KEY", "Content-Type": "application/json"}
r = requests.post(f"{B}/create", headers=H, json={
    "merchant_site_id": "YOUR_SITE_UUID",
    "operation_id": f"ord-{uuid.uuid4().hex[:12]}",
    "amount": 1100,
    "description": "11 RUB",
    "sbp_redirectURL": "https://example.com/ok",
}, timeout=60)
r.raise_for_status()
data = r.json()
s = requests.get(f"{B}/{data['payment_id']}/status", headers={"X-API-Key": "YOUR_KEY"}, timeout=60)
print(s.json())
const base = "https://api.v-dar.ru/api/v2/payments";
const headers = { "X-API-Key": "YOUR_KEY", "Content-Type": "application/json" };
const create = await fetch(`${base}/create`, {
  method: "POST", headers,
  body: JSON.stringify({
    merchant_site_id: "YOUR_SITE_UUID",
    operation_id: "ord-" + crypto.randomUUID(),
    amount: 1100,
    description: "11 RUB",
    sbp_redirectURL: "https://example.com/ok"
  })
});
const { payment_id, payment_url } = await create.json();
const st = await fetch(`${base}/${payment_id}/status`, { headers: { "X-API-Key": "YOUR_KEY" } });
console.log(await st.json());
using System.Net.Http.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "YOUR_KEY");
var body = new {
    merchant_site_id = "YOUR_SITE_UUID",
    operation_id = "ord-" + Guid.NewGuid().ToString("N")[..12],
    amount = 1100,
    description = "11 RUB",
    sbp_redirectURL = "https://example.com/ok"
};
var res = await client.PostAsJsonAsync("https://api.v-dar.ru/api/v2/payments/create", body);
res.EnsureSuccessStatusCode();
var created = await res.Content.ReadFromJsonAsync<System.Text.Json.JsonElement>();
var pid = created.GetProperty("payment_id").GetString();
var st = await client.GetAsync($"https://api.v-dar.ru/api/v2/payments/{pid}/status");
Console.WriteLine(await st.Content.ReadAsStringAsync());
$key = "YOUR_KEY";
$payload = json_encode([
  "merchant_site_id" => "YOUR_SITE_UUID",
  "operation_id" => "ord-" . bin2hex(random_bytes(6)),
  "amount" => 1100,
  "description" => "11 RUB",
  "sbp_redirectURL" => "https://example.com/ok",
]);
$ch = curl_init("https://api.v-dar.ru/api/v2/payments/create");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["X-API-Key: $key", "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => $payload,
  CURLOPT_RETURNTRANSFER => true,
]);
$out = curl_exec($ch);
curl_close($ch);
$data = json_decode($out, true);
$ch2 = curl_init("https://api.v-dar.ru/api/v2/payments/{$data['payment_id']}/status");
curl_setopt_array($ch2, [
  CURLOPT_HTTPHEADER => ["X-API-Key: $key"],
  CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($ch2);

Идемпотентность: необязательное поле idempotency_key (до 128 символов) — повторный POST с тем же ключом возвращает тот же платёж без повторного создания.

Проверка статуса

GET /api/v2/payments/{payment_id}/status

Опрос каждые 3–5 с до финального статуса: PAID_VERIFIED, FAILED_REJECTED, FAILED_VERIFICATION, REFUNDED, PARTIALLY_REFUNDED, EXPIRED (неоплачен), либо устаревшие PAID / FAILED.

Запрос актуализирует статус платежа по payment_id.

Пример ответа (успех)
{
  "payment_id": "789e4567-e89b-12d3-a456-426614174000",
  "status": "PAID_VERIFIED",
  "operation_id": "ORDER-998877",
  "amount": 150000,
  "created_at": "2025-01-25T12:30:00Z",
  "paid_at": "2025-01-25T12:32:15Z",
  "name_verification_status": "VERIFIED",
  "payment_url": "...",
  "qrc_id": "..."
}

Поступление на баланс торговой точки

После PAID_VERIFIED проверьте баланс через API v1 (тот же X-API-Key):

curl -sS "https://api.v-dar.ru/api/v1/merchants/balance/YOUR_SITE_UUID" \
  -H "X-API-Key: YOUR_KEY"

История операций: GET /api/v1/operations/ — см. руководство по кодам и операциям.

Оплата по QR СБП

Оплата существующего QR СБП с баланса торговой точки (USDT).

Контракт: /api/v2/sbp-qr. MD для AI-агента · Swagger #/sbp-qr. Auth: X-API-Key с правом full_access.
1. Что делает API

Мерчант передаёт payload QR → V-DAR исполняет оплату → списывает USDT с баланса точки → сообщает результат webhook’ом. Сущности: payment, merchant site, balance, deposit, webhook, error, dispute.

2. Quick Start
  1. A. QR с суммой + confirm:true → processing.
  2. B. QR с суммой + confirm:false → requires_confirmation → POST …/confirm.
  3. C. QR без суммы → amount_required → POST …/amount (сумма и исполнение; /confirm не нужен).
  4. Читайте payment.total_charge. Ждите webhook или poll 3–5 с. Возможен later charged_back.
curl -sS -X POST "https://api.v-dar.ru/api/v2/sbp-qr/payments" \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{
    "merchant_site_id": "YOUR_SITE_ID",
    "qr_payload": "https://qr.nspk.ru/...",
    "idempotency_key": "order-123-try-1",
    "external_id": "order-123",
    "confirm": true
  }'
{
  "payment": {
    "payment_id": "8be0c1a2-…",
    "external_id": "order-123",
    "status": "processing",
    "required_action": null,
    "fiat_amount": "1250.00",
    "fiat_currency": "RUB",
    "charge": {"amount":"13.15","fee":"0.26","total":"13.41","max_total":"13.41","currency":"USDT"},
    "total_charge": "13.41",
    "created_at": "2026-10-28T12:00:00+00:00",
    "completed_at": null,
    "failure": null
  }
}
3. Payment lifecycle
resolving_amount
   +--> amount_required --> POST /amount  (сумма + исполнение)
   +--> requires_confirmation --> POST /confirm  (только QR с суммой + confirm=false)
   v
processing --> succeeded | failed | cancelled
succeeded --> charged_back   (dispute ИЛИ auto-reversal)
statusДействиеФинальный?
resolving_amountЖдатьнет
amount_requiredPOST …/amountнет
requires_confirmationPOST …/confirmнет
processingWebhook / poll ≤1/3–5снет
succeededГотово; возможен later charged_backда*
failed / cancelledfailureда
charged_backПолный возврат списания+комиссиида

* Публичный статус created не возвращается. succeeded может перейти в charged_back.

4–6. Три сценария confirm / amount
  • A. QR с суммой, confirm=true → processing.
  • B. QR с суммой, confirm=false → requires_confirmation → POST …/confirm.
  • C. QR без суммы → amount_required → POST …/amount одновременно задаёт сумму и подтверждает исполнение; доп. /confirm не нужен.
  • Лимиты только из GET /limits. Вне диапазона — AMOUNT_OUT_OF_RANGE.
7. Проверка статуса

GET /api/v2/sbp-qr/payments/{payment_id} — fallback к webhook, сверка после timeout. Не агрессивный polling.

8. Webhooks
  • Основной канал. POST JSON; успех — любой 2xx; timeout доставки по умолчанию 10 с.
  • События: payment.status_changed, payment.dispute_updated, deposit.credited, deposit.rejected. События deposit.returned нет.
  • Поля: callback_id, event_type, timestamp, payment/dispute, sign.
  • Не exactly-once, порядок не гарантирован. Дедуп по callback_id.
  • Retry: 30s → 2m → 10m → … до ~24 ч.
  • X-Vdar-Webhook-Secret + HMAC-SHA256 hex sign (см. MD).
9. Idempotency
  • idempotency_key + то же тело → тот же payment; иное тело → 409 IDEMPOTENCY_KEY_REUSED.
  • external_id — бизнес-ID заказа (уникален на точке); повтор → 409 DUPLICATE_EXTERNAL_ID (+ details.payment_id).
  • После timeout на POST — повторите тот же key, не создавайте новый.
10. Duplicate protection
  • Ключ: merchant_site_id + qr_payload, окно 60 с. Сумма не влияет.
  • Активная/succeeded в окне → обычно тот же payment; failed/cancelled → 409 DUPLICATE_QR (details.payment_id).
  • После 60+ с — новый платёж с новым idempotency_key.
11. Money / fee / total charge

Все деньги — decimal string с 2 знаками. Fiat RUB; списание USDT. Итог: total_charge (= charge.total). Не считайте по курсу/формулам вручную. charge.fee — информативно; max_total — потолок оценки/резерва.

12. Balance and deposits
GET /api/v2/sbp-qr/balance?merchant_site_id=...
GET /api/v2/sbp-qr/deposit-address?merchant_site_id=...&network=TRC20
GET /api/v2/sbp-qr/deposits?merchant_site_id=...
  • USDT; сети TRC20, BEP20, SOL, ERC20, TON. Автозачисление сейчас: TRC20 и BEP20 (+ webhook deposit.credited). SOL/ERC20/TON — позже. active/temporary; min_amount в ответе.
  • Rejected → баланс не растёт. Возврат может быть ручным/отложенным; срок не гарантирован. Смотрите статус в /deposits; не шлите повтор, пока статус неясен.
13. Errors
{"error":{"code":"RATE_LIMIT_EXCEEDED","message":"Rate limit exceeded","retryable":true,"details":{"retry_after_seconds":60}}}
HTTPcoderetry
400VALIDATION_ERRORнет
401UNAUTHORIZEDнет
403FORBIDDEN / METHOD_DISABLEDнет / да
404PAYMENT_NOT_FOUND / MERCHANT_SITE_NOT_FOUNDнет
409IDEMPOTENCY_KEY_REUSED / DUPLICATE_* / INVALID_STATEнет / ~60с QR
422INVALID_QR / AMOUNT_OUT_OF_RANGE / INSUFFICIENT_BALANCEнет
429RATE_LIMIT_EXCEEDED + header Retry-Afterда
500/503INTERNAL_ERROR / SERVICE_TEMPORARILY_UNAVAILABLEнет / да
14. Retry policy

Не ретраить: INVALID_QR, AMOUNT_OUT_OF_RANGE, IDEMPOTENCY_KEY_REUSED. Ретраить: SERVICE_TEMPORARILY_UNAVAILABLE, RATE_LIMIT_EXCEEDED, timeout — с тем же idempotency_key.

15. Disputes / chargebacks

charged_back = финансовая отмена ранее успешной операции (dispute или auto-reversal). Возврат на баланс = вся списанная сумма включая комиссию. Webhook: payment.dispute_updated / payment.status_changed.

16. Limits & reconciliation
GET /api/v2/sbp-qr/limits
GET /api/v2/sbp-qr/payments/history?merchant_site_id=...&status=succeeded&limit=50
GET /api/v2/sbp-qr/payments?merchant_site_id=...&external_id=order-123
17. Sandbox / testing

Отдельного публичного sandbox URL нет. Тест — production API с тестовой точкой/балансом по согласованию с поддержкой. Сценарии: success, amount_required, confirm=false, insufficient balance, webhook signature, dispute.

18. FAQ (кратко)
  • Timeout на POST → повторить тот же idempotency_key.
  • Новый key — только новый заказ / сознательный restart после terminal.
  • external_id ≠ idempotency_key.
  • Итог списания — total_charge.
  • Один QR дважды в 60 с нельзя; после окна — можно.
  • succeeded может стать charged_back.
  • Полный FAQ и примеры кода — в MD.

Исходящие выплаты на карту (KHM_PAY)

Для торговой точки с подключённым и включённым методом выплат KHM_PAY доступны эндпоинты группы khm-payouts в Swagger. Аутентификация: заголовок X-API-Key с правом full_access.

Создание выплаты

POST https://api.v-dar.ru/api/v2/payouts/khm (на стенде замените хост).

ПолеОбяз.Описание
merchant_site_idдаUUID торговой точки (метод KHM_PAY должен быть включён для точки).
operation_idдаУникальный идентификатор операции у мерчанта (в рамках мерчанта).
amountдаСумма выплаты получателю в рублях (десятичное, например 100.00).
currency_codeнетПо умолчанию RUB; иначе поддерживается только RUB.
card_noдаНомер карты получателя (PAN).
first_nameдаИмя держателя карты.
last_nameдаФамилия держателя карты.
channelнетКанал провайдера; если не передан — значение по умолчанию на стороне платформы.
idempotency_keyнетДо 128 символов; повтор с тем же ключом возвращает ту же выплату без повторного создания у провайдера.
Проверка баланса до создания: перед резервом средств платформа проверяет, что на балансе торговой точки достаточно средств на сумму выплаты + комиссии. Комиссия считается по процентам, заданным для метода KHM_PAY на этой торговой точке (в т.ч. доля платформы и агентов). Если средств недостаточно — ответ 400, текст «Недостаточно средств на балансе торговой точки», в details — код INSUFFICIENT_BALANCE и поля payout_amount, fee_amount, required_total, available_balance, currency_code.
Статус выплаты

GET https://api.v-dar.ru/api/v2/payouts/khm/{payout_id}

СтатусСмысл
createdСоздана запись (кратковременно до резерва).
awaiting_providerЗарезервировано на Hold, ожидается/выполняется создание у провайдера.
processingПлатёж у провайдера в обработке (ожидает опроса статуса).
succeededВыплата успешно завершена, проводки зафиксированы.
failedВыплата отклонена или ошибка провайдера; резерв возвращён на точку (если применимо).
refundedВозврат по выплате (служебный конечный статус при возврате).

Фоновый опрос нефинальных выплат у провайдера выполняет процесс payment-status-checker на стороне платформы (периодичность задаётся конфигурацией окружения, по умолчанию порядка минут). Для ручной проверки в тесте можно вызывать GET …/payouts/khm/{id} периодически.

Режим demo провайдера (тестовые PAN)

При включённом demo у провайдера выплат поведение по тестовым номерам карт (последовательности известны из документации провайдера):

Номер карты (PAN)Ожидаемое поведение
4111111111100031Успешная выплата сразу.
4111111111100023Успех после ожидания порядка 2 минут (нужен опрос статуса / воркер).
4111111111100072Неуспех сразу.
4111111111100056Неуспех после ожидания порядка 2 минут.
другие допустимые в demoИсход успех/неуспех через случайное время 0–10 минут.

Сумму amount согласуйте с лимитами demo; для регресса фиксируйте одну сумму (например 100.00 ₽) на все кейсы, если провайдер не требует иного.

План регрессионного тестирования через API V-DAR

Перед прогоном подготовьте: тестового мерчанта, merchant_site_id с включённым KHM_PAY, X-API-Key с full_access, достаточный баланс точки (с запасом под комиссию), уникальные operation_id на каждый кейс. База API: https://api.v-dar.ru (или стенд).

  1. Предусловия: GET /api/v2/payments/methods?merchant_site_id=… — в ответе есть активный метод с кодом KHM_PAY. GET /api/v1/merchants/balance/{merchant_site_id} — зафиксировать баланс до тестов.
  2. TC-INSUFFICIENT: Запрос POST /api/v2/payouts/khm с суммой amount, заведомо большей, чем баланс с учётом комиссии. Ожидание: HTTP 400, сообщение о недостатке средств, details.code = INSUFFICIENT_BALANCE, числовые поля в details согласованы с ручным расчётом (выплата + комиссия).
  3. TC-CARD-OK-IMMEDIATE: PAN 4111111111100031, уникальный operation_id. Ожидание: HTTP 200, status в ответе стремится к финальному успеху; GET …/payouts/khm/{payout_id} — succeeded (возможен краткий awaiting_provider/processing). Проверить проводки: повторный GET …/balance/…, при необходимости GET /api/v1/operations/ — суммы резерва/списания согласованы с total_reserved из ответа.
  4. TC-CARD-OK-DELAYED: PAN 4111111111100023. После создания опрашивать GET …/payouts/khm/{id} каждые 15–30 с до 4 мин; ожидание перехода в succeeded около 2 мин ± допуск. Убедиться, что воркер/checker не оставляет выплату вечно в processing.
  5. TC-CARD-FAIL-IMMEDIATE: PAN 4111111111100072. Ожидание: failed, резерв возвращён, last_error присутствует при необходимости; баланс точки восстановлен относительно резерва.
  6. TC-CARD-FAIL-DELAYED: PAN 4111111111100056. Опрос до 4 мин; ожидание failed после задержки ~2 мин.
  7. TC-CARD-RANDOM: Любой другой допустимый PAN в demo. Опрос до 12 мин; зафиксировать фактический финальный статус и время. Повторный прогон может отличаться — норма для random.
  8. TC-DUPLICATE-OP: Повторить POST …/payouts/khm с тем же operation_id. Ожидание: отказ (дубликат операции).
  9. TC-IDEMPOTENCY: Дважды одинаковый запрос с одним idempotency_key и одинаковым телом. Ожидание: второй ответ с duplicate: true, одна выплата в системе.
  10. TC-STATUS-POLL: Для одной «долгой» выплаты сравнить поля external_payment_id, смену статусов и отсутствие расхождения с логами payment-status-checker на стороне платформы (по согласованию с администратором).
  11. Постусловия: сверка баланса точки и выборочно цепочки операций; убедиться, что нет «зависших» сумм на Hold для финальных succeeded/failed.

После того как вы передадите агенту тестовые merchant_site_id и ключ API (только в защищённом канале, не в публичных логах), можно автоматизировать шаги через скрипт или CI; до этого план выполняется вручную по чеклисту выше.

Автоматический черновик прогона: scripts/khmpay_vdar_api_regression.py (--smoke или --full, переменные VDAR_API_BASE, VDAR_X_API_KEY, VDAR_MERCHANT_SITE_ID).

Пример создания (curl)
curl -sS -X POST "https://api.v-dar.ru/api/v2/payouts/khm" \
  -H "X-API-Key: YOUR_FULL_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_site_id": "YOUR_SITE_UUID",
    "operation_id": "TEST-PAYOUT-001",
    "amount": "100.00",
    "currency_code": "RUB",
    "card_no": "4111111111100031",
    "first_name": "TEST",
    "last_name": "USER"
  }'

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

СтатусОписание
CREATEDПлатёж создан, ожидает оплаты
PAID_PENDING_VERIFICATIONОплачен, проверяется ФИО
PAID_VERIFIEDОплачен и проверен, средства зачислены
PAID_MANUAL_REVIEWОплачен, требует ручной проверки (не найден в выписке за 45 минут)
FAILED_REJECTEDОтклонён при обработке платежа (RJCT)
FAILED_VERIFICATIONНе прошёл проверку ФИО (MISMATCH)
REFUNDINGВозврат в процессе
REFUNDEDПолностью возвращён
PARTIALLY_REFUNDEDЧастично возвращён
EXPIREDИстёк срок (45 минут), только для неоплаченных платежей
Старые статусы (для обратной совместимости):
PENDINGЭквивалент PAID_PENDING_VERIFICATION
PAIDЭквивалент PAID_VERIFIED или PAID_MANUAL_REVIEW
FAILEDЭквивалент FAILED_REJECTED или FAILED_VERIFICATION

Коды ошибок API (400 Bad Request)

Код ошибкиОписание
PAYER_NAME_MISMATCH Имя payer_name, переданное в запросе, не совпадает с именем, зашифрованным в payer_token. Возможна попытка подмены данных.
DUPLICATE_OPERATION_ID Платеж с таким operation_id уже существует. Используйте уникальные ID заказов.
AMOUNT_TOO_SMALL Сумма платежа меньше минимально допустимой (минимум 100 копеек = 1.00 RUB).
AMOUNT_TOO_LARGE Сумма платежа больше максимально допустимой (максимум 100000000 копеек = 1,000,000.00 RUB).
PAYMENT_CREATION_ERROR Общая ошибка создания платежа (внутренняя ошибка системы).
INVALID_TOKEN Не удалось расшифровать payer_token. Возможно, он поврежден или устарел.
SITE_NOT_FOUND Указанный merchant_site_id не существует или не принадлежит мерчанту ключа.
METHOD_NOT_CONFIGURED Для торговой точки не настроен активный способ приёма на стороне платформы. Тело POST /create менять не нужно — обратитесь в поддержку V-DAR.
QR_CREATION_FAILED Ошибка на этапе создания платежа; детали — в тексте ответа API. При устойчивой ошибке обратитесь в поддержку V-DAR.
HTTP 422 Ошибка валидации тела запроса (например, отсутствуют обязательные поля для выбранного метода).