Қысқаша
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:
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:
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береді. Екеуі екі басқа нәрсе: Қай тарифті таңдау керек.
Бірнеше бренд бір ботта
Бір ботта бірнеше дүкен немесе бірнеше бағыт сатсаңыз, екі жолы бар.
Бір ұйым, бөлек белгі. Барлық ақша бір Kaspi шотына түседі. Брендті metadata ішіне жазасыз ({"brand": "shop_a"}) және есепті сол бойынша бөлесіз. Ең қарапайымы.
Бөлек кілттер. Әр бағытқа бөлек API кілт жасайсыз — сонда счёттарды көзі бойынша сүзу оңай. Кілтті нақты бір кассирге байлауға да болады: байланған кілт тек сол кассирдің счёттарын көреді, басқасына 404 береді. Бірнеше кассир туралы: Бірнеше кассир қосуға бола ма.
Ақша әртүрлі шотқа түсуі керек болса — бұл бөлек ұйым деген сөз, әр ұйымның өз кассирі мен өз тарифі болады: Бір аккаунтта бірнеше ұйым.
Ерекше ескертулер
- Бот токені мен API кілтті шатастырмаңыз. Екеуі де серверде, бірақ API кілт Telegram-ға ешқашан берілмейді.
- Кеш төлем. Мерзімі біткен счётқа ақша келсе,
invoice.paidоқиғасыlate: trueбелгісімен келеді. Ботта мұны өңдеңіз: не тауарды беріңіз, не қайтарыңыз. - Webhook идемпотентті болсын. Бір оқиға қайталанып келуі мүмкін,
(invoice.id, status)жұбы бойынша бір рет өңдеңіз. Әйтпесе клиент тауарды екі рет алады. - Біздің өз ботымыз бар. Ол сату боты емес, бақылау боты:
/invoice,/today,/last,/status,/cancel,/support. Оны қатар қосып қойсаңыз, счёттарды телефоннан көріп отырасыз: Telegram нұсқаулығы. - Sandbox-та бүкіл циклді өткізіңіз: счёт →
simulate→ webhook → бот тауарды берді.
Жиі қойылатын сұрақтар
API кілтті бот кодына жазуға бола ма? Жоқ. Бот пен кілт бір серверде тұрса да, кілтті айнымалы ортадан оқыңыз, репозиторийге салмаңыз. Кілт сыртқа шықса бірден жойып, жаңасын жасаңыз.
Клиент нөмірін білмесем? QR счёт жасаңыз — ол үшін нөмір керек емес. Телефонға push тек kind: "phone" үшін қажет.
Бот жауап бермей қалса, ақша жоғала ма? Жоқ. Төлем Kaspi-де өтеді, ақша шотыңызға түседі. Бот көтерілген соң webhook қайта жіберіледі — біз 2xx алмағанша 11 рет қайталаймыз.
Топ чатта сатуға бола ма? Ботты топқа қосуға болады, бірақ төлем сілтемесін жеке чатқа жіберген дұрыс: сілтемеде сома мен сипаттама көрінеді.
Клиент ботта тұрып QR-ды сканерлей ала ма? Өз телефонында бір экранда екеуін қатар істеу ыңғайсыз. Сондықтан ботта payUrl сілтемесін немесе kind: "phone" push-ын қолданған дұрыс, QR суретін қосымша нұсқа ретінде беріңіз.