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-Key | mk_… | Авторизация server-to-server при создании платежа. |
| Webhook secret | whsec_… | Получение: GET /api/merchants/:id/webhook-secret. Ротация: POST …/rotate. Оба endpoint защищены сессией. |
| vp_session | HttpOnly 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"
}'| amount | number, ≥ 1 | Обязательная сумма в валюте ордера. |
| currency | RUB | Текущий рабочий фиатный контур. |
| method | card | sbp | c2c | Метод выдачи реквизита. |
| traffic_type | classic | bt | mk | Тип трафика; по умолчанию classic. |
| direction | pay_in | pay_out | Направление; по умолчанию pay_in. |
| order_id | string | Ваш уникальный идентификатор для сверки и идемпотентной бизнес-логики. |
| callback_url | https URL | URL уведомления конкретного ордера; иначе используется 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-статус.
