# Мобильді қосымшаға қосу

> Дұрыс реті: қосымша → өз серверіңіз → Qut Pay → Kaspi. API кілт APK немесе IPA ішінде болмауы керек. payUrl ашылғанда не болады және App Store мен Play Market ережесі.

## Қысқаша

Мобильді қосымша 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`.

Қауіпсіздік туралы жалпы: [Қосу қауіпсіз бе және сервис қалай құрылған](/kb/is-it-safe).

## Қандай 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 қайтарады](/kb/api-401).

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

**Серверім жоқ, тек қосымша. Қалай болады?** Онда алдымен шағын сервер керек — оның бар жұмысы счёт жасау және webhook қабылдау. Бұл бірнеше эндпоинт қана. Сервер болмаса, кілт міндетті түрде қосымшаның ішіне түседі, ал бұл жарамайды.

**Кілтті обфускация жасап қойсам ше?** Жарамайды. Обфускация кілтті жасырмайды, тек іздеуді қиындатады. Жиналған қосымшаның трафигін көру де жеткілікті.

**Қосымшада QR суретін көрсетуім керек пе?** Бір телефонда QR-ды сканерлеу ыңғайсыз. `payUrl` немесе `deepLink` арқылы Kaspi-ді ашқан дұрыс. QR — басқа адамның телефонынан төлейтін жағдайға.

**Клиент төлеп, қосымша жабылып қалса?** Ештеңе жоғалмайды. Ақша Kaspi-де өтеді, webhook серверге келеді, тапсырыс жабылады. Клиент қосымшаны қайта ашқанда дайын күйді көреді.

**Веб-нұсқасы да бар, екеуіне бір кілт бола ма?** Болады, бірақ бөлек кілт жасаған ыңғайлырақ: біреуі шығып кетсе, тек соны жойып, екіншісін тимей қалдырасыз.
