# Вендинг автоматына терминалсыз төлем

> Автомат экранында динамикалық QR, клиент сканерлейді, webhook келеді, контроллер тауарды береді. Су, кофе және снек автоматтары үшін схема, жабдық жағы және QR терезесі.

## Қысқаша

Вендинг автоматында схема қарапайым: клиент тауарды таңдайды → автоматтың контроллері бұлттағы сіздің серверге айтады → сервер Qut Pay-де счёт жасайды → QR автоматтың экранында шығады → клиент Kaspi-мен сканерлейді → бізден серверге `invoice.paid` webhook келеді → сервер автоматқа «бер» деген команда жібереді. Су, кофе, снек автоматтарының бәріне бірдей келеді. QR **әр сатылымға жаңадан** жасалады, экранда тұрақты тұрған сурет жарамайды.

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

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

Клиент растағаннан кейін webhook әдетте **бес секунд ішінде** келеді, сондықтан адам автоматтың жанында тұрып күте алады.

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

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

```
POST https://api.qut.kz/api/v1/invoices
X-API-Key: qp_live_…
Idempotency-Key: vm-014-1726300000

{
  "amount": 250,
  "kind": "qr",
  "description": "Автомат №14, су 5 л",
  "externalOrderId": "vm-014-88231",
  "metadata": { "machine": "VM-014", "slot": "A2", "address": "Абай 10" }
}
```

Жауаптан керегі: `id`, `qrImageUrl` (экранға шығаратын сурет), `qrUrl`, `expiresAt` (таймер үшін).

Қосымша:

- `GET /api/v1/invoices/{id}` — webhook кешіксе күйін сұрау
- `POST /api/v1/invoices/{id}/cancel` — клиент кетіп қалса, счётты жабу
- `POST /api/v1/invoices/{id}/refund` — тауар берілмей қалса, ақшаны қайтару

## Автомат нөмірін metadata-ға жазыңыз

Бұл — бүкіл сценарийдің негізі. `metadata` ішіне кемінде үшеуін салыңыз: **автомат нөмірі**, **ұяшық немесе тауар коды**, **мекенжай**. Webhook келгенде қай автоматқа команда жіберу керегін сол өрістен бірден білесіз, өз базаңыздан іздемей-ақ.

`externalOrderId` ретінде автомат нөмірі мен ішкі сатылым нөмірін біріктірген ыңғайлы: `vm-014-88231`. Кейін есеп бергенде де, дауды шешкенде де осы нөмір бойынша табасыз.

Нүктелер бойынша бөлек есеп керек болса, әр нүктеге бөлек API кілт жасаңыз — счёттарды көзі бойынша сүзу жеңілдейді.

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

Автоматтың ішінде не керек:

- **Байланысы бар контроллер.** GSM модемі (SIM картамен) немесе нүктеде Wi-Fi болса Wi-Fi модулі. Қосылымсыз схема жұмыс істемейді: счёт бұлтта жасалады.
- **Экран.** QR-ды көрсететін кез келген экран жарайды. Кейбір автоматтарда бұрыннан бар, кейбіріне шағын дисплей қою керек.
- **Бұлттағы сервер.** Счёт жасайды, webhook қабылдайды, автоматқа команда жібереді. API кілт тек осында тұрады, контроллердің ішінде емес.
- **Команда арнасы.** Көбіне MQTT: контроллер брокерге жазылып тұрады, сервер сол арнаға «ұяшық A2-ні аш» деп жібереді. HTTP арқылы да болады — контроллер серверден күйді сұрап отырады, бірақ MQTT жылдамырақ әрі желіге аз жүктейді.

**API кілтті контроллерге салмаңыз.** Автоматтың ішіндегі құрылғыны ашып алуға болады, ал бір кілт бүкіл желіге жарайды. Контроллер тек өз серверіңізге, өзінің құрылғы токенімен жүгінсін.

## QR терезесі — ең маңызды шектеу

Сканерлеу терезесі шамамен **үш минут**, оны Kaspi белгілейді. Вендингте бұл бірден екі салдар береді.

**Экранда тұрақты QR ілініп тұра алмайды.** Әр сатылымға жаңа счёт жасалады. Автоматтың экранында алдын ала басып қойылған QR суреті болса, ол бір күннен кейін жұмысын тоқтатады.

**Таймер көрсетіңіз.** `expiresAt` өрісін алып, экранда «QR 2:40 ішінде жарамды» деп жазыңыз. Уақыт біткенде «Қайта көрсету» батырмасын беріңіз — контроллер жаңа счёт сұрайды, ескісі `expired` болып қалады.

Тұрақты QR басып қою керек болса (мысалы, «сұрақ болса осында төлеңіз» деген қосымша жол), ол QR емес, **төлем сілтемесі** болуы керек: `qut.kz/p/<slug>`. Оның мерзімі бітпейді, сомасы ашық түрі де бар.

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

- **Идемпоттылық.** Байланыс үзіліп, контроллер сұрауды қайта жіберуі — қалыпты жағдай. `Idempotency-Key` қойыңыз, әйтпесе бір клиентке екі счёт шығады.
- **Webhook идемпотентті болсын.** `(invoice.id, status)` жұбы бойынша бір-ақ рет өңдеңіз — әйтпесе автомат екі бөтелке беріп жібереді.
- **Тауар берілмей қалса.** Ақша түсіп, ұяшық ашылмаса (тұрып қалды, тауар бітті), автоматты түрде `refund` жасайтын логика жазыңыз. Бұл вендингтегі ең жиі дау.
- **Webhook пен күйді сұрауды қатар жүргізіңіз.** Клиент автоматтың жанында тұр, күту ұзаққа созылмауы керек. Webhook 5-10 секунд ішінде келмесе, контроллер `GET /invoices/{id}` арқылы күйді сұрап көрсін: [Төлем баяу расталады](/kb/slow-payments).
- **Webhook адресі ашық болуы керек.** Продакшенде тек `https` және нақты домен, IP мен туннель адресі қабылданбайды. Адрес авторизациясыз ашық болсын, әйтпесе жеткізу құлайды: [Webhook келмей жатыр](/kb/webhook-not-arriving).
- **Кеш төлем.** Мерзімі біткен счётқа ақша келсе, оқиға `late: true` белгісімен келеді. Клиент кетіп қалған болса, ақшаны қайтарыңыз.
- **Автомат саны көбейгенде тарифті қайта қараңыз.** Он автомат күніне 50 сатылымнан жасаса, айына 15 000 счёт болады: [Қай тарифті таңдау керек](/kb/tariff-choose).

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

**Автоматтың өзінде интернет жоқ, бола ма?** Болмайды. Счёт бұлтта жасалады, ал төлем расталғанын автомат білуі керек. Ең арзаны — GSM модемі бар контроллер.

**Бір QR-ды бірнеше адам сканерлесе не болады?** Бір QR — бір счёт. Сондықтан ол бір ғана сатылымға жарайды, әрі әр сатылымнан кейін экранды бастапқы күйге қайтару керек.

**Автоматта экран жоқ, тек түймелер. Қалай?** Онда шағын дисплей қосу керек. QR-ды көрсететін жер болмаса, бұл схема жұмыс істемейді.

**Сканерлеп қойды да, интернет үзіліп қалды. Ақша қайда?** Ақша сіздің Kaspi шотыңызға түседі, ол автоматқа тәуелді емес. Автомат тауарды бере алмаса, `refund` жасаңыз. Сондықтан контроллердің әр командасын журналға жазып отырыңыз.

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