Примеры SDK и Кода
Примеры использования Syncra Merchant API V2 на различных языках программирования
Примеры интеграции и использования SDK
В этом разделе представлены готовые к использованию примеры кода для интеграции с Syncra Merchant API V2 на четырех языках: curl (HTTP), Python, Node.js и PHP.
Все примеры ниже сверены с proto field names V2. Ключевые инварианты:
- PayIn:
payment_method_id(UUID), безsuccess_url/failed_url. - Withdraw:
tron_address(НЕaddress), безblockchain: "TRC20". - Webhook verify: заголовок
X-Signature(НЕX-Syncra-Signature).
1. Быстрый старт (Quick Start)
Создание простой платежной ссылки для приема оплаты. payment_method_id
обязателен — UUID или слаг из GET /payment-methods (раздел 3 ниже).
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/deals/payin \
-H "X-Merchant-Token: your_merchant_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"currency": "RUB",
"payment_method_id": "sbp_rub",
"idempotency_key": "quickstart_order_001"
}'import requests
import time
import hmac
import hashlib
import json
token = "your_merchant_token"
secret = "your_webhook_secret"
timestamp = str(int(time.time()))
body = json.dumps({
"amount": 150000,
"currency": "RUB",
"payment_method_id": "sbp_rub",
"idempotency_key": "quickstart_order_001"
})
payload = f"{timestamp}.{body}"
signature = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
headers = {
"X-Merchant-Token": token,
"X-Timestamp": timestamp,
"X-Signature": f"t={timestamp},v1={signature}",
"Content-Type": "application/json"
}
response = requests.post(
"https://api.syncra.money/api/v1/p2p/merchant/deals/payin",
headers=headers,
data=body
)
print(response.json())const crypto = require('crypto');
const token = 'your_merchant_token';
const secret = 'your_webhook_secret';
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify({
amount: 150000,
currency: 'RUB',
payment_method_id: 'sbp_rub',
idempotency_key: 'quickstart_order_001'
});
const payload = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
fetch('https://api.syncra.money/api/v1/p2p/merchant/deals/payin', {
method: 'POST',
headers: {
'X-Merchant-Token': token,
'X-Timestamp': timestamp,
'X-Signature': `t=${timestamp},v1=${signature}`,
'Content-Type': 'application/json'
},
body: body
})
.then(res => res.json())
.then(console.log);<?php
$token = "your_merchant_token";
$secret = "your_webhook_secret";
$timestamp = (string)time();
$body = json_encode([
"amount" => 150000,
"currency" => "RUB",
"payment_method_id" => "sbp_rub",
"idempotency_key" => "quickstart_order_001"
]);
$payload = $timestamp . '.' . $body;
$signature = hash_hmac('sha256', $payload, $secret);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/deals/payin");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-Merchant-Token: {$token}",
"X-Timestamp: {$timestamp}",
"X-Signature: t={$timestamp},v1={$signature}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
?>2. Инициализация и Хелпер Аутентификации (Auth Setup)
Класс-хелпер для формирования заголовков подписи HMAC-SHA256. Подпись
вычисляется по строке "{timestamp}.{body}".
import time
import hmac
import hashlib
class SyncraAuth:
def __init__(self, token: str, secret: str):
self.token = token
self.secret = secret
def get_headers(self, body_str: str) -> dict:
timestamp = str(int(time.time()))
payload = f"{timestamp}.{body_str}"
signature = hmac.new(
self.secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return {
"X-Merchant-Token": self.token,
"X-Timestamp": timestamp,
"X-Signature": f"t={timestamp},v1={signature}",
"Content-Type": "application/json"
}const crypto = require('crypto');
class SyncraAuth {
constructor(token, secret) {
this.token = token;
this.secret = secret;
}
getHeaders(body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', this.secret)
.update(payload)
.digest('hex');
return {
'X-Merchant-Token': this.token,
'X-Timestamp': timestamp,
'X-Signature': `t=${timestamp},v1=${signature}`,
'Content-Type': 'application/json'
};
}
}<?php
class SyncraAuth {
private $token;
private $secret;
public function __construct($token, $secret) {
$this->token = $token;
$this->secret = $secret;
}
public function getHeaders($bodyStr) {
$timestamp = (string)time();
$payload = $timestamp . '.' . $bodyStr;
$signature = hash_hmac('sha256', $payload, $this->secret);
return [
"X-Merchant-Token: {$this->token}",
"X-Timestamp: {$timestamp}",
"X-Signature: t={$timestamp},v1={$signature}",
"Content-Type: application/json"
];
}
}
?>3. Прием платежей (Create PayIn)
Создание платежной сессии для клиента. Используется payment_method_id
(UUID метода из GET /payment-methods). Поля success_url / failed_url в V2
не передаются — редиректы настраиваются на уровне мерчанта.
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/deals/payin \
-H "X-Merchant-Token: your_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab" \
-H "Content-Type: application/json" \
-d '{
"amount": 250000,
"currency": "RUB",
"payment_method_id": "550e8400-e29b-41d4-a716-446655440000",
"idempotency_key": "unique_order_99812",
"callback_url": "https://yourserver.com/callback",
"description": "Пополнение счёта игрока 99812"
}'# Использование класса хелпера SyncraAuth из раздела 2
auth = SyncraAuth("your_token", "your_secret")
body = json.dumps({
"amount": 250000,
"currency": "RUB",
"payment_method_id": "550e8400-e29b-41d4-a716-446655440000",
"idempotency_key": "unique_order_99812",
"callback_url": "https://yourserver.com/callback",
"description": "Пополнение счёта игрока 99812"
})
headers = auth.get_headers(body)
res = requests.post(
"https://api.syncra.money/api/v1/p2p/merchant/deals/payin",
headers=headers, data=body
)
print(res.json())
# Ответ: MerchantDealInfo (deal_id, payment_url).const auth = new SyncraAuth('your_token', 'your_secret');
const body = JSON.stringify({
amount: 250000,
currency: 'RUB',
payment_method_id: '550e8400-e29b-41d4-a716-446655440000',
idempotency_key: 'unique_order_99812',
callback_url: 'https://yourserver.com/callback',
description: 'Пополнение счёта игрока 99812'
});
const headers = auth.getHeaders(body);
fetch('https://api.syncra.money/api/v1/p2p/merchant/deals/payin', {
method: 'POST',
headers: headers,
body: body
})
.then(res => res.json())
.then(console.log);
// Ответ: MerchantDealInfo (deal_id, payment_url).<?php
$auth = new SyncraAuth("your_token", "your_secret");
$body = json_encode([
"amount" => 250000,
"currency" => "RUB",
"payment_method_id" => "550e8400-e29b-41d4-a716-446655440000",
"idempotency_key" => "unique_order_99812",
"callback_url" => "https://yourserver.com/callback",
"description" => "Пополнение счёта игрока 99812"
]);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/deals/payin");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $auth->getHeaders($body));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
// Ответ: MerchantDealInfo (deal_id, payment_url).
?>4. Выплаты клиентам (Create PayOut)
Выполнение P2P-выплаты на банковские карты. Поле payment_method — слаг
(card_rub, sbp_rub, mobcom_rub) или UUID метода из
GET /payment-methods, НЕ uppercase enum.
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/deals/payout \
-H "X-Merchant-Token: your_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab" \
-H "Content-Type: application/json" \
-d '{
"amount": 500000,
"currency": "RUB",
"wallet_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
"target_requisite": "2202201234567890",
"target_name": "Иван И.",
"payment_method": "card_rub",
"idempotency_key": "unique_payout_1291"
}'auth = SyncraAuth("your_token", "your_secret")
body = json.dumps({
"amount": 500000,
"currency": "RUB",
"wallet_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
"target_requisite": "2202201234567890",
"target_name": "Иван И.",
"payment_method": "card_rub",
"idempotency_key": "unique_payout_1291"
})
headers = auth.get_headers(body)
res = requests.post(
"https://api.syncra.money/api/v1/p2p/merchant/deals/payout",
headers=headers, data=body
)
print(res.json())
# Ответ: MerchantDealInfo (deal_id, status, target_requisite).const auth = new SyncraAuth('your_token', 'your_secret');
const body = JSON.stringify({
amount: 500000,
currency: 'RUB',
wallet_id: '8f870a31-b88d-4cb0-a882-628d08cbda88',
target_requisite: '2202201234567890',
target_name: 'Иван И.',
payment_method: 'card_rub',
idempotency_key: 'unique_payout_1291'
});
const headers = auth.getHeaders(body);
fetch('https://api.syncra.money/api/v1/p2p/merchant/deals/payout', {
method: 'POST',
headers: headers,
body: body
})
.then(res => res.json())
.then(console.log);
// Ответ: MerchantDealInfo (deal_id, status, target_requisite).<?php
$auth = new SyncraAuth("your_token", "your_secret");
$body = json_encode([
"amount" => 500000,
"currency" => "RUB",
"wallet_id" => "8f870a31-b88d-4cb0-a882-628d08cbda88",
"target_requisite" => "2202201234567890",
"target_name" => "Иван И.",
"payment_method" => "card_rub",
"idempotency_key" => "unique_payout_1291"
]);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/deals/payout");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $auth->getHeaders($body));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
// Ответ: MerchantDealInfo (deal_id, status, target_requisite).
?>5. Верификация Вебхуков (Verify Callback)
Проверка подписи входящих вебхуков на ваш обработчик. Подпись приходит в
заголовке X-Signature в формате t=<unix>,v1=<hex_hmac_sha256>.
Заголовок называется X-Signature (НЕ X-Syncra-Signature).
Подпись вычисляется по сырому телу запроса — не нормализуйте JSON.
import hmac
import hashlib
def verify_syncra_webhook(raw_body: str, signature_header: str, secret: str) -> bool:
# Заголовок: X-Signature: t=<unix>,v1=<hex>
parts = signature_header.split(',')
t_val = [p for p in parts if p.startswith('t=')][0][2:]
v1_val = [p for p in parts if p.startswith('v1=')][0][3:]
payload = f"{t_val}.{raw_body}"
expected = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(v1_val, expected)const crypto = require('crypto');
function verifySyncraWebhook(rawBody, signatureHeader, secret) {
// Заголовок: X-Signature: t=<unix>,v1=<hex>
const parts = signatureHeader.split(',');
const t = parts.find(p => p.startsWith('t=')).substring(2);
const v1 = parts.find(p => p.startsWith('v1=')).substring(3);
const payload = `${t}.${rawBody}`;
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}<?php
function verifySyncraWebhook($rawBody, $signatureHeader, $secret) {
// Заголовок: X-Signature: t=<unix>,v1=<hex>
$parts = explode(',', $signatureHeader);
$t = '';
$v1 = '';
foreach ($parts as $part) {
if (strpos($part, 't=') === 0) $t = substr($part, 2);
if (strpos($part, 'v1=') === 0) $v1 = substr($part, 3);
}
$payload = $t . '.' . $rawBody;
$expected = hash_hmac('sha256', $payload, $secret);
return hash_equals($v1, $expected);
}
?>6. История Сделок (List Deals)
Постраничный запрос списка сделок мерчанта с фильтрами. Эндпоинт
ListMerchantDeals принимает параметры в query string метода GET.
Для GET-запросов подпись HMAC вычисляется по пустому телу (параметры передаются в URL, а не в JSON body).
curl -X GET "https://api.syncra.money/api/v1/p2p/merchant/deals?deal_type=PAYIN&statuses=COMPLETED&page_size=20" \
-H "X-Merchant-Token: your_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab"auth = SyncraAuth("your_token", "your_secret")
# Подпись для GET-запроса — по пустому телу
headers = auth.get_headers("")
params = {
"deal_type": "PAYIN",
"statuses": ["COMPLETED"],
"page_size": 20
}
res = requests.get(
"https://api.syncra.money/api/v1/p2p/merchant/deals",
headers=headers, params=params
)
print(res.json())const auth = new SyncraAuth('your_token', 'your_secret');
// Подпись для GET-запроса — по пустому телу
const headers = auth.getHeaders('');
const params = new URLSearchParams({
deal_type: 'PAYIN',
page_size: '20'
});
params.append('statuses', 'COMPLETED');
fetch(`https://api.syncra.money/api/v1/p2p/merchant/deals?${params}`, {
method: 'GET',
headers: headers
})
.then(res => res.json())
.then(console.log);<?php
$auth = new SyncraAuth("your_token", "your_secret");
// Подпись для GET-запроса — по пустому телу
$headers = $auth->getHeaders("");
$query = http_build_query([
"deal_type" => "PAYIN",
"statuses" => ["COMPLETED"],
"page_size" => 20
]);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/deals?" . $query);
curl_setopt($ch, CURLOPT_HTTPGET, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
?>Параметры фильтрации (Query Parameters)
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
deal_type | string | Нет | Тип операции: "PAYIN" или "PAYOUT". |
statuses | array | Нет | Массив canonical-статусов (повторяющийся параметр, например statuses=COMPLETED&statuses=EXPIRED). |
created_after | string(RFC3339) | Нет | Нижняя граница времени создания сделки (включительно). |
created_before | string(RFC3339) | Нет | Верхняя граница времени создания сделки (включительно). |
page_size | int32 | Нет | Элементов на странице (по умолчанию 50, максимум 250). |
page_token | string | Нет | Токен следующей страницы из предыдущего ответа. |
7. Разрешение Диспутов (Create Appeal)
Создание апелляции по сделке с указанием причины и UUID загруженных доказательств.
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/appeals \
-H "X-Merchant-Token: your_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "7ac148fe-19a3-45bb-b992-019fac55b721",
"reason": "Клиент перевёл средства, но платёж не зачислен",
"file_ids": ["f3a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c"]
}'auth = SyncraAuth("your_token", "your_secret")
body = json.dumps({
"deal_id": "7ac148fe-19a3-45bb-b992-019fac55b721",
"reason": "Клиент перевёл средства, но платёж не зачислен",
"file_ids": ["f3a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c"]
})
headers = auth.get_headers(body)
res = requests.post(
"https://api.syncra.money/api/v1/p2p/merchant/appeals",
headers=headers, data=body
)
print(res.json())const auth = new SyncraAuth('your_token', 'your_secret');
const body = JSON.stringify({
deal_id: '7ac148fe-19a3-45bb-b992-019fac55b721',
reason: 'Клиент перевёл средства, но платёж не зачислен',
file_ids: ['f3a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c']
});
const headers = auth.getHeaders(body);
fetch('https://api.syncra.money/api/v1/p2p/merchant/appeals', {
method: 'POST',
headers: headers,
body: body
})
.then(res => res.json())
.then(console.log);<?php
$auth = new SyncraAuth("your_token", "your_secret");
$body = json_encode([
"deal_id" => "7ac148fe-19a3-45bb-b992-019fac55b721",
"reason" => "Клиент перевёл средства, но платёж не зачислен",
"file_ids" => ["f3a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c"]
]);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/appeals");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $auth->getHeaders($body));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
?>8. Криптовалютный Вывод (Crypto Withdrawal)
Вывод баланса мерчанта в Tether (USDT) на внешний TRON-кошелёк. Поле адреса
называется tron_address (НЕ address); blockchain опционален и
по умолчанию TRON.
curl -X POST https://api.syncra.money/api/v1/p2p/merchant/wallet/withdraw \
-H "X-Merchant-Token: your_token" \
-H "X-Timestamp: 1783856120" \
-H "X-Signature: t=1783856120,v1=9e87d0c3c8801d0a5198d023b6b19a16f2c8d2037920ab3e09819cd8e412cfab" \
-H "Content-Type: application/json" \
-d '{
"amount_usdt": 1000000,
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"idempotency_key": "withdraw_001"
}'auth = SyncraAuth("your_token", "your_secret")
body = json.dumps({
"amount_usdt": 1000000,
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"idempotency_key": "withdraw_001"
})
headers = auth.get_headers(body)
res = requests.post(
"https://api.syncra.money/api/v1/p2p/merchant/wallet/withdraw",
headers=headers, data=body
)
print(res.json())const auth = new SyncraAuth('your_token', 'your_secret');
const body = JSON.stringify({
amount_usdt: 1000000,
tron_address: 'TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a',
idempotency_key: 'withdraw_001'
});
const headers = auth.getHeaders(body);
fetch('https://api.syncra.money/api/v1/p2p/merchant/wallet/withdraw', {
method: 'POST',
headers: headers,
body: body
})
.then(res => res.json())
.then(console.log);<?php
$auth = new SyncraAuth("your_token", "your_secret");
$body = json_encode([
"amount_usdt" => 1000000,
"tron_address" => "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"idempotency_key" => "withdraw_001"
]);
$ch = curl_init("https://api.syncra.money/api/v1/p2p/merchant/wallet/withdraw");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $auth->getHeaders($body));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
echo $response;
?>9. Реквизиты на платёжной странице (CheckoutView)
Сам POST /deals/payin не возвращает реквизиты — он отдаёт только
payment_url. Чувствительные данные (номер карты, телефон, IBAN) игрок
получает, когда открывает платёжную страницу: она дёргает публичный эндпоинт
GET /api/v1/p2p/checkout/{hash} (без авторизации — hash является
capability-токеном) и рендерит CheckoutView.
Если вы делаете собственный фронтенд (вместо hosted payform) — используйте тот же эндпоинт. Все чувствительные поля зашифрованы в Vault и расшифровываются сервером; передавать PAN/IBAN мерчанту через API PayIn протокол не предусматривает.
Контракт CheckoutView (поля реквизита)
| Поле | Тип | Назначение | Когда непусто |
|---|---|---|---|
card_number | string | Полный PAN (расшифрован из Vault) или идентификатор кошелька для E_WALLET. | BANK_CARD, E_WALLET, сделка активна. |
holder_name | string | ФИО держателя карты/счёта. | BANK_CARD, E_WALLET, BANK_TRANSFER, сделка активна. |
phone_number | string | Номер телефона в E.164 для СБП/SIM. | SBP, SIM, сделка активна. |
iban | string (optional) | IBAN получателя (ISO 13616), расшифрован из Vault тем же transit key, что и PAN. | BANK_TRANSFER, сделка активна. |
payment_method_name | string | Название банка/канала для отображения. | Всегда, если задано. |
Все поля реквизита опциональны и пустые, если не применимы к типу
сделки. В терминальных состояниях (COMPLETED, EXPIRED, FAILED, ...)
чувствительные поля обнуляются — игроку PAN/IBAN больше не нужен.
Пример ответа: перевод по IBAN (BANK_TRANSFER)
{
"deal_id": "7ac148fe-19a3-45bb-b992-019fac55b721",
"hash": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"status": "ESCROW_LOCKED",
"amount": 150000,
"currency": "TRY",
"payment_method_name": "Havale — Bank Ziraat",
"expires_at": 1783859720,
"brand_name": "Casino Royal",
"primary_color": "#7c3aed",
"logo_url": "https://cdn.casino-royal.example/logo.svg",
"theme": "dark",
"success_redirect_url": "https://casino-royal.example/deposit/success",
"fail_redirect_url": "https://casino-royal.example/deposit/failed",
"card_number": "",
"holder_name": "Ahmet Yilmaz",
"phone_number": "",
"iban": "TR32 0006 2000 1234 5678 9012 34"
}Тип реквизита (BANK_CARD, SBP, E_WALLET, SIM, BANK_TRANSFER) не
передаётся в CheckoutView напрямую — он однозначно выводится из того, какое
из полей card_number / phone_number / iban заполнено. Справочник типов —
в Типы реквизитов.
Коды ошибок
Обработка ошибок, форматы ответов при сбоях, справочник кодов и retry-матрица в Syncra Merchant API V2
Verify merchant credentials POST
Verifies a merchant API token + HMAC-SHA256 signature. Called by the API Gateway auth interceptor on every request. Public clients do NOT call this endpoint directly — it is the canonical verification entrypoint. Returns the resolved `tenant_id` and `merchant_id` on success.