Voyage Pay / Merchant API

Интеграция без догадок

Контракт Voyage Pay: создание P2P-ордера, безопасный публичный ответ, статусы и подписанные webhook-доставки с повторными попытками.

SECTION / 01

Быстрый старт

Минимальный путь от кабинета до первого ордера.

01

Получите доступ

Овнер Voyage Pay создаёт аккаунт мерчанта. Войдите в Merchant workspace.
02

Заберите API key

Ключ формата mk_ используется только сервером мерчанта. Не размещайте его во frontend-коде.
03

Настройте URL

Укажите webhook, success и fail URL в разделе «Интеграция». Только публичный HTTPS.
04

Создайте ордер

Отправьте запрос с уникальным order_id и перенаправьте клиента на /pay/{id} вашего Voyage Pay frontend.

Payment URL

После создания откройте <FRONTEND_URL>/pay/<payment.id>. API URL и адрес клиентской платёжной страницы — разные значения.
SECTION / 02

Ключи и доступ

X-Api-Keymk_…Авторизация server-to-server при создании платежа.
Webhook secretwhsec_…Получение: GET /api/merchants/:id/webhook-secret. Ротация: POST …/rotate. Оба endpoint защищены сессией.
vp_sessionHttpOnly cookieТолько для браузерного кабинета; не используется в интеграции магазина.

Разделение секретов

API key и webhook secret выполняют разные задачи. Первый разрешает создать ордер, второй доказывает, что уведомление отправил Voyage Pay.
SECTION / 03

Создание ордера

POST /api/payments/create

curl -X POST https://voyagepay.pro/api/payments/create \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: mk_ВАШ_API_KEY" \
  -d '{
    "amount": 10000,
    "currency": "RUB",
    "method": "sbp",
    "traffic_type": "classic",
    "direction": "pay_in",
    "order_id": "order-10027",
    "description": "Оплата заказа 10027",
    "callback_url": "https://merchant.example/webhooks/voyage",
    "success_url": "https://merchant.example/payment/success",
    "fail_url": "https://merchant.example/payment/fail"
  }'
amountnumber, ≥ 1Обязательная сумма в валюте ордера.
currencyRUBТекущий рабочий фиатный контур.
methodcard | sbp | c2cМетод выдачи реквизита.
traffic_typeclassic | bt | mkТип трафика; по умолчанию classic.
directionpay_in | pay_outНаправление; по умолчанию pay_in.
order_idstringВаш уникальный идентификатор для сверки и идемпотентной бизнес-логики.
callback_urlhttps URLURL уведомления конкретного ордера; иначе используется URL из настроек мерчанта.
SECTION / 04

Ответ и статусы

{
  "id": "8c998946-93ef-4bcc-b119-1b874ac69c42",
  "order_id": "order-10027",
  "amount": 10000,
  "currency": "RUB",
  "method": "sbp",
  "traffic_type": "classic",
  "direction": "pay_in",
  "status": "pending",
  "description": "Оплата заказа 10027",
  "expires_at": "2026-07-18T12:30:00.000Z",
  "success_url": "https://merchant.example/payment/success",
  "fail_url": "https://merchant.example/payment/fail",
  "requisite": {
    "type": "sbp",
    "bank": "bank-name",
    "owner_name": "Получатель",
    "value": "+7 900 000-00-00",
    "phone": "+7 900 000-00-00"
  }
}
pendingОжидает оплатыОрдер назначен и ещё не подтверждён.
successУспешноФинансовый расчёт проведён; финальный положительный статус.
failedОшибка / спорПлатёж не завершён или переведён в спор.
expiredИстёкВремя оплаты вышло.
cancelledОтменёнОтменён администратором.

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

Публичный безопасный DTO: GET /api/payments/pay/:id или GET /api/payments/:id/public. Полные внутренние данные доступны только авторизованному владельцу платежа.
SECTION / 05

Webhook и подпись

POST /webhooks/voyage
X-Webhook-Id: 4b811e8c-4db7-49a7-a762-662f77a1f88c
X-Webhook-Event: payment.success
X-Webhook-Signature: sha256=<hex_hmac>

{
  "event_id": "4b811e8c-4db7-49a7-a762-662f77a1f88c",
  "event": "payment.success",
  "payment_id": "8c998946-93ef-4bcc-b119-1b874ac69c42",
  "order_id": "order-10027",
  "amount": 10000,
  "currency": "RUB",
  "method": "sbp",
  "traffic_type": "classic",
  "status": "success",
  "fee_amount": 250,
  "tx_hash": null,
  "created_at": "2026-07-18T12:00:00.000Z",
  "timestamp": 1784366400000
}

Подпись считается как HMAC-SHA256 от точного JSON-тела. Сравнивайте подписи constant-time способом и отвечайте любым HTTP 2xx только после надёжной фиксации события.

import crypto from 'node:crypto';

const rawBody = JSON.stringify(requestBody);
const expected = 'sha256=' + crypto
  .createHmac('sha256', process.env.VOYAGE_WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');

const valid = crypto.timingSafeEqual(
  Buffer.from(receivedSignature),
  Buffer.from(expected),
);

Повторы

Voyage Pay сохраняет доставку и повторяет неуспешный webhook с возрастающей задержкой. event_id и X-Webhook-Id постоянны — храните их и не обрабатывайте событие дважды.
SECTION / 06

Чек-лист запуска

01

HTTPS везде

Webhook и redirect URL должны быть публичными HTTPS-адресами без редиректов на внутренние сети.
02

Секреты на сервере

Не отправляйте mk_ и whsec_ в браузер, аналитику, логи или клиентские приложения.
03

Идемпотентность

Дедупликация по event_id; order_id связывает платёж с заказом на вашей стороне.
04

Статус из API

Не считайте success_url доказательством оплаты. Источник истины — подписанный webhook и API-статус.