SyncroPay API
приём и выплаты
REST-интеграция card-to-card в гривне. Все методы — POST с JSON, авторизация по токену, результат — подписанный колбек со status_id.
Приём платежа за 4 шага
1 · Создать заказ
/api/v4/order/create с order_id и суммой.
2 · Показать реквизит
Из ответа — cred_formatted и redirect_url.
3 · Колбек
Ловите подписанный вебхук с status_id.
4 · Зачесть
status_id = 5 → выдайте заказ клиенту.
merch_amount приходят в ответе и колбеке.Учётные данные и базовый URL
Выдаёт менеджер после подключения:
| Параметр | Назначение |
|---|---|
API-токен | Секретный ключ для заголовка x-api-token. Аутентифицирует все запросы. |
webhook_secret | Секрет для проверки подписи X-Signature входящих колбеков (HMAC-SHA256). |
callback_url | Ваш эндпоинт для колбеков (настраивается в аккаунте). |
https://pay.syncropay.tech/apix-api-token и webhook_secret в клиентский код или приложения. Только server-to-server.Авторизация
Токен — в заголовке x-api-token (либо Authorization: Bearer <токен>). Без валидного токена — ошибка 101 (HTTP 401).
x-api-token: sk_live_ваш_секретный_токен
Content-Type: application/jsonPOST /api/v1/health_check → { "status": "ok" } (без авторизации).Формат запросов и ответов
- Все методы — POST, тело и ответ — JSON (UTF-8).
- Успех:
{ "status": "ok", ... }. Ошибка:{ "status": "error", "error_code": N, "error": "..." }. - Суммы — в гривнах (
1500/1500.50). order_id— ваш идентификатор заказа, уникальный. Повтор → ошибка104. Это же и защита от дублей — переиспользуйте тот жеorder_idпри сетевых ретраях.cred_type— тип реквизита; поддерживается только карта =1.
Создать заказ (v4)
Параметры тела
| Поле | Тип | Описание |
|---|---|---|
order_id обяз | string | Ваш ID заказа, уникальный. |
amount обяз | number | Сумма в UAH. |
cred_type опц | number | Тип реквизита, только 1 (карта). По умолчанию 1. |
customer_id опц | string | ID клиента на вашей стороне. |
return_url опц | string | Куда вернуть клиента после оплаты (иначе — наша страница оплаты). |
randomize_enable опц | 0 | 1 | Включить автоподбор незанятой суммы, если базовая занята. |
randomize_direction опц | string | increase (по умолч.) или decrease. |
randomize_stepping опц | number | Шаг подбора, UAH (по умолч. 5). |
randomize_max_steps опц | number | Макс. число шагов (1–50, по умолч. 10). |
randomize_max_final_amount опц | number | Потолок итоговой суммы при increase. |
Запрос
curl -X POST https://pay.syncropay.tech/api/v4/order/create \
-H "x-api-token: sk_live_ваш_токен" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ORDER-10245",
"amount": 1500,
"cred_type": 1,
"return_url": "https://shop.example/success"
}'
const res = await fetch("https://pay.syncropay.tech/api/v4/order/create", {
method: "POST",
headers: {
"x-api-token": process.env.SYNCROPAY_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
order_id: "ORDER-10245", amount: 1500, cred_type: 1,
return_url: "https://shop.example/success",
}),
});
const data = await res.json();
// data.redirect_url → редирект клиента; data.cred_formatted → карта
import os, requests
res = requests.post(
"https://pay.syncropay.tech/api/v4/order/create",
headers={"x-api-token": os.environ["SYNCROPAY_TOKEN"]},
json={
"order_id": "ORDER-10245",
"amount": 1500,
"cred_type": 1,
},
)
data = res.json() # data["redirect_url"], data["cred_formatted"]
Ответ status: ok
{
"status": "ok",
"order_id": "ORDER-10245",
"transaction_id": 100251,
"amount": 1500,
"amount_initial": 1500,
"usdt_amount": 36.14,
"usdt_rate": 41.5,
"status_id": 2,
"bank_name": "Mono",
"receiver_name": "IVAN P.",
"cred": "5375411234567890",
"cred_formatted": "5375 41** **** 7890",
"cred_type": 1,
"redirect_url": "https://pay.syncropay.tech/checkout/pk_9f3c...e1",
"deadline": "2026-07-09T12:20:00.000Z"
}cred_formatted, bank_name, receiver_name) либо редиректить на redirect_url. При randomize_enable=1 итоговая amount может отличаться от запрошенной — списывать нужно ровно amount, исходная — в amount_initial.Создать заказ (v1)
order_id, amount, cred_type; ответ идентичен v4.Используйте v4, если нужен автоподбор суммы (снижает пересечения реквизитов при высоком потоке); иначе достаточно v1.
Статус заказа
{ "order_id": "..." } или { "transaction_id": ... }. Нет → ошибка 404.{
"status": "ok",
"order_data": {
"order_id": "ORDER-10245",
"transaction_id": "100251",
"amount": "1500.00",
"amount_initial": "1500.00",
"commission": "30.00",
"merch_amount": "1470.00",
"usdt_amount": "36.14",
"usdt_rate": "41.5",
"cred": "5375411234567890",
"cred_formatted": "5375 41** **** 7890",
"receiver_name": "IVAN P.",
"receiver_bank": "Mono",
"status_id": "5",
"deadline": "2026-07-09T12:20:00.000Z"
}
}Список заказов
{ "status_id": N } — фильтр по статусу. Возвращает { "status":"ok", "orders":[ ... ] }.Создать выплату
Параметры тела
| Поле | Тип | Описание |
|---|---|---|
order_id обяз | string | Ваш ID выплаты, уникальный. |
amount обяз | number | Сумма в UAH. |
cred обяз | string | Номер карты получателя (проверка по алгоритму Луна). |
cred_type опц | number | Только 1 (карта). |
recipient_name опц | string | ФИО получателя. |
receiver_bank опц | string | Банк получателя. |
curl -X POST https://pay.syncropay.tech/api/v2/payout/create \
-H "x-api-token: sk_live_ваш_токен" \
-H "Content-Type: application/json" \
-d '{
"order_id": "PAYOUT-556",
"amount": 3200,
"cred": "5375411234567890",
"cred_type": 1,
"recipient_name": "Іван Петренко",
"receiver_bank": "ПриватБанк"
}'
const res = await fetch("https://pay.syncropay.tech/api/v2/payout/create", {
method: "POST",
headers: {
"x-api-token": process.env.SYNCROPAY_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
order_id: "PAYOUT-556", amount: 3200,
cred: "5375411234567890", cred_type: 1,
recipient_name: "Іван Петренко",
}),
});
{
"status": "ok",
"payout_id": "100252",
"payout_type": "CARD",
"amount": "3200.00",
"commission": "48.00",
"final_amount": "3248.00",
"cred_formatted": "5375 41** **** 7890",
"currency": { "id": "1", "code": "UAH", "title": "UAH" },
"receiver_country": "UA",
"receiver_bank": "ПриватБанк"
}Статус и список выплат
{ "order_id" } или { "payout_id" }. Нет → ошибка 110. Возвращает payout_status: NEW · PROCESSING · COMPLETED · FAILED · CANCELLED.{ "status": "COMPLETED" }. Возвращает { "payouts":[ ... ] }.Апелляции
Если клиент оплатил, но заказ не подтверждён, мерчант открывает апелляцию с чеком — заявка уходит в спор (status_id = 7), оператор рассматривает.
{ "order_id", "comments", ... }. Открывает спор по заказу.order_id.Жизненный цикл
Типовой путь приёма по status_id. На каждой смене статуса приходит колбек.
status_id = 5 (COMPLETED). Остальные статусы промежуточные и не гарантируют поступление средств.Формат колбека
На каждой смене статуса мы шлём POST на ваш callback_url с JSON-телом и заголовком X-Signature. Отвечайте HTTP 2xx, иначе будут ретраи.
Тело — приём (PayIn)
{
"order_id": "ORDER-10245",
"transaction_id": 100251,
"amount": "1500.00",
"amount_initial": "1500.00",
"commission": "30.00",
"merch_amount": "1470.00",
"status_id": 5,
"cred": "5375411234567890",
"cred_type": "1",
"receiver_name": "IVAN P.",
"receiver_bank": "Mono"
}Тело — выплата (PayOut)
{
"status": "ok",
"payout_id": "100252",
"order_id": "PAYOUT-556",
"type": "CARD",
"amount": "3200.00",
"commission": "48.00",
"final_amount": "3248.00",
"payout_status": "COMPLETED",
"cred_formatted": "5375 41** **** 7890",
"currency": { "id": "1", "code": "UAH", "title": "UAH" },
"country_iso": "UA"
}Доставка и ретраи
- До 5 попыток с интервалами 0, 1, 5, 15, 60 минут, пока не получим 2xx.
- Обработчик должен быть идемпотентным: дедуп по
(order_id, status_id)— один и тот же статус может прийти повторно. - Всегда проверяйте
X-Signatureперед обработкой.
Проверка подписи
Заголовок X-Signature — это HMAC-SHA256(webhook_secret, тело_запроса) в hex. Считайте HMAC от сырого тела (до JSON-парсинга) и сравните в константном времени.
import crypto from "node:crypto";
// нужно СЫРОЕ тело — express.raw({ type: "application/json" })
app.post("/callback", express.raw({ type: "*/*" }), (req, res) => {
const got = req.headers["x-signature"];
const exp = crypto.createHmac("sha256", process.env.SYNCROPAY_SECRET)
.update(req.body).digest("hex");
const a = Buffer.from(got || ""), b = Buffer.from(exp);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b))
return res.sendStatus(401);
const cb = JSON.parse(req.body);
if (cb.status_id === 5) markOrderPaid(cb.order_id);
res.sendStatus(200);
});
// $raw — сырое тело: file_get_contents("php://input")
$raw = file_get_contents("php://input");
$got = $_SERVER["HTTP_X_SIGNATURE"] ?? "";
$exp = hash_hmac("sha256", $raw, getenv("SYNCROPAY_SECRET"));
if (!hash_equals($exp, $got)) { http_response_code(401); exit; }
$cb = json_decode($raw, true);
if ((int)$cb["status_id"] === 5) markOrderPaid($cb["order_id"]);
http_response_code(200);
Коды статусов (status_id)
| status_id | Код | Значение |
|---|---|---|
| 1 | NEW | Заказ создан. |
| 2 | CRED_ISSUED | Реквизит выдан, ждём оплату клиента. |
| 3 | CUSTOMER_PAID | Клиент отметил оплату / идёт сверка. |
| 5 | COMPLETED | Успех. Средства зачтены. Терминальный. |
| 4 | CANCELLED | Отменён. Терминальный. |
| 6 | REJECTED | Отклонён. Терминальный. |
| 7 | APPEAL | Открыт спор / апелляция. |
| 8 | NOT_TAKEN | Не взят в работу (нет реквизита). |
| 9 | EXPIRED | Истёк срок оплаты. Терминальный. |
Выплаты используют payout_status: NEW · PROCESSING · COMPLETED · FAILED · CANCELLED.
Коды ошибок
Ошибки: { "status": "error", "error_code": N, "error": "текст" }.
| error_code | HTTP | Причина |
|---|---|---|
| 101 | 401 | Мерчант не найден или неактивен (плохой токен). |
| 102 | 400 | Сумма меньше минимально допустимой. |
| 103 | 422 | Сумма больше максимально допустимой. |
| 104 | 409 | Заказ с таким order_id уже существует. |
| 105 | 400 | Неверный cred_type (поддерживается только карта = 1). |
| 106 | 400 | Нет подходящего реквизита (карты) под заказ. |
| 108 | 400 | Неверный номер карты / формат реквизита. |
| 109 | 400 | Выплата: недостаточно баланса или нет трейдера. |
| 110 | 404 | Выплата не найдена. |
| 404 | 404 | Заказ не найден. |
Чеклист интеграции
- Получить у менеджера API-токен, webhook_secret, задать callback_url, согласовать лимиты и курс.
- Проверить связь:
POST /api/v1/health_check. - Реализовать
/api/v4/order/createс уникальнымorder_id. - Показать клиенту реквизит (
cred_formatted) или редирект наredirect_url. - Поднять эндпоинт колбеков: проверка
X-Signature→ зачисление приstatus_id = 5→ ответ 2xx. - Сделать обработчик идемпотентным (дедуп по order_id + status_id).
- Fallback: периодически опрашивать
/api/v1/order/status. - Протестировать на тестовых доступах, затем переключиться на боевые.
callback_url менеджеру — поможем с тестовым прогоном полного цикла до status_id = 5.