Перейти к содержанию

Быстрый старт

Создадим первый платёж и получим webhook за 5 минут.

Что нужно

  • api_key — публичный идентификатор. Строка вида xh_live_... (продакшн) или xh_test_... (sandbox)
  • api_secret — секрет, хранится на твоём бэкенде
  • webhook_secret — для верификации webhook'ов
  • Публичный URL для webhook'ов (HTTPS, работающий 24/7)

Как получить ключи

  1. Свяжись с командой X-Hub через контакты, которые передал тебе менеджер. Обсудим тариф, настроим нужные опции (приём RUB / обмен USDT→RUB / KYC) и создадим твой аккаунт.

  2. На твой email прилетит magic-link от X-Hub. Кликаешь → попадаешь в свой кабинет на https://app.x-hub.online.

  3. Генерируешь свои ключи сам. В кабинете раздел «API ключи» → большая кнопка «Сгенерировать API-ключи». Нажимаешь и получаешь один раз все три секрета:

    • api_key (префикс xh_test_ для sandbox или xh_live_ для production)
    • api_secret
    • webhook_secret
  4. Сразу сохраняешь в менеджер паролей (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 будет присылать события платежей:

  1. В том же разделе «API ключи» кабинета найди блок Webhook URL
  2. Нажми «Изменить» → вставь URL своего endpoint'а (только HTTPS публичный)
  3. Сохрани

Важно:

  • Пока 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"
}

Что дальше: два пути на выбор.

  1. Редирект. Отправить клиента на payment_url — он оплатит по ссылке.
  2. Свой чекаут (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"