# Тұрақ пен шлагбаумға

> Шығу тұсындағы экранда QR немесе билет нөмірі бойынша счёт, төлем расталғанда webhook IP-реле арқылы шлагбаумды ашады. Динамикалық және басылған QR, көлік нөмірі, кідіріс.

## Қысқаша

Тұрақта схема мынау: жүргізуші шығу тұсына келеді → билет нөмірін енгізеді немесе нөмірін камера оқиды → сіздің сервер тұрақ уақытына қарай соманы есептеп, Qut Pay-де счёт жасайды → экранда QR шығады → жүргізуші Kaspi-мен сканерлеп растайды → бізден webhook келеді → сервер IP-реле арқылы шлагбаумды ашады. QR **әр шығуға жаңадан** жасалады: сканерлеу терезесі шамамен үш минут. Жүргізуші машинадан шықпай тұрып төлейді.

## Жұмыс схемасы

| Қадам | Кім | Не істейді |
|---|---|---|
| 1 | Жүргізуші | Шығу тұсына келеді, билет нөмірін енгізеді |
| 2 | Тұрақ бақылаушысы | Серверге сұрау: билет №, кіру уақыты, көлік нөмірі |
| 3 | Сіздің сервер | Соманы есептеп, `POST /api/v1/invoices` жасайды |
| 4 | Экран | QR мен таймерді көрсетеді |
| 5 | Жүргізуші | Kaspi-мен сканерлеп растайды |
| 6 | Qut Pay | Серверге `invoice.paid` жібереді |
| 7 | Сіздің сервер | IP-релеге команда жібереді |
| 8 | Шлагбаум | Ашылады, оқиға журналға жазылады |

Клиент растағаннан кейін webhook әдетте бес секунд ішінде келеді. Шығу тұсында мұның өзі ұзақ: артында кезек тұр. Сондықтан төмендегі «кідіріс» бөлімін міндетті түрде оқыңыз.

## Қандай API әдісі қолданылады

Негізгісі — `POST /api/v1/invoices`, `kind: "qr"`:

```
POST https://api.qut.kz/api/v1/invoices
X-API-Key: qp_live_…
Idempotency-Key: park-exit-4471

{
  "amount": 600,
  "kind": "qr",
  "description": "Тұрақ: билет 4471, 2 сағ 15 мин",
  "externalOrderId": "park-4471",
  "metadata": {
    "ticket": "4471",
    "plate": "123ABC02",
    "gate": "exit-2",
    "entered_at": "2026-09-14T09:12:00Z"
  }
}
```

Керегі: `id`, `qrImageUrl`, `expiresAt`. Қалғаны: `GET /api/v1/invoices/{id}` — күйін сұрау, `POST /api/v1/invoices/{id}/cancel` — жүргізуші төлемей кетсе, `POST /api/v1/invoices/{id}/refund` — шлагбаум ашылмай қалса.

Абонементпен жүретін тұрақ болса, айлық төлемді жазылым арқылы кестеге қоюға болады. Бірақ есіңізде болсын: жазылым **счётты кестеге сай шығарады**, ақшаны клиент әр жолы өзі растайды.

## Көлік нөмірін metadata-ға жазыңыз

`metadata` ішіне міндетті түрде салыңыз: **билет нөмірі**, **көлік нөмірі**, **қай шлагбаум**, **кіру уақыты**. Webhook келгенде қай шлагбаумды ашу керегін сол өрістен бірден білесіз.

Бұл дауды шешуге де керек: «мен төледім, ол ашылмады» дегенде счётты көлік нөмірі бойынша тауып, оқиғалар тізбегін көрсетесіз. `externalOrderId` ретінде билет нөмірін беріңіз — іздеу жеңіл болады.

Бірнеше тұрағыңыз болса, әрқайсысына бөлек API кілт беріңіз: нүктелер бойынша есеп бөлек шығады.

## Динамикалық және басылған QR

**Динамикалық QR (негізгі жол).** Экранда әр шығуға жаңа счёт көрсетіледі. Сомасы сол көлікке нақты есептелген, төлем нақты билетке байланады, шлагбаум автоматты ашылады. Осы жолды қолданыңыз.

**Басылған QR (қосымша жол).** Шығу тұсына тұрақты қағаз код іліп қоюға болады, бірақ ол счёттың QR-ы болмауы керек — счёттың терезесі шамамен үш минут, ертесіне ол жұмысын тоқтатады. Тұрақты код үшін **төлем сілтемесін** қолданыңыз: `qut.kz/p/<slug>`, сомасы ашық түрі де бар.

Бірақ басылған кодтың бір кемшілігі бар: төлем нақты билетке байланбайды, сондықтан шлагбаум өздігінен ашылмайды. Оны күзетші қолмен ашады немесе оператор кабинеттен төлемді көріп растайды. Сондықтан ол — автомат істемей қалғандағы немесе кіші тұрақтағы қосалқы нұсқа.

| | Динамикалық QR | Басылған төлем сілтемесі |
|---|---|---|
| Сома | Нақты есептелген | Клиент өзі енгізеді немесе тұрақты |
| Билетке байланады | Иә | Жоқ |
| Шлагбаум автоматты ашыла ма | Иә | Жоқ, қолмен |
| Мерзімі | Шамамен 3 минут | Бітпейді |
| Қайда жарайды | Негізгі жол | Қосалқы, кіші тұрақ |

## Жабдық жағы

- **Шығу тұсындағы экран мен пернетақта** — QR көрсету және билет нөмірін енгізу үшін.
- **Бұлттағы сервер** — соманы есептейді, счёт жасайды, webhook қабылдайды, шлагбаумға команда береді. API кілт тек осы жерде тұрады.
- **IP-реле немесе контроллер** — шлагбаумның «ашу» кірісіне жалғанады. Сервер релеге команда жібереді, реле контактіні жабады.
- **Байланыс** — тұрақта интернет болуы міндетті. Қосалқы GSM арнасын қойған дұрыс: интернет үзілсе, шлагбаум жұмыс істемей қалады.

**Релені интернетке тікелей шығармаңыз.** Ол тек сіздің сервердің командасын қабылдасын. API кілтті реленің немесе жергілікті контроллердің ішіне салмаңыз.

## Кідіріс маңызды: webhook пен сұрауды қатар жүргізіңіз

Шығу тұсында бірнеше секундтың өзі ұзақ. Сондықтан екі арнаны қатар ұстаңыз:

1. **Webhook — негізгісі.** `invoice.paid` келгенде шлагбаум ашылады.
2. **Күйді сұрау — сақтандырғыш.** Счёт жасалғаннан кейін контроллер `GET /api/v1/invoices/{id}` арқылы күйді әр 2-3 секунд сайын сұрап отырсын. Қайсысы бірінші «paid» көрсе, сол ашады — екеуін бір оқиға деп санаңыз, екі рет ашпаңыз.

Poller әр үш секунд сайын жүреді, ал үш минуттан жас счёттар әр айналымда тексеріледі — яғни жаңа счёттың күйі ең жиі жаңарады. Жалпы жылдамдық туралы: [Төлем баяу расталады](/kb/slow-payments).

Webhook мүлде келмей жатса, себебін журналдан қарау керек: [Webhook келмей жатыр](/kb/webhook-not-arriving).

## Ерекше ескертулер

- **Идемпоттылық.** Жүргізуші «QR» батырмасын бірнеше рет бассa, әр басқан сайын жаңа счёт шықпауы керек. `Idempotency-Key` ретінде билет нөмірін беріңіз.
- **Webhook идемпотентті болсын.** `(invoice.id, status)` жұбы бойынша бір рет өңдеңіз, әйтпесе шлагбаум екі рет ашылып, артындағы көлік те өтіп кетеді.
- **Шлагбаум ашылмай қалса — қайтару.** Ақша түсіп, реле істемесе (интернет үзілді, жабдық қатесі) `refund` жасаңыз. Бұл жердегі ең жиі дау осы.
- **Кеш төлем.** Мерзімі біткен счётқа ақша келсе, оқиға `late: true` белгісімен келеді. Жүргізуші кетіп қалған болса, ақшаны қайтарыңыз.
- **Күзетшіге қолмен ашу мүмкіндігін қалдырыңыз.** Интернет үзілген сәтте тұрақ тұрып қалмауы керек.
- **Әр оқиғаны журналға жазыңыз**: счёт жасалды, төленді, реле командасы кетті, шлагбаум ашылды. Дау шыққанда осы журнал ғана көмектеседі.
- **Алдымен sandbox.** `qp_test_…` кілтімен бүкіл тізбекті өткізіңіз: счёт → `simulate` → webhook → реле командасы: [Sandbox пен нақты режимнің айырмашылығы](/kb/sandbox-vs-live).

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

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

**Көлік нөмірін камера оқиды, билет жоқ. Бола ма?** Иә. Сома есептеу мен счёт жасау реті сол күйі қалады, тек `metadata` ішіне билет орнына көлік нөмірін жазасыз.

**Абонемент сатсам, ай сайын өзі шешіле ме?** Жоқ. Жазылым белгіленген кестемен **счёт шығарады**, ал төлемді клиент әр жолы Kaspi-де өзі растайды.

**Жүргізуші төлеп үлгермей, QR-дың уақыты бітсе?** Жаңа счёт жасайсыз — экранда «Қайта көрсету» батырмасы болсын. Ескі счёт `expired` болып қалады, ақша алынбайды.

**Интернет үзілсе не болады?** Счёт жасау да, webhook та жұмыс істемейді. Сондықтан қосалқы GSM арнасын және күзетшінің қолмен ашу мүмкіндігін алдын ала қарастырыңыз.
