Қысқаша
Мобильді қосымша Qut Pay-ге тікелей жүгінбейді. Реті әрқашан төрт буын: қосымша → өз серверіңіз → Qut Pay → Kaspi. Себебі біреу: API кілт тек серверде болуы керек, APK немесе IPA файлдың ішінде емес — жиналған қосымшаны кез келген адам ашып, кілтті шығарып ала алады. Сервер счёт жасап, қосымшаға payUrl немесе deepLink қайтарады, қосымша соны ашады, төлем расталған соң серверге webhook келеді.
Жұмыс схемасы
| Қадам | Кім | Не істейді |
|---|---|---|
| 1 | Қосымша | Клиент «Төлеу» дейді, қосымша өз серверіне сұрау жібереді |
| 2 | Сіздің сервер | Тапсырысты тексереді, соманы өзі есептейді |
| 3 | Сіздің сервер | POST /api/v1/invoices — X-API-Key осы жерде ғана қолданылады |
| 4 | Сіздің сервер | Қосымшаға тек id мен payUrl (немесе deepLink) береді |
| 5 | Қосымша | Сілтемені ашады — Kaspi қосымшасы ашылады |
| 6 | Клиент | Kaspi-де растайды |
| 7 | Qut Pay | Сіздің серверге invoice.paid жібереді |
| 8 | Сіздің сервер | Тапсырысты жабады, қосымшаға push немесе күй арқылы хабарлайды |
Соманы қосымшадан алмаңыз. Клиент қосымшаның сұрауын өзгертіп, 100 теңге деп жіберуі мүмкін. Сервер соманы тапсырыстың өзінен есептеп шығаруы керек.
API кілт туралы
Бұл осы мақаладағы ең маңызды бөлім.
- Кілт ешқашан қосымшаның ішінде болмайды. APK мен IPA — жай архив, оны ашып қарауға болады. Кодта, ресурста,
strings.xml-де,Info.plist-те, обфускацияланған күйінде де сақтамаңыз. - Кілт сіздің сервердің айнымалы ортасында тұрады. Репозиторийге салмаңыз.
- Қосымша сіздің серверге өзінің авторизациясымен жүгінеді (клиент сессиясы, JWT — не қолдансаңыз да). Qut Pay кілті ол жерде мүлдем көрінбейді.
- Кілт сыртқа шығып кетсе, оны бірден кабинеттен жойып, жаңасын жасаңыз. Ескі кілт сол сәтте жұмысын тоқтатады.
- Кілтке қажет құқықтарды ғана беріңіз:
invoices:write,invoices:read, қайтару жасайтын болсаңызrefunds:write.
Қауіпсіздік туралы жалпы: Қосу қауіпсіз бе және сервис қалай құрылған.
Қандай API әдісі қолданылады
Серверде:
POST https://api.qut.kz/api/v1/invoices
X-API-Key: qp_live_…
Idempotency-Key: app-order-8841
{
"amount": 12900,
"kind": "qr",
"description": "Тапсырыс №8841",
"externalOrderId": "8841",
"successUrl": "https://menim-app.kz/pay/ok?order=8841",
"failUrl": "https://menim-app.kz/pay/fail?order=8841",
"metadata": { "platform": "ios", "user_id": "u_512" }
}
Қосымшаға жауаптың бәрін бермеңіз — тек id мен payUrl (немесе deepLink) жетеді.
Қалғаны: GET /api/v1/invoices/{id} — қосымша қайтып келгенде күйін сұрау, POST /api/v1/invoices/{id}/cancel, POST /api/v1/invoices/{id}/refund.
Клиенттің нөмірін білсеңіз (қосымшада тіркелген болса), kind: "phone" де жарайды: Kaspi-іне push келеді, customer.phone 7XXXXXXXXXX пішімінде, сома бүтін теңге, сипаттама 60 таңба.
payUrl ашылғанда не болады
payUrl сілтемесін қосымшадан ашқанда телефонда Kaspi қосымшасы ашылады да, клиент төлемді сол жерде растайды. Сізге керек екі нәрсе:
1. Клиент қайтып келгенде күйді тексеріңіз. iOS пен Android-та қосымшаға қайта кірген сәтті ұстап (applicationDidBecomeActive, onResume), серверден тапсырыстың күйін сұраңыз. Клиент төлемей де қайтып келуі мүмкін — сондықтан «қайтып келді» деген өздігінен «төледі» дегенді білдірмейді.
2. Ақиқат көзі — webhook. Тапсырысты «төленді» етуді тек серверде, invoice.paid оқиғасы бойынша жасаңыз. Қосымшаның сөзіне сенбеңіз: оны өзгертуге болады.
Екеуін қатар жүргізген дұрыс: webhook — негізгісі, қосымшадағы сұрау — интерфейсті жылдам жаңарту үшін.
successUrl мен failUrl тек http(s) болады. Оларды өз домендеріңізге бағыттап, ол беттерден қосымшаға қайтаратын сілтеме қойған ыңғайлы.
App Store мен Play Market ережесі
Бұл техникалық емес, дүкендік шектеу, бірақ модерациядан өтуіңіз соған байланысты.
| Не сатылады | Сыртқы төлем |
|---|---|
| Цифрлық тауар: жазылым, премиум қолжетімділік, ойын монетасы, қосымша ішіндегі мазмұн | Рұқсат емес — дүкеннің өз төлемін қолдану керек |
| Нақты тауар: киім, тағам, кітап, дүкеннен сатып алу | Рұқсат |
| Жеткізу, курьер, такси | Рұқсат |
| Қызмет ақысы: жөндеу, консультация, оқу, салон | Рұқсат |
| Брондау: үстел, нөмір, билет, уақыт | Рұқсат |
Яғни Qut Pay арқылы төлемді нақты тауар мен қызметке қосуға болады, ал қосымшаның ішіндегі цифрлық мазмұнға болмайды. Дүкендердің ережелері өзгеріп отырады — жариялар алдында ағымдағы редакциясын өзіңіз тексеріп алыңыз.
Ерекше ескертулер
- Идемпоттылық. Мобильді желі нашар, сұрау қайталанып жіберілуі қалыпты жағдай.
Idempotency-Keyтақырыбына тапсырыс нөмірін беріңіз — сол кілтпен қайталасаңыз жаңа счёт жасалмай, бұрынғысы қайтады. - QR-дың сканерлеу терезесі шамамен үш минут. Қосымшада таймер көрсетіңіз және «Жаңа счёт» батырмасын беріңіз. Уақытты кодқа жазбай,
expiresAtөрісінен алыңыз. - Кеш төлем. Мерзімі біткен счётқа ақша келсе, оқиға
late: trueбелгісімен келеді. Тапсырысты автоматты жауып тастамаңыз. - Webhook идемпотентті болсын —
(invoice.id, status)жұбы бойынша бір рет өңдеңіз. - Алдымен sandbox.
qp_test_…кілтімен бүкіл тізбекті өткізіңіз: счёт →simulate→ webhook → қосымшада күй жаңарды. - 401 қатесі шықса, ең жиі себебі — режим сәйкессіздігі немесе қате тақырып: API 401 қайтарады.
Жиі қойылатын сұрақтар
Серверім жоқ, тек қосымша. Қалай болады? Онда алдымен шағын сервер керек — оның бар жұмысы счёт жасау және webhook қабылдау. Бұл бірнеше эндпоинт қана. Сервер болмаса, кілт міндетті түрде қосымшаның ішіне түседі, ал бұл жарамайды.
Кілтті обфускация жасап қойсам ше? Жарамайды. Обфускация кілтті жасырмайды, тек іздеуді қиындатады. Жиналған қосымшаның трафигін көру де жеткілікті.
Қосымшада QR суретін көрсетуім керек пе? Бір телефонда QR-ды сканерлеу ыңғайсыз. payUrl немесе deepLink арқылы Kaspi-ді ашқан дұрыс. QR — басқа адамның телефонынан төлейтін жағдайға.
Клиент төлеп, қосымша жабылып қалса? Ештеңе жоғалмайды. Ақша Kaspi-де өтеді, webhook серверге келеді, тапсырыс жабылады. Клиент қосымшаны қайта ашқанда дайын күйді көреді.
Веб-нұсқасы да бар, екеуіне бір кілт бола ма? Болады, бірақ бөлек кілт жасаған ыңғайлырақ: біреуі шығып кетсе, тек соны жойып, екіншісін тимей қалдырасыз.