Быстрый старт
Проведите первый платёж за 5 минут — от получения ключей до приёма webhook
Быстрый старт
Этот гайд проведёт вас от регистрации до первого успешного платежа за 5 минут. Мы создадим PayIn, перенаправим игрока на платёжную страницу и примем webhook о завершении. Готовый код можно скопировать и запустить.
Если вы хотите сначала понять общую картину — начните с обзора API.
Что вам нужно
Перед началом интеграции убедитесь, что у вас есть:
| Ресурс | Описание | Где получить |
|---|---|---|
| API Token | Публичный 64-символьный hex-токен мерчанта. Передаётся в заголовке X-Merchant-Token. | Администратор платформы Syncra выдаёт при заведении мерчанта (разовое показ). |
| Webhook Secret | Приватный секретный ключ (32 байта) для вычисления HMAC-SHA256 подписей. Не передаётся по сети. | Выдаётся вместе с API Token (разовое показ). Храните его в секрете. |
| Callback URL | Публичный HTTPS-адрес вашего сервера для приёма вебхуков. | Ваш сервер (например, https://merchant.example/callbacks/syncra). |
Webhook Secret показывается один раз при выдаче или ротации ключей. Если вы его потеряли — попросите администратора выполнить ротацию. Подробности в разделе Аутентификация.
Шаг 1. Авторизация
Каждый запрос к Syncra API подписывается с помощью HMAC-SHA256. Подпись
вычисляется по строке {timestamp}.{raw_body} и передаётся в заголовке
X-Signature. Это защищает запрос от подделки и повторного воспроизведения.
Ниже — готовые примеры подписи на curl и Node.js. Полная спецификация — в
Аутентификации.
const crypto = require('crypto');
/**
* Строит три заголовка аутентификации для запроса к Syncra API.
* @param {string} body — сырое тело запроса (строка JSON, как уходит по сети).
* @param {string} token — X-Merchant-Token (64-char hex).
* @param {string} secret — Webhook Secret (для HMAC-SHA256).
*/
function authHeaders(body, token, secret) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return {
'X-Merchant-Token': token,
'X-Timestamp': timestamp,
'X-Signature': `t=${timestamp},v1=${signature}`,
'Content-Type': 'application/json',
};
}
module.exports = { authHeaders };# В curl подпись удобнее считать через openssl. Скрипт ниже подписывает
# произвольное тело и отправляет GET-запрос баланса.
BODY='{"amount":150000,"currency":"RUB"}'
SECRET='whsec_your_webhook_secret_here'
TOKEN='mtoken_your_merchant_token_here'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X GET https://api.syncra.money/api/v1/p2p/merchant/balance \
-H "X-Merchant-Token: $TOKEN" \
-H "X-Timestamp: $TS" \
-H "X-Signature: t=$TS,v1=$SIG" \
-H "Content-Type: application/json"Сервер отклоняет запросы, у которых X-Timestamp расходится с серверным
временем более чем на 5 минут. Убедитесь, что системные часы
синхронизированы (NTP).
Шаг 2. Создание PayIn
Чтобы принять платёж от игрока, создайте сделку вызовом POST /deals/payin.
Минимально достаточно пяти полей: сумма, валюта, идемпотентный ключ,
идентификатор игрока и метод оплаты.
POST https://api.syncra.money/api/v1/p2p/merchant/deals/payinМинимальный набор полей
| Поле | Тип | Описание |
|---|---|---|
amount | int64 | Сумма в минимальных единицах валюты (копейках для RUB). 150000 = 1500.00 RUB. |
currency | string | ISO 4217, например "RUB". |
idempotency_key | string | Уникальный ключ запроса (UUID v4). Защищает от дублей при retry. Подробнее → |
client_id | string | Идентификатор игрока в вашей системе (например, player_9921). |
payment_method_id | string | Обязательное поле: UUID (канонический формат) или слаг метода оплаты из GET /payment-methods (Banks → Получение списка методов). Пустое значение отклоняется с 400; получите идентификатор из каталога до первого платежа. |
Пример запроса (curl)
# 1. Сгенерируйте idempotency_key (UUID v4) — он должен быть уникален для каждого платежа.
IDEMPOTENCY_KEY=$(uuidgen | tr 'A-Z' 'a-z')
# 2. Тело запроса в точности так, как оно пойдёт по сети (без лишних пробелов).
BODY='{"amount":150000,"currency":"RUB","idempotency_key":"'"$IDEMPOTENCY_KEY"'","client_id":"player_9921","payment_method_id":"<UUID из шага 1>"}'
# 3. Рассчитайте HMAC-SHA256 подпись.
SECRET='whsec_your_webhook_secret_here'
TOKEN='mtoken_your_merchant_token_here'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
# 4. Отправьте запрос.
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/deals/payin \
-H "X-Merchant-Token: $TOKEN" \
-H "X-Timestamp: $TS" \
-H "X-Signature: t=$TS,v1=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"Пример ответа (200 OK)
{
"deal": {
"deal_id": "7ac148fe-19a3-45bb-b992-019fac55b721",
"status": "ESCROW_LOCKED",
"amount": "150000",
"currency": "RUB",
"payment_url": "https://checkout.syncra.money/pay/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"created_at": "2026-08-03T12:00:00Z"
}
}Поле payment_url — это ссылка на hosted платёжную страницу. Именно на неё
нужно отправить игрока.
В ответе V2 используется поле payment_url. В этом же объекте сделки
(MerchantDealInfo, proto field 9) оно хранится под именем payment_url.
Подробное описание страницы оплаты — в Hosted
Checkout.
Шаг 3. Редирект игрока
Возьмите payment_url из ответа и перенаправьте туда игрока. Это hosted
страница на домене checkout.syncra.money/pay/{hash} — её брендируют под
вашего мерчанта
(цвета, логотип, название). Вам не нужно рисовать форму оплаты самостоятельно.
// Браузерный редирект после создания PayIn на бэкенде.
const response = await fetch('/your-backend/create-payin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ playerId: 'player_9921', amount: 150000 }),
});
const { payment_url } = await response.json();
// Отправляем игрока на платёжную страницу Syncra.
window.location = payment_url;app.post('/api/deposit', async (req, res) => {
const deal = await syncra.createPayIn({
amount: req.body.amount,
currency: 'RUB',
idempotency_key: crypto.randomUUID(),
client_id: req.user.id,
});
// Возвращаем URL фронту — пусть редиректит игрока.
res.json({ payment_url: deal.payment_url });
});<!-- Если хотите редиректнуть сразу с бэкенда -->
<meta http-equiv="refresh" content="0; url={{payment_url}}" />Что увидит игрок на странице оплаты:
- Реквизиты трейдера — номер карты / телефона, ФИО держателя.
- Сумму и таймер — сколько перевести и сколько времени осталось.
- Пошаговые инструкции — как совершить перевод в приложении банка.
- Кнопку «Я оплатил» — после нажатия запускается проверка поступления.
Полный разбор экранов — в Hosted Checkout.
Шаг 4. Приём webhook
Когда платёж подтверждается, Syncra отправляет POST на ваш callback_url
событие deal.completed. Ниже — минимальный сервер на Node.js, который
проверяет подпись и зачисляет депозит.
// webhook.js — запуск: node webhook.js (нужен express: npm i express)
const crypto = require('crypto');
const express = require('express');
const WEBHOOK_SECRET = 'whsec_your_webhook_secret_here';
const app = express();
// ВАЖНО: подпись считается по СЫРОМУ телу. Поэтому перехватываем тело
// до JSON-парсинга и сохраняем как rawBody.
app.use('/callbacks/syncra', express.raw({ type: 'application/json' }));
app.post('/callbacks/syncra', (req, res) => {
const rawBody = req.body.toString('utf8'); // сырое тело как строка
const signatureHeader = req.get('X-Signature'); // t=...,v1=...
// 1. Проверяем подпись V2.
if (!verifySignature(rawBody, signatureHeader, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'invalid signature' });
}
// 2. Парсим тело ТОЛЬКО после успешной проверки.
const event = JSON.parse(rawBody);
// 3. Обрабатываем событие. ВАЖНО: возвращаем 200 как можно быстрее —
// тяжёлую логику (зачисление депозита) выносим в фоновую очередь.
if (event.event === 'deal.completed') {
console.log(`Deposit confirmed: deal=${event.deal_id} amount=${event.amount}`);
// creditPlayer(event.deal_id, event.amount); // ваша бизнес-логика
}
// 4. Возвращаем 200 — иначе Syncra будет ретраить webhook.
res.status(200).json({ status: 'success' });
});
// Проверка HMAC-SHA256 по формату t={ts},v1={hex}
function verifySignature(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
const tsPart = signatureHeader.split(',').find(p => p.startsWith('t='));
const sigPart = signatureHeader.split(',').find(p => p.startsWith('v1='));
if (!tsPart || !sigPart) return false;
const timestamp = tsPart.substring(2);
const provided = sigPart.substring(3);
// Защита от replay: отклоняем запросы старше 5 минут.
const age = Math.abs(Math.floor(Date.now() / 1000) - parseInt(timestamp, 10));
if (age > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
// Сравнение защищено от timing-атак.
return crypto.timingSafeEqual(
Buffer.from(provided, 'utf8'),
Buffer.from(expected, 'utf8'),
);
}
app.listen(3000, () => console.log('Webhook server on :3000'));Полезные поля webhook deal.completed
{
"event": "deal.completed",
"deal_id": "7ac148fe-19a3-45bb-b992-019fac55b721",
"merchant_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
"amount": 150000,
"currency": "RUB",
"status": "COMPLETED",
"received_amount": 150000,
"amount_match_status": "full",
"timestamp": "2026-08-03T12:04:12Z"
}| Поле | Зачем нужно |
|---|---|
deal_id | Идентификатор сделки — по нему вы сопоставляете платёж с вашим заказом. |
amount / received_amount | Сколько должно было прийти / сколько пришло фактически. |
amount_match_status | full / partial / overpaid — сверка сумм; переплата/недоплата отдельными статусами бывают только в числовых кодах V1 (14/15), в V2 — всегда через это поле. |
status | Статус сделки (для deal.completed — COMPLETED; полный справочник — Statuses). |
Всегда делайте обработку идемпотентной. Syncra может прислать один и тот
же webhook повторно (рассинхрон сети, retry). Проверяйте по deal_id, что уже
зачислили этот платёж. Полные правила — в
Callbacks.
Готово
Вы только что провели первый платёж. Схема целиком:
Что дальше
- Hosted Checkout — как настроить брендирование платёжной страницы (цвета, лого, redirect URL).
- Идемпотентность — как работают ключи и почему они обязательны для платежей.
- Приём платежей (PayIn) — полный список полей, статусов и методов оплаты.
- Callbacks — все типы событий и политика ретраев.
- Аутентификация — детали HMAC-SHA256 на 4 языках.