# Онлайн мектеп пен курс платформасына

> Курсқа кіруді сатудан бастап, webhook арқылы қолжетімділікті ашуға, кезеңдік төлемге, топтық тариф пен жеңілдікке дейін. Мобильді қосымшаңыз болса App Store мен Play ережесін де қараңыз.

## Қысқаша

Онлайн мектепте бүкіл жұмыс бір тізбекке құрылады: **счёт → төлем → webhook → қолжетімділік**. Оқушы курсты таңдайды, сіз счёт жасайсыз, ол Kaspi арқылы төлейді, сізге `invoice.paid` келеді де, сіздің платформаңыз сол сәтте курсты ашады. Кезеңдік төлем үшін [жазылым](/kb/subscriptions-api) бар, ол счётты кестеге сай өзі шығарады — бірақ ақша өздігінен алынбайды, әр төлемді оқушы Kaspi-де өзі растайды.

## Негізгі сценарий: курсқа кіру

```json
POST /api/v1/invoices
{
  "amount": 39000,
  "kind": "qr",
  "description": "«Python негіздері» курсы, 2 ағым",
  "externalOrderId": "ENR-8842",
  "customer": { "name": "Айдана", "email": "aidana@example.kz" },
  "successUrl": "https://school.kz/enroll/8842/done",
  "failUrl": "https://school.kz/enroll/8842/retry",
  "metadata": { "user_id": "3311", "course_id": "py-basics", "cohort": "2" }
}
```

- `successUrl` мен `failUrl` — тек http(s). Клиент төлегеннен кейін сол бетке қайтады.
- **`successUrl`-ды қолжетімділік ашудың белгісі деп санамаңыз.** Ол жай ғана бет. Клиент оны қолмен ашып қоя алады, ал шынайы төлем расталуы webhook арқылы келеді.
- `metadata` ішінде `user_id` мен `course_id` болсын — webhook қайтқанда кімге не ашатыныңызды сол арқылы табасыз.
- `Idempotency-Key` тақырыбын жіберіңіз: оқушы «Төлеу» батырмасын екі рет басса, екі счёт жасалмайды.

Оқушы сайтта отырса `kind: "qr"` ыңғайлы: беттегі QR-ды телефонымен сканерлейді немесе `payUrl` арқылы өтеді. Ал WhatsApp пен Instagram-да сөйлесіп жатсаңыз, `kind: "phone"` ыңғайлырақ — Kaspi-іне бірден push келеді, `description` онда 60 таңбадан аспайды.

## Webhook арқылы қолжетімділікті ашу

Бұл ең маңызды бөлігі. Реті:

1. Кабинет → **Интеграциялар** бөлімінде webhook адресін қосасыз. Продакшенде тек `https` және нақты домен: IP мен туннель адресі қабылданбайды.
2. Құпия бір рет көрсетіледі — сақтап қойыңыз.
3. Адресіңіз авторизациясыз ашық болуы керек. Басқа жерге апаратын қайта бағыттау ұсталмайды.
4. Қолтаңбаны тексересіз: `HMAC-SHA256(secret, timestamp + "." + rawBody)`, hex, `sha256=` префиксімен. **Денені өзгертілмеген байт күйінде** тексеріңіз, JSON-ға айналдырғанға дейін. 5 минуттан ескі `timestamp` қабылданбасын.
5. `invoice.paid` келгенде `metadata.user_id` мен `metadata.course_id` бойынша курсты ашасыз.
6. 2xx қайтарасыз. Қайтармасаңыз жеткізу **11 рет қайталанады** (10 секундтан бір сағатқа дейін өсіп).

Өңдеуіңіз идемпотентті болсын: `(invoice.id, status)` жұбын сақтап, қайталанғанын елеңіз. Әйтпесе бір оқушыға он бір рет «қош келдіңіз» хаты кетеді.

Толығырақ: [Webhook баптау](/kb/webhook-setup), [Webhook қауіпсіздігі](/kb/webhook-security), [Webhook келмей жатыр](/kb/webhook-not-arriving).

## Кезеңдік төлем

Ай сайынғы жазылым, транш бойынша оқу ақысы, «курс + қолдау» форматы — бәрі [жазылым](/kb/subscriptions-api) арқылы.

- **Аралық**: `month` (әдепкі жағдай), `week`, `day`; кратность `every`.
- **Қайталау сатысы** `retryDelaysMin`, әдепкі `[15, 60, 360]` минут, ең көбі 5 мән. Оқушы счётты бірден көрмесе, қайта шығады.
- Саты біткенде сол кезең тасталады, кесте келесі кезеңге жалғасады. `failedRuns`, `lastError`, `lastRunStatus` өрістері бар.
- **Өткізіп алу саясаты** `misfirePolicy`: `run_once` (әдепкі) немесе `skip`, `misfireAfterMin` әдепкі 1440.
- Оқушы демалысқа шықса — `pause`, қайтып келгенде `resume`. Өткен кезекті бірден жіберу керек болса, `POST /subscriptions/{id}/resume` `{ catchUp: true }`.

**Қолжетімділікті жабуды өзіңіз шешесіз.** Жазылым төленбесе, біз курсты жаппаймыз — біз тек счёт шығардық және ол төленбегенін хабарлаймыз. Сіздің платформаңыз «төленбеген кезең» дегенді көріп, қолжетімділікті жабады немесе қалдырады.

Толығырақ: [Жазылымдық бизнеске](/kb/for-subscription-business).

## Топтық тариф және жеңілдік

**Жеңілдік.** Соманы сіз есептейсіз. Промокод, ерте тіркелу бағасы, әлеуметтік жеңілдік — бәрі сіздің жағыңызда шешіледі, счётқа дайын соманы бересіз. Qut Pay жеңілдік санамайды, промокод жүйесі жоқ.

**Топтық тариф.** Компания қызметкерлерін оқуға жіберсе немесе ата-аналар тобы бірге тіркелсе, екі жол бар:

| Жол | Қашан ыңғайлы | Қалай |
|---|---|---|
| Бір ортақ счёт | Компания өзі төлейді | Бір счёт, толық сома, `metadata` ішінде тізім |
| Әрқайсысына бөлек счёт | Әркім өзі төлейді | [Топтап счёт](/kb/bulk-invoices): `POST /invoices/bulk`, бір сұрауда 1-100 |

Топтап шығарғанда әр элемент бөлек тексеріледі: біреуі қате болса, қалғандары жасала береді. Жауапты элемент бойынша қарап шығып, құлағандарын қайта жіберіңіз.

## Мобильді қосымшаңыз болса

Мұнда абай болу керек. **App Store мен Play Market ережесі бойынша цифрлық тауарды сыртқы төлеммен сатуға болмайды.** Курсқа кіру, жазылым, платформа ішіндегі премиум — бұлар цифрлық тауар. Ал нақты тауар, жеткізу, офлайн қызмет ақысы, брондау — рұқсат.

Іс жүзінде онлайн мектептің көбі мына жолмен жүреді: **төлем сайтта (браузерде) жасалады**, қосымша тек дайын қолжетімділікті көрсетеді. Қосымшаның ішінен төлеуге сілтеме қою да дүкендердің ережесіне қайшы келуі мүмкін — ережені өзіңіз оқып, өз тәуекеліңізді бағалаңыз.

Техникалық жағы бөлек: қосымша бізге **тікелей жүгінбейді**. Реті — қосымша → өз серверіңіз → Qut Pay → Kaspi. API кілт тек серверде болуы керек, APK немесе IPA ішіне салуға болмайды. Толығырақ: [Мобильді қосымшаға қосу](/kb/for-mobile-app).

## Кодсыз нұсқасы

- **Тұрақты төлем сілтемелері.** Әр курсқа бір сілтеме: `qut.kz/p/<slug>`. Instagram профиліне, Telegram арнасына, лендингке қоясыз. Төлемді кабинеттен көріп, қолжетімділікті қолмен ашасыз: [Төлем сілтемелері](/kb/payment-links).
- **Tilda немесе кез келген форма.** Тіркелу формасынан счёт шығару форма-хук арқылы жасалады: [Tilda және кез келген форма](/kb/tilda-forms).
- **WordPress сайты болса** WooCommerce плагині бар: [WooCommerce плагині](/kb/woocommerce).
- **Telegram бот арқылы сату.** Курсты ботта сатып, төлемнен кейін жабық арнаға шақыру жіберуге болады: [Telegram ботында тауар сату](/kb/for-telegram-bot).
- **n8n.** Счёт жасау мен webhook қабылдаудың дайын workflow-лары бар.

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

- **Қолжетімділікті тек `invoice.paid` ашсын.** `invoice.created` мен `invoice.pending` — төлем емес.
- **Кеш келген төлем.** Счёт `expired` болғаннан кейін де ақша келіп, `invoice.paid` `late: true` белгісімен келуі мүмкін. Курсты ашасыз немесе ақшаны қайтарасыз: [Кеш келген төлем](/kb/late-payment).
- **API кілтті фронтендке салмаңыз.** Ол тек серверде. Браузерде орындалатын JS-те кілт болса, ол шықты деп есептеңіз: [API кілт сыртқа шығып кетті](/kb/leaked-key).
- **Сатылымның шыңына дайын болыңыз.** Ағым ашылған күні счёт саны бірнеше есе өседі. Айлық лимит пен тәуліктік қорғаныс — екі бөлек нәрсе: [Тарифтер және лимиттер](/kb/tariff-limits).
- **Оқушыны қайтару саясатыңызбен алдын ала таныстырыңыз.** Техникалық жағынан толық та, ішінара да қайтара аласыз: [Қайтару API](/kb/refunds-api).

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

**Төлемнен кейін курс бірден ашыла ма?** Иә, `invoice.paid` webhook әдетте 5 секунд ішінде келеді, платформаңыз сол сәтте ашады.

**Оқушы төледі, бірақ курс ашылмады. Не тексеремін?** Алдымен webhook журналын: адрес қолжетімді ме, 2xx қайтардыңыз ба, қолтаңба сәйкес келді ме. Реті: [Webhook келмей жатыр](/kb/webhook-not-arriving).

**Промокод жасауға бола ма?** Qut Pay жағында промокод жүйесі жоқ. Жеңілдікті өз платформаңызда есептеп, счётқа дайын соманы бересіз.

**Жазылым төленбесе, сіздер курсты жабасыздар ма?** Жоқ. Біз счёт шығарамыз және оның күйін хабарлаймыз, ал қолжетімділікті жабу-жаппауды сіздің платформаңыз шешеді.

**Қосымшамнан төлем қабылдауға бола ма?** Техникалық жағынан — иә, бірақ қосымша сервер арқылы жүреді. Ал App Store мен Play ережесі бойынша цифрлық тауарды сыртқы төлеммен сатуға болмайды, сондықтан онлайн курстарда төлемді браузерге шығарған қауіпсіз: [Мобильді қосымшаға қосу](/kb/for-mobile-app).
