Коротко
В онлайн-школе всё сводится к одной цепочке: счёт → оплата → webhook → доступ. Ученик выбирает курс, вы создаёте счёт, он платит через Kaspi, вам приходит invoice.paid, и ваша платформа в тот же момент открывает курс. Для периодических платежей есть подписки — они выставляют счёт по расписанию, но деньги сами не списываются: каждую оплату ученик подтверждает в Kaspi лично.
Базовый сценарий: покупка доступа
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" — push придёт прямо в Kaspi, но учтите, что description там не длиннее 60 символов.
Открытие доступа по webhook
Это самая ответственная часть. Порядок такой:
- В кабинете, в разделе Интеграции, добавляете адрес webhook. В бою принимается только
httpsи реальный домен: IP и адреса туннелей не подойдут. - Секрет показывается один раз — сохраните его.
- Адрес должен быть открыт без авторизации. Редиректы на другой адрес не отслеживаются.
- Проверяете подпись:
HMAC-SHA256(secret, timestamp + "." + rawBody), hex, с префиксомsha256=. Тело — в неизменном байтовом виде, до разбора JSON.timestampстарше 5 минут принимать не надо. - По
invoice.paidоткрываете курс, находя ученика поmetadata.user_idиmetadata.course_id. - Отвечаете 2xx. Если не ответили — доставка повторится до 11 раз (с нарастанием от 10 секунд до часа).
Обработчик должен быть идемпотентным: храните пару (invoice.id, status) и игнорируйте повторы. Иначе одному ученику уйдёт одиннадцать писем «добро пожаловать».
Подробнее: Настройка вебхуков, Безопасность вебхуков, Вебхук не приходит.
Периодические платежи
Ежемесячная подписка, оплата обучения траншами, формат «курс + сопровождение» — всё это подписки.
- Интервал:
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 }.
Закрывать доступ решаете вы. Если подписка не оплачена, курс мы не закрываем — мы лишь выставили счёт и сообщили, что он не оплачен. Ваша платформа видит неоплаченный период и сама решает, закрыть доступ или оставить.
Подробнее: Бизнес по подписке.
Групповые тарифы и скидки
Скидки. Сумму считаете вы. Промокод, цена раннего бронирования, социальная скидка — всё это решается на вашей стороне, а в счёт уходит уже готовая сумма. Qut Pay скидки не считает, системы промокодов у нас нет.
Групповой тариф. Компания отправляет сотрудников на обучение или группа родителей записывается вместе — есть два пути:
| Путь | Когда удобно | Как |
|---|---|---|
| Один общий счёт | Платит компания | Один счёт на всю сумму, список в metadata |
| Отдельный счёт каждому | Платит каждый сам | Массовое создание: POST /invoices/bulk, от 1 до 100 за запрос |
При массовом создании каждый элемент проверяется отдельно: ошибка в одном не мешает остальным создаться. Разберите ответ поэлементно и переотправьте только упавшие.
Если у школы есть мобильное приложение
Здесь нужна осторожность. По правилам App Store и Google Play цифровые товары нельзя продавать через внешнюю оплату. Доступ к курсу, подписка, премиум внутри платформы — это цифровые товары. Реальный товар, доставка, офлайн-услуга и бронирование — можно.
На практике большинство онлайн-школ делает так: оплата происходит на сайте, в браузере, а приложение показывает уже открытый доступ. Ссылка на оплату прямо из приложения тоже может нарушать правила магазинов — прочитайте их сами и оцените собственный риск.
Техническая сторона отдельная: приложение не обращается к нам напрямую. Порядок такой: приложение → ваш сервер → Qut Pay → Kaspi. API-ключ должен жить только на сервере, класть его в APK или IPA нельзя. Подробнее: Приём оплаты в мобильном приложении.
Вариант без кода
- Постоянные ссылки на оплату. По ссылке на каждый курс:
qut.kz/p/<slug>. Их кладут в профиль Instagram, в Telegram-канал, на лендинг. Оплату видите в кабинете и открываете доступ вручную: Ссылки на оплату. - Tilda и любые формы. Счёт из формы записи выставляется через хук формы: Tilda и любые формы.
- Если сайт на WordPress, есть плагин: Плагин WooCommerce.
- Продажа через Telegram-бота. Курс продаётся в боте, а после оплаты бот отправляет приглашение в закрытый канал: Продажи через Telegram-бота.
- n8n. Есть готовые сценарии на создание счёта и приём webhook.
Что учесть заранее
- Доступ открывает только
invoice.paid.invoice.createdиinvoice.pendingоплатой не являются. - Поздние оплаты. Деньги могут прийти уже после того, как счёт стал
expired, иinvoice.paidпридёт с пометкойlate: true. Либо открываете курс, либо возвращаете деньги: Поздняя оплата. - Не кладите API-ключ во фронтенд. Только сервер. Ключ в браузерном JS считайте утёкшим: API-ключ утёк.
- Готовьтесь к пикам продаж. В день открытия потока число счетов вырастает в разы. Месячный лимит и суточная защита — это разные вещи: Тарифы и лимиты.
- Заранее объясните ученикам свою политику возврата. Технически доступен и полный, и частичный: API возвратов.
Вопросы и ответы
Курс откроется сразу после оплаты? Да, webhook invoice.paid обычно приходит в пределах 5 секунд, и платформа открывает доступ в тот же момент.
Ученик оплатил, а доступ не открылся. Что проверять? Сначала журнал webhook: доступен ли адрес, ответили ли вы 2xx, сошлась ли подпись. Порядок проверки: Вебхук не приходит.
Можно сделать промокоды? На стороне Qut Pay системы промокодов нет. Скидку считает ваша платформа и передаёт в счёт готовую сумму.
Если подписка не оплачена, вы закроете доступ к курсу? Нет. Мы выставляем счёт и сообщаем его статус, а закрывать доступ или нет — решает ваша платформа.
Можно принимать оплату прямо в приложении? Технически да, но через ваш сервер. При этом правила App Store и Google Play запрещают продавать цифровые товары через внешнюю оплату, поэтому для онлайн-курсов безопаснее выносить оплату в браузер: Приём оплаты в мобильном приложении.