Быстрый старт
Создадим первый платёж и получим webhook за 5 минут.
Что нужно
api_key— публичный идентификатор. Строка видаxh_live_...(продакшн) илиxh_test_...(sandbox)api_secret— секрет, хранится на твоём бэкендеwebhook_secret— для верификации webhook'ов- Публичный URL для webhook'ов (HTTPS, работающий 24/7)
Как получить ключи
-
Свяжись с командой X-Hub через контакты, которые передал тебе менеджер. Обсудим тариф, настроим нужные опции (приём RUB / обмен USDT→RUB / KYC) и создадим твой аккаунт.
-
На твой email прилетит magic-link от X-Hub. Кликаешь → попадаешь в свой кабинет на
https://app.x-hub.online. -
Генерируешь свои ключи сам. В кабинете раздел «API ключи» → большая кнопка «Сгенерировать API-ключи». Нажимаешь и получаешь один раз все три секрета:
api_key(префиксxh_test_для sandbox илиxh_live_для production)api_secretwebhook_secret
-
Сразу сохраняешь в менеджер паролей (1Password, Bitwarden и т.п.).
api_keyпотом можно посмотреть в любой момент в кабинете, ноapi_secretиwebhook_secretбольше показаны не будут.
Как хранятся секреты
Ключи и секреты генерируются на сервере X-Hub и показываются один раз — в момент генерации. api_secret хранится у нас только в виде хэша — восстановить его нельзя, только перевыпустить. webhook_secret хранится на нашей стороне: он нужен серверу X-Hub, чтобы подписывать каждый webhook.
Ключи у тебя раздельные для теста и боя, а webhook_secret — общий
api_key + api_secret живут в двух независимых слотах: тестовом и боевом. Когда ты генерируешь ключи второго слота, webhook_secret не выдаётся повторно — он один на оба режима, не меняется, и в открытом виде показывается ровно один раз, при самой первой генерации. Если ты его потерял — перевыпусти его отдельно (см. ниже); повторно «подсмотреть» существующий нельзя. Так сделано намеренно: этим секретом подписываются уведомления об оплате, и любой, у кого есть доступ к твоему кабинету, иначе мог бы получить его, просто нажав «сгенерировать ключи» в тестовом режиме.
Ротация
api_secret— ротируешь сам в том же разделе «API ключи» кнопкой «Перевыпустить». Старый секрет сразу перестаёт работать. Ротируется секрет того слота, в котором ты сейчас работаешь; при подозрении на утечку боевого ключа можно (и нужно) сначала перевести аккаунт в тестовый режим — приём боевых платежей остановится, — а потом сменить именно боевой секрет.webhook_secret— перевыпускается отдельной кнопкой в блоке Webhook. Старый секрет сразу перестаёт работать, поэтому меняй его синхронно со своим бэкендом (лучше в окно низкого трафика).api_key— стабильный публичный идентификатор. Ротация не предусмотрена (для смены нужен новый merchant-аккаунт).- Emergency reset — если потерял доступ к кабинету, попроси X-Hub через поддержку перевыпустить
api_secret— тебе передадут его разово через защищённый канал. Уточни, какой слот меняем: тестовый или боевой.
Храни api_secret и webhook_secret только на бэкенде
Никогда не отдавай клиенту/браузеру. При компрометации — перевыпусти через кабинет.
Настройка Webhook URL
После генерации ключей укажи URL твоего сервера, куда X-Hub будет присылать события платежей:
- В том же разделе «API ключи» кабинета найди блок Webhook URL
- Нажми «Изменить» → вставь URL своего endpoint'а (только HTTPS публичный)
- Сохрани
Важно:
- Пока
webhook_urlне указан (или не сгенерированwebhook_secret), webhook'и копятся в PENDING и не отправляются. Как только конфигурация становится полной (URL + секрет), накопленные события отпускаются сразу — в ближайшем цикле доставки, а не через отложенную паузу. Новые события после этого доставляются в течение ~5 секунд. - Если webhook так и не настроен, накопленные события живут 30 суток, после чего помечаются
FAILEDи не доставляются уже никогда. - URL можно менять в любой момент — изменение не требует реcтарта интеграции.
- Подробнее про формат webhook payload — в разделе Webhook ниже, про проверку подписи — в Verify signature.
Переход sandbox → production
У тебя один аккаунт и два слота ключей: тестовые xh_test_... и боевые xh_live_...:
- В sandbox тестируешь интеграцию под ключами
xh_test_...(без реальных денег). - Когда готов к боевому запуску — сам переключаешь режим в кабинете кнопкой «Активировать боевой режим», генерируешь
xh_live_...ключи и подставляешь их в код вместо test-ключей. - Запрос с ключом не-активного режима вернёт
401с кодомMODE_MISMATCHи понятным сообщением, какой режим сейчас активен.
Подробнее — в разделе Sandbox ниже.
Сетевые требования
Входящие webhook'и (X-Hub → твой сервер)
X-Hub отправляет webhook'и с фиксированного IP 72.56.237.132. Если твой webhook-endpoint закрыт файрволом — добавь этот IP в whitelist.
Исходящие запросы (твой сервер → X-Hub)
Если X-Hub выдал тебе IP-whitelist (не всем мерчантам — по запросу), то запросы с других IP получат 403 IP_NOT_WHITELISTED. По умолчанию whitelist не активен.
Production endpoint: https://api.x-hub.online | Sandbox: тот же URL, разный префикс ключа.
1. Создать платёж
curl -X POST https://api.x-hub.online/api/v1/payments \
-H "X-Api-Key: xh_test_abc123..." \
-H "X-Api-Secret: YOUR_API_SECRET" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount_rub": "500.00",
"external_id": "order-12345",
"payment_method": "sbp",
"description": "VPN подписка 1 месяц",
"success_url": "https://shop.example.com/orders/12345/done"
}'
Ответ:
{
"id": "pay_550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"amount_rub": "500.00",
"payment_url": "https://qr.nspk.ru/BD10100NR0811JNT8FAQG039UTUIDSOM",
"expires_at": "2026-04-14T13:15:00Z"
}
Что дальше: два пути на выбор.
- Редирект. Отправить клиента на
payment_url— он оплатит по ссылке. - Свой чекаут (host-to-host). В
payment_urlприходит ссылка НСПК, и её содержимое — это и есть полезная нагрузка QR-кода СБП. Закодируй её в QR любой библиотекой и покажи у себя: на своей странице, в приложении или картинкой в боте. Клиент никуда не уходит.
Нужна оплата картой?
Этот пример — про payment_method: "sbp". Для "card" тело запроса то же самое, а вот payment_url в ответе работает по-другому: только редирект, host-to-host-путь недоступен. Разница и что делать, если карты не подключены — в оплате картой.
2. Клиент оплачивает
Клиент видит QR-код СБП, оплачивает через приложение банка. X-Hub получает подтверждение, меняет статус на paid.
3. Получить webhook
X-Hub делает POST на твой webhook_url с payload:
{
"event": "payment.status_changed",
"payment_id": "pay_550e8400-...",
"external_id": "order-12345",
"status": "paid",
"previous_status": "processing",
"amount_rub": "500.00",
"actual_amount_rub": null,
"amount_usdt": null,
"payment_method": "sbp",
"timestamp": "2026-04-14T13:05:12Z",
"metadata": {"user_id": "usr_abc"}
}
В payload payment.status_changed есть поле refund_pending (true, пока по платежу идёт незавершённый возврат). В уведомлениях settled, expired и части paid / amount_mismatch / failed его пока нет — отсутствие поля читай как false, а не как ошибку формата.
У неуспешного списания в payload приходит failure_reason — почему не прошло. От причины зависит, что делать: не хватило денег → повторить через день-два и подписку не отменять; согласие на автосписание отозвано → просить привязать заново. Поле приходит только у status: "failed"; у остальных статусов его в payload нет. Коды и действия →
У рекуррентного списания в payload дополнительно приходят два поля: "subscription_id": "sub_..." — подписка (и её пользователь), и "period_ref": "2026-08" — период, который это списание занимает. По ним событие сопоставляется с подпиской и периодом без обращения к API. Оба поля приходят при любом изменении статуса списания: оплата, отказ, истечение, расчёт — и в песочнице тоже. У обычных (не подписочных) платежей этих полей в payload нет.
В заголовках X-Webhook-Signature и X-Webhook-Timestamp — HMAC-SHA256 от timestamp + "." + body, ключ = твой webhook_secret. Как проверить подпись →
4. Проверить статус вручную
curl https://api.x-hub.online/api/v1/payments/pay_550e8400-... \
-H "X-Api-Key: xh_test_abc123..." \
-H "X-Api-Secret: YOUR_API_SECRET"