# Webhook келмей жатыр — себебін қалай табу керек

> Счёт төленді, бірақ сіздің серверіңізге хабарлама жетпеді. Диагностиканы қай жерден бастау керек, ең жиі кездесетін себеп қайсы және оны бір сынаумен қалай анықтауға болады.

## Қысқаша

Алдымен кабинеттегі **webhook журналын** ашыңыз: біз хабарламаны жібердік пе, сервер қандай жауап қайтарды — бәрі сонда жазулы. Журналда «жіберілді, бірақ жауап жоқ» немесе 401/403 тұрса, мәселе сіздің адресіңізде. Ең жиі кездесетін себеп — **webhook адресі авторизациямен жабық**: сайтыңыздың басты паролі, IP тізімі, Cloudflare қорғанысы немесе VPN бізді ішке кіргізбейді. Біз кәдімгі сыртқы клиент сияқты келеміз, ешқандай логин-пароль жібермейміз.

## Симптом бойынша

| Не көріп тұрсыз | Ықтимал себебі | Не істеу керек |
|---|---|---|
| Журналда бірде-бір жазба жоқ | Адрес мүлде қосылмаған немесе бұл оқиға таңдалмаған | Кабинет → Интеграциялар → адресті және оқиға тізімін тексеріңіз |
| Журналда «жіберілді», жауап 401/403 | Адрес авторизациямен жабық | Дәл сол жолды ашық қалдырыңыз |
| Жауап 404 | Адрес жолы қате немесе маршрут тіркелмеген | Толық адресті браузерге емес, curl-мен тексеріңіз |
| Жауап 301/302 | Басқа жерге қайта бағыттау — біз ұстамаймыз | Соңғы адресті webhook ретінде жазыңыз |
| Жауап 500 | Сіздің өңдеушіңіз құлап жатыр | Өз журналыңызды қараңыз |
| Жауап уақыты біткен (timeout) | Өңдеу тым ұзақ | Алдымен 200 қайтарыңыз, жұмысты кейін істеңіз |
| Хабарлама келеді, бірақ код оны қабылдамайды | Қолтаңба сәйкес келмейді | Төмендегі 5-қадамды қараңыз |

## Диагностика реті

Ретті бұзбаңыз — әр қадам келесісін мағыналы етеді.

**1. Кабинеттегі журналды қараңыз.** Интеграциялар бөлімінде әр жіберілім жазылады: оқиға түрі, уақыты, серверіңіздің HTTP жауабы. Егер жазба мүлде болмаса, біз жібермегенбіз — демек счёттың күйі әлі өзгермеген немесе адрес қосылмаған. Егер жазба бар да, жауап сәтсіз болса, мәселе сіздің жағыңызда.

**2. Адрес жария қолжетімді ме?** Бұл ең жиі кездесетін себеп. Webhook адресі интернеттен кез келген клиент үшін ашық болуы керек. Бізде сіздің паролііңіз, токеніңіз, VPN-іңіз жоқ.

Тексеру: өзіңіздің серверіңізден емес, **сырттан** сұрау жіберіңіз.

```
curl -i -X POST https://sizdin-domen.kz/webhooks/qutpay -d '{}'
```

401, 403, 302 немесе байланыс мүлде орнамаса — біз де дәл солай кіре алмаймыз. Жиі кездесетін кедергілер: сайтқа қойылған басты пароль (basic auth), әкімшілік панельдің қорғанысы, WAF немесе Cloudflare ережесі, IP бойынша ақ тізім, staging орта.

Шешімі — бүкіл сайтты ашу емес, **тек осы бір жолды** авторизациясыз қалдыру. Қауіпсіздікті қолтаңба тексеруі қамтамасыз етеді, ол пароль орнына жүреді.

**3. https па және нақты домен бе?** Продакшенде тек `https` қабылданады, домен нақты болуы керек. IP адрес, уақытша туннель адрестері (ngrok сияқты) қабылданбайды. Сертификат жарамды болсын: мерзімі өткен немесе өзі қол қойған сертификат байланысты үзеді.

**4. Қайта бағыттауды алып тастаңыз.** Біз 307 және 308 бағыттауларын **тек сол адрестің өзіне** ұстаймыз — мысалы `http` → `https` немесе соңындағы қиғаш сызық. Басқа домен немесе басқа жолға апаратын бағыттау ұсталмайды. Сондықтан webhook ретінде соңғы, нақты адресті жазыңыз: `www` болса `www`-мен, қиғаш сызық керек болса сызығымен.

**5. Қолтаңба тексеруін тексеріңіз.** Кейде хабарлама жетеді, бірақ өңдеуші оны қабылдамай тастайды. Қолтаңба `HMAC-SHA256(secret, timestamp + "." + rawBody)` түрінде есептеледі, hex, `sha256=` префиксімен. Ең жиі қате — денені JSON-ға айналдырып, сосын қайта мәтінге түрлендіріп тексеру. **Өзгертілмеген байт күйіндегі денені** қолданыңыз.

**6. Сервер 2xx қайтарады ма?** Біз 2xx-тен басқасының бәрін сәтсіз деп есептейміз. Өңдеу ұзаққа созылатын болса, алдымен 200 қайтарып, жұмысты фонда істеңіз.

## Қайталау

2xx емес жауап берсеңіз немесе жауап мүлде келмесе, біз хабарламаны **11 рет қайталаймыз**. Аралық 10 секундтан басталып, бір сағатқа дейін өседі. Яғни серверіңіз бір сағатқа құласа да, қалпына келгенде хабарлама жетеді — қолмен ештеңе істеудің қажеті жоқ.

Бірақ осы себепті **өңдеуіңіз идемпотентті болуы керек**: бір оқиға бірнеше рет келуі мүмкін. `(invoice.id, status)` жұбын кілт ретінде алып, бұрын өңдегеніңізді екінші рет өңдемеңіз.

## webhook.site арқылы сынау

Мәселе бізде ме, сізде ме — екі минутта анықтауға болады.

1. [webhook.site](https://webhook.site) ашасыз, ол сізге уақытша адрес береді
2. Кабинет → Интеграциялар → сол адресті webhook ретінде қосасыз
3. Sandbox режимінде счёт жасап, төлемді симуляциялайсыз
4. webhook.site бетінде хабарлама пайда болды ма?

Пайда болса — біз дұрыс жіберіп тұрмыз, мәселе сіздің серверіңізде: адрес жабық, маршрут жоқ немесе код қате. Пайда болмаса — адрес тізімін, оқиға таңдауын тексеріп, қолдауға жазыңыз.

Сынау біткен соң уақытша адресті өшіруді ұмытпаңыз: ол жария, оған келген деректі кез келген адам көреді.

## Жиі жіберілетін қателер

- **Webhook адресін браузерде ашып тексеру.** Браузер GET жібереді, ал біз POST жібереміз. GET жауап берді дегеніміз POST жауап береді дегенді білдірмейді.
- **Локалдағы адресті жазу.** `localhost`, `127.0.0.1` немесе жергілікті желі адресі сырттан көрінбейді.
- **Оқиға тізімін тарылтып қою.** Тек `invoice.paid` таңдап қойып, сосын `invoice.expired` неге келмейді деп ойлау.
- **Тек webhook-қа сүйену.** Кідіріске сезімтал сценарийде webhook-пен қатар счёт күйін де сұрап тұрған дұрыс.
- **Тест пен нақты режимді шатастыру.** Sandbox-та бір адрес, live-та басқа адрес тұрса, бір режимде істеп, екіншісінде істемейді.

## Жиі қойылатын сұрақтар

**Webhook орнына күйді өзім сұрасам бола ма?** Болады, бірақ екеуін қатар жүргізген дұрыс: webhook жылдам, сұрау сенімді.

**Хабарлама бірнеше рет келді — бұл қате ме?** Жоқ, бұл қалыпты. Қайталау механизмі осылай жұмыс істейді. Өңдеуіңіз идемпотентті болса, зияны жоқ.

**Құпияны жоғалтып алдым, көру мүмкін бе?** Жоқ, құпия бір рет қана көрсетіледі. Кабинеттен жаңасын жасаңыз да, кодтағы мәнді жаңартыңыз.

**Сервер уақытша құлап тұрса, төлемдер жоғала ма?** Жоқ. Қайталау бір сағатқа дейін созылады, ал счёттардың күйі бізде сақталады — кез келген уақытта API арқылы оқи аласыз.

**Webhook қанша уақыт ішінде келеді?** Клиент төлегеннен кейін әдетте бес секунд ішінде. Кешігу болса: [Счёт pending күйінде тұрып қалды](/kb/invoice-stuck-pending).
