В отличие от приёма платежей (где достаточно X-Api-Key), вывод средств - операция повышенного риска. Поэтому подписывать нужно все эндпоинты выплат:
POST /integration/payouts- создание выплатыGET /integration/payouts/{uuid}- статус выплатыGET /integration/payouts- история выплатGET /integration/payouts/quote- предварительный расчёт
Заголовки
| Поле | Тип | Описание |
|---|---|---|
X-Api-Keyобязательное | string | API-ключ мерчанта (pk_…) |
X-Timestampобязательное | string | Unix-время в секундах на момент запроса |
X-Signatureобязательное | string | HMAC-SHA256 в hex от строки подписи (см. ниже) |
Строка подписи
Соедините четыре части символом перевода строки \n:
{timestamp}\n{METHOD}\n{path}\n{sha256_hex(raw_body)}
timestamp- то же значение, что в заголовкеX-TimestampMETHOD- HTTP-метод в верхнем регистре (POSTилиGET)path- полный путь запроса включая префикс/api/v1, например/api/v1/integration/payouts. Без хоста и без query-строки - фильтры и пагинация в истории выплат на подпись не влияютsha256_hex(raw_body)- SHA-256 в hex от сырого тела. ДляGETтело пустое - этоsha256("")
Подпись: HMAC-SHA256(строка, api_secret) в hex, где api_secret - секрет мерчанта (sk_…).
X-Timestamp должен отличаться от серверного времени не более чем на 300 секунд (±5 минут). Синхронизируйте часы сервера (NTP), иначе запросы будут отклоняться с 401.
Формирование заголовков
import crypto from "node:crypto";function signedHeaders(method, path, rawBody, apiKey, apiSecret) {const timestamp = Math.floor(Date.now() / 1000).toString();const body = method === "GET" || method === "HEAD" ? "" : rawBody;const bodyHash = crypto.createHash("sha256").update(body).digest("hex");const payload = [timestamp, method, path, bodyHash].join("\n");const signature = crypto.createHmac("sha256", apiSecret).update(payload).digest("hex");return {"X-Api-Key": apiKey,"X-Timestamp": timestamp,"X-Signature": signature,"Content-Type": "application/json",};}const path = "/api/v1/integration/payouts";const body = JSON.stringify({ amount: 2500000, currency: "RUB",external_id: "withdraw-001" });await fetch("https://api.cashera.cash" + path, {method: "POST",headers: signedHeaders("POST", path, body, PK, SK),body,});
Белый список IP
Дополнительно мерчант может ограничить, с каких IP-адресов принимаются подписанные запросы. Если allowlist настроен в кабинете, запросы с других адресов отклоняются с 403. Запросы с разрешённых адресов проходят как обычно.
Ошибки подписи
| Поле | Тип | Описание |
|---|---|---|
401 | Unauthorized | Отсутствует X-Timestamp/X-Signature, подпись неверна или время вне окна |
403 | Forbidden | Не настроен секрет мерчанта или IP не в белом списке |
