# Telegram ботында тауар сату

> Бот → сіздің сервер → счёт → клиентке push немесе сілтеме → webhook → тауарды беру. Төленбеген счёттардың жиналуынан қорғану, бірнеше бренд, Python пен Node мысалы.

## Қысқаша

Telegram бот Qut Pay-ге тікелей жүгінбейді — ортада сіздің сервер тұрады. Клиент ботта тауарды таңдайды → бот сіздің серверге айтады → сервер счёт жасайды → бот клиентке QR суретін немесе төлем сілтемесін жібереді (не болмаса Kaspi-іне push келеді) → төлем расталған соң бізден webhook келеді → сервер ботқа «бер» деп айтады, бот тауарды береді. Кілт тек серверде болады, бот кодында емес.

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

| Қадам | Кім | Не істейді |
|---|---|---|
| 1 | Клиент | Ботта тауарды таңдап, «Төлеу» дейді |
| 2 | Бот | Сіздің серверге сұрау жібереді (chat_id, тауар, сома) |
| 3 | Сервер | `POST /api/v1/invoices`, `metadata` ішіне `chat_id` жазады |
| 4 | Бот | `qrImageUrl` суретін немесе `payUrl` сілтемесін жібереді |
| 5 | Клиент | Kaspi-де растайды |
| 6 | Qut Pay | Серверге `invoice.paid` жібереді |
| 7 | Сервер | `metadata.chat_id` бойынша ботқа хабарлайды, бот тауарды береді |

`metadata` ішіне `chat_id` салу — осы схеманың кілті. Webhook келгенде кімге жіберу керегін сол өрістен білесіз, өз базаңыздан іздемей-ақ.

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

Счёт жасау — `POST /api/v1/invoices`. Екі түрі бар:

- `kind: "qr"` — QR мен сілтеме қайтады. Клиент ботта суретті көріп сканерлейді немесе сілтемені басады. Сипаттама 100 таңбаға дейін.
- `kind: "phone"` — клиенттің Kaspi қосымшасына push келеді, `customer.phone` міндетті (`7XXXXXXXXXX`). Сома бүтін теңге, сипаттама 60 таңба.

Клиенттің нөмірін білсеңіз, ботта `phone` ыңғайлы: клиент ештеңе сканерлемейді, Kaspi-і өзі ашылады. Нөмірін Telegram-ның «Контакт жіберу» батырмасымен сұрап алуға болады.

Қалғаны: `GET /api/v1/invoices/{id}` — күйін сұрау, `POST /api/v1/invoices/{id}/cancel` — болдырмау, `POST /api/v1/invoices/{id}/refund` — қайтару.

## Қысқаша мысал

Python:

```python
import requests

r = requests.post(
    "https://api.qut.kz/api/v1/invoices",
    headers={"X-API-Key": API_KEY, "Idempotency-Key": f"tg-{chat_id}-{cart_id}"},
    json={
        "amount": 5900,
        "kind": "qr",
        "description": "Курс: Бірінші модуль",
        "externalOrderId": str(cart_id),
        "metadata": {"chat_id": chat_id, "bot": "kurs_bot"},
    },
    timeout=15,
)
inv = r.json()
bot.send_photo(chat_id, inv["qrImageUrl"], caption=f"Төлеу: {inv['payUrl']}")
```

Node.js:

```js
const res = await fetch('https://api.qut.kz/api/v1/invoices', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.QUTPAY_KEY,
    'Idempotency-Key': `tg-${chatId}-${cartId}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 5900,
    kind: 'phone',
    description: 'Курс: Бірінші модуль',
    customer: { phone: '77010000000' },
    metadata: { chat_id: chatId, bot: 'kurs_bot' },
  }),
});
const inv = await res.json();
```

Webhook жағында денені өзгертілмеген байт күйінде алып, `HMAC-SHA256(secret, timestamp + "." + rawBody)` есептеп салыстырасыз, сосын `payload.metadata.chat_id` бойынша ботқа хабарлайсыз. Дайын SDK бар: Node.js, PHP, Python.

## Төленбеген счёттардың жиналуынан қорғану

Ботта бұл нағыз мәселе: адамдар «Төлеу» батырмасын басады да, төлемей кетеді. Бірнеше жүз ашық счёт тәуліктік қорғанысқа тіреліп қалуы мүмкін.

- **Бір клиентке — бір ашық счёт.** Жаңасын жасар алдында ескісін `cancel` етіңіз немесе күйін сұраңыз. Базада `chat_id → соңғы invoice id` сақтаңыз.
- **`Idempotency-Key` қойыңыз.** Клиент батырманы бес рет басса да бір счёт шығады. Кілтке `chat_id` мен себет нөмірін қосыңыз.
- **Батырманы бұғаттаңыз.** Счёт жасалғаннан кейін «Төлеу» батырмасын алып тастаңыз немесе «Күтудеміз» деп өзгертіңіз.
- **QR терезесі біткенде өзіңіз тазалаңыз.** `expiresAt` өткен соң хабарламаны өңдеп, «Мерзімі бітті, жаңасын алу» батырмасын қойыңыз.
- **Есіңізде болсын: тәуліктік сан — бизнес лимиті емес**, циклге түскен интеграциядан қорғаныс. Ол `tariff_daily_burst` қатесін береді, айлық лимит `tariff_limit_reached` береді. Екеуі екі басқа нәрсе: [Қай тарифті таңдау керек](/kb/tariff-choose).

## Бірнеше бренд бір ботта

Бір ботта бірнеше дүкен немесе бірнеше бағыт сатсаңыз, екі жолы бар.

**Бір ұйым, бөлек белгі.** Барлық ақша бір Kaspi шотына түседі. Брендті `metadata` ішіне жазасыз (`{"brand": "shop_a"}`) және есепті сол бойынша бөлесіз. Ең қарапайымы.

**Бөлек кілттер.** Әр бағытқа бөлек API кілт жасайсыз — сонда счёттарды көзі бойынша сүзу оңай. Кілтті нақты бір кассирге байлауға да болады: байланған кілт тек сол кассирдің счёттарын көреді, басқасына 404 береді. Бірнеше кассир туралы: [Бірнеше кассир қосуға бола ма](/kb/two-cashiers).

**Ақша әртүрлі шотқа түсуі керек болса** — бұл бөлек ұйым деген сөз, әр ұйымның өз кассирі мен өз тарифі болады: [Бір аккаунтта бірнеше ұйым](/kb/multiple-organizations).

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

- **Бот токені мен API кілтті шатастырмаңыз.** Екеуі де серверде, бірақ API кілт Telegram-ға ешқашан берілмейді.
- **Кеш төлем.** Мерзімі біткен счётқа ақша келсе, `invoice.paid` оқиғасы `late: true` белгісімен келеді. Ботта мұны өңдеңіз: не тауарды беріңіз, не қайтарыңыз.
- **Webhook идемпотентті болсын.** Бір оқиға қайталанып келуі мүмкін, `(invoice.id, status)` жұбы бойынша бір рет өңдеңіз. Әйтпесе клиент тауарды екі рет алады.
- **Біздің өз ботымыз бар.** Ол сату боты емес, бақылау боты: `/invoice`, `/today`, `/last`, `/status`, `/cancel`, `/support`. Оны қатар қосып қойсаңыз, счёттарды телефоннан көріп отырасыз: [Telegram нұсқаулығы](https://api.qut.kz/docs/guide/telegram_bot).
- **Sandbox-та бүкіл циклді өткізіңіз**: счёт → `simulate` → webhook → бот тауарды берді.

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

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

**Клиент нөмірін білмесем?** QR счёт жасаңыз — ол үшін нөмір керек емес. Телефонға push тек `kind: "phone"` үшін қажет.

**Бот жауап бермей қалса, ақша жоғала ма?** Жоқ. Төлем Kaspi-де өтеді, ақша шотыңызға түседі. Бот көтерілген соң webhook қайта жіберіледі — біз 2xx алмағанша 11 рет қайталаймыз.

**Топ чатта сатуға бола ма?** Ботты топқа қосуға болады, бірақ төлем сілтемесін жеке чатқа жіберген дұрыс: сілтемеде сома мен сипаттама көрінеді.

**Клиент ботта тұрып QR-ды сканерлей ала ма?** Өз телефонында бір экранда екеуін қатар істеу ыңғайсыз. Сондықтан ботта `payUrl` сілтемесін немесе `kind: "phone"` push-ын қолданған дұрыс, QR суретін қосымша нұсқа ретінде беріңіз.
