> Источник: https://apipay.kz/guides/podpiski-apipay · Обновлено: 2026-08-30 · apipay.kz
> ApiPay — независимый сервис приёма платежей поверх вашего Kaspi Pay.

# Подписки ApiPay: есть ли автосписание с покупателя?

**TL;DR.** Автосписания нет: оплата по номеру в Kaspi всегда подтверждается самим покупателем — сервис не может списать деньги без его подтверждения (списание без подтверждения существует только для карт в классическом эквайринге). Подписка ApiPay — это **авто-выставление**: по расписанию (например, каждое 1-е число) система сама создаёт обычный счёт Kaspi, покупатель получает push и нажимает «Оплатить». Не оплатил — система сама перевыставляет счёт (по умолчанию до 3 попыток раз в 24 часа), затем даёт льготный grace-период (по умолчанию 3 дня). Если и он истёк — подписка переходит в `expired` навсегда: реактивации нет, создаётся новая подписка.

## Как работает подписка

Цикл выглядит так:

1. Вы создаёте подписку: номер телефона покупателя + период + сумма.
2. В расчётную дату система сама создаёт **обычный счёт Kaspi** — покупателю приходит push в приложение Kaspi.
3. Покупатель нажимает «Оплатить» — деньги, как всегда, идут напрямую на ваш Kaspi-счёт. Вам приходит вебхук `subscription.payment_succeeded`.
4. Дата следующего выставления сдвигается на период — и так по кругу.

## Создание подписки через API

```bash
curl -X POST "https://api.apipay.kz/api/v1/subscriptions" \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "87001234567",
    "amount": 15000,
    "billing_period": "monthly",
    "billing_day": 1,
    "billing_time": "13:00",
    "total_cycles": 12,
    "description": "Абонемент, тариф Стандарт"
  }'
```

Параметры и границы: `billing_period` — `daily | weekly | biweekly | monthly | quarterly | yearly`; `amount` — от 100 до 1 000 000 ₸ и **только целые тенге** (дробную сумму создание подписки примет, но каждое списание уйдёт в `error` с `amount_must_be_whole_tenge`); `max_retry_attempts` — 1–10 (по умолчанию 3); `retry_interval_hours` — 1–168 (по умолчанию 24); `grace_period_days` — 1–30 (по умолчанию 3); `description` — до 60 символов (Kaspi показывает покупателю только их); `bill_immediately` — выставить первый счёт сразу при создании.

### Когда именно выставляется счёт

`billing_day` — 1–28 у месячного, квартального и годового периода; у `weekly` и `biweekly` это **день недели**: 1 — понедельник, 7 — воскресенье (значение больше 7 на этих периодах вернёт `422`). У `daily` поле не используется.

Числом больше 28 день не задаётся: 29-е, 30-е и 31-е есть не в каждом месяце. Если нужен конец месяца, передайте `billing_day_from_end` вместо `billing_day` — `0` означает последний день месяца, `1` — предпоследний. Поля взаимоисключающие: вместе они вернут `422`, а сама опора от конца месяца доступна только месячному, квартальному и годовому периоду. В кабинете обе опоры выбираются одним списком, словами: числа с 1-го по 27-е, «предпоследний день месяца» и «последний день месяца». 28-е число доступно только через API.

`billing_time` — час выставления в формате `ЧЧ:ММ` **по времени Алматы**, окно с 06:00 до 22:00. Не передан — счёт уходит в 13:00.

`first_billing_at` (`ГГГГ-ММ-ДД`, календарь Алматы) задаёт дату **первого** счёта, дальше подписка идёт по расписанию. Поле несовместимо с `bill_immediately`: вместе они вернут `422`. Дата не может быть в прошлом и дальше двух лет вперёд — иначе `422`. Без него первый счёт считается как «дата начала + период».

`total_cycles` (1–600) ограничивает подписку по числу **оплат**: неоплаченная попытка цикл не расходует. Сколько уже внесено, показывает `cycles_paid`. Поле не передано — подписка бессрочна. Когда оплаты закончились, подписка переходит в `expired`, а в вебхуке `subscription.expired` приходит `reason: total_cycles_reached`.

В ответе подписки `next_billing_in_days` — **знаковое**: отрицательное значение означает, что дата списания уже прошла.

**Важно:** ответ `201` на `POST /subscriptions` означает «подписка создана», а не «покупателю уже ушёл счёт». Если вы ждёте немедленный push покупателю — передайте `bill_immediately: true`. Управление: `POST /subscriptions/{id}/pause | resume | cancel`, счета подписки — `GET /subscriptions/{id}/invoices`.

Подпискам нужна верифицированная организация: без неё API отвечает `400 Subscriptions require a verified organization`.

## Что происходит, когда покупатель не платит?

1. **Счёт не оплачен** (истёк через 24 часа или отменён) → вам приходит `subscription.payment_failed` с причиной (`Invoice expired` / `Invoice cancelled`) и номером попытки.
2. **Система сама перевыставляет счёт** — по умолчанию до 3 попыток с интервалом `retry_interval_hours` (по умолчанию 24 ч). Исключение одно: если счёт **истёк**, он перевыставляется сразу — покупатель уже израсходовал весь срок его жизни, и ждать сверх этого нечего. Вручную ничего пересоздавать не нужно (и не надо: получите два параллельных счёта).
3. **Попытки исчерпаны** → начинается **grace-период** (`subscription.grace_period_started`, по умолчанию 3 дня): новые счета в это время не выставляются, доступ покупателю вы пока не отключаете, а любая его оплата немедленно возвращает подписку в норму.
4. **Grace истёк** → `subscription.expired`: биллинг по этой подписке остановлен **навсегда**.

Отдельно от этой лестницы стоит **явный отказ покупателя**. Если он сам отклонил счёт в Kaspi, подписка отменяется сразу: приходит `subscription.cancelled` с `reason: payer_refused`, ретраев и grace не будет. Нехватка денег на счёте отказом **не** считается — там идут обычные повторы. Отменённую так подписку возобновить нельзя, как и после `cancel`: если покупатель передумал, создайте новую.

После `expired` реактивации не существует — ни кнопки, ни метода API. Покупатель вернулся через месяц? Создайте **новую** подписку.

Отдельная причина тишины — ваш собственный тариф ApiPay. Если тариф не оплачен (`403 tariff_inactive`) или включено ограничение по дневному лимиту (`429 tariff_limit_reached`), автосписание пропускает цикл молча: счёт покупателю не выставляется, дата следующего списания не сдвигается, счётчик неудачных попыток не растёт, вебхука об этом нет. См. [Лимит счетов по тарифу](/guides/limit-schetov-po-tarifu).

**Пропущенные за простой периоды сгорают.** Когда причина уходит, подписка выставляет один счёт за текущий период и встаёт на ближайшую будущую дату — покупателю не приходит пачка счетов за всё время тишины.

Если списания стояли **дольше недели**, сами они не возобновятся: в кабинете, в разделе «Автоплатежи», появится кнопка «Запустить подписки» — нажать её должен человек. Там же кабинет называет причину остановки, если она ещё не устранена.

Пауза устроена мягче: `pause` останавливает выставление, `resume` продолжает **от текущего момента** — пропущенные периоды не доначисляются, покупателю не прилетит «счёт за три месяца тишины». `cancel` — безвозвратен, как и `expired`.

## События subscription.* для интеграции

Вебхуки подписок приходят на тот же URL, что и счета (настройка — «[Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay)»):

| Событие | Когда | Доп. поля (в корне payload) |
|---|---|---|
| `subscription.created` | Подписка создана | — |
| `subscription.payment_succeeded` | Счёт подписки оплачен | `invoice_id`, `amount`, `paid_at` |
| `subscription.payment_failed` | Счёт истёк/отменён | `invoice_id`, `amount`, `reason`, `attempt_number` |
| `subscription.grace_period_started` | Ретраи исчерпаны | `grace_period_days`, `expires_at` |
| `subscription.expired` | Grace истёк либо закончились оплаты `total_cycles` | во втором случае `reason: total_cycles_reached` |
| `subscription.cancelled` | Отмена: вашим запросом либо явным отказом покупателя | при отказе покупателя — `reason: payer_refused`, `invoice_id` |
| `subscription.paused` / `resumed` | Соответствующие действия | — |

Три инженерных нюанса (трек D):

- **Дедупликация обязательна:** у событий подписок нет защиты от дублей — дедуплицируйте по `(event, subscription.id, invoice_id)`.
- **Ретраев в логах не ищите:** события `subscription.*` не попадают в webhook-логи кабинета и не имеют ручного перезапуска — отвечайте `200` быстро и обрабатывайте асинхронно.
- **Счёт подписки в статусе `error`** — это тоже неуспешная попытка: приходит `subscription.payment_failed`, в payload добавляется `error_code`. Если ошибка на стороне сервиса (например, оборвалась Kaspi-сессия), попытка не засчитывается и период сохраняется — следующая попытка будет позже. Если причина в плательщике (`client_not_found` — у номера нет Kaspi), идут обычные ретраи и затем grace.
- **Позиция каталога, создание которой брошено, ломает списание.** Если в корзине подписки стоит позиция с `sellable: false` (`status: failed` при `operation: create`), счёт не выставится, и прогон засчитается **неудачным**: `failed_attempts` растёт, дальше ретраи, grace и `expired`. Это не отсрочка — в отличие от неоплаченного тарифа, где цикл просто пропускается. Позицию нужно починить до исчерпания `max_retry_attempts`: работу по строке возобновляет `PATCH /catalog/{id}`, в кабинете — кнопка «Повторить».

## Вопросы и ответы

**Почему нельзя сделать настоящее автосписание?**
Оплата по номеру телефона в Kaspi всегда подтверждается самим покупателем: списание без подтверждения доступно только в карточном эквайринге с токенизацией карты. Клиентам это честно формулируется так: «счёт будет приходить автоматически, оплата в один клик».

**Можно ли изменить сумму действующей подписки?**
Да, через `PUT /subscriptions/{id}` — изменения применяются к будущим счетам. Тем же методом меняются день и час списания и число оплат; дата первого счёта и `bill_immediately` задаются только при создании.

**Во сколько покупателю приходит счёт по подписке?**
По умолчанию счета уходят в 13:00 по времени Алматы. Час выбирается полем `billing_time` в окне с 06:00 до 22:00.

**Подписка умерла (`expired`) — я потеряю историю?**
Нет: подписка и все её счета остаются в кабинете и API. Остановлен только будущий биллинг — для продолжения создайте новую подписку.

Смотрите также: [Как создать счёт Kaspi по номеру](/guides/kak-sozdat-schet-kaspi-po-nomeru) · [Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay) · Тарифы и комиссия ApiPay · [Рекуррентные платежи Kaspi](/recurring-payments-kaspi) · пиллар «[Как принимать оплату Kaspi через API](/kaspi-api)».

---

ApiPay — независимый сервис и не аффилирован с АО «Kaspi Bank». Kaspi и Kaspi Pay — товарные знаки их правообладателя.
База знаний: https://apipay.kz/guides
