Skip to main content

Обзор

Подарочные подписки позволяют пользователям Cabinet покупать VPN-подписки и дарить их другим людям. Начиная с v3.29.0 (бот) и v1.30.0 (Cabinet) основной сценарий — покупка по коду: покупатель получает уникальный подарочный код, который можно передать любым способом. Получатель активирует код в боте или в Cabinet. Прежний способ — указание получателя при покупке (email / Telegram username) — по-прежнему поддерживается.

Возможности

  • Код-подарок — покупка без указания получателя, передача кода любым удобным способом
  • Активация кода через deep link бота (t.me/bot?start=GIFT_XXXXXXXXXXXX) или в Cabinet (вкладка «Активировать»)
  • Отправка подписки по email или Telegram username (классический способ)
  • Оплата с баланса (мгновенно) или через платёжную систему
  • Выбор тарифа и периода подписки
  • Персональное сообщение для получателя (до 1000 символов)
  • 3 вкладки в разделе подарков: Купить, Активировать, Мои подарки
  • Защита от активации собственного кода
  • Rate limiting: 5 подарков в минуту
Для работы подарочных подписок необходимо:
  1. CABINET_ENABLED=true
  2. Функция включена через админ-панель (переключатель CABINET_GIFT_ENABLED)
  3. Хотя бы одна платёжная система подключена
  4. Настроены тарифы (SALES_MODE=tariffs)

Включение функции

Через Admin Cabinet

  1. Откройте Cabinet → Админ-панель → Настройки
  2. Найдите переключатель Подарочные подписки (Gift Subscriptions)
  3. Включите его
Переключатель сохраняется в базе данных как системная настройка CABINET_GIFT_ENABLED. Перезапуск бота не требуется.

Через API

# Включить
curl -X PATCH https://cabinet.example.com/api/cabinet/branding/gift-enabled \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

# Проверить статус
curl https://cabinet.example.com/api/cabinet/branding/gift-enabled

RBAC права

Для управления настройкой требуется разрешение settings:edit. Для чтения — settings:read.

Для пользователей

Интерфейс Cabinet

Раздел подарков в Cabinet содержит 3 вкладки:
ВкладкаНазначение
КупитьВыбор тарифа, периода и оплата. Указание получателя не обязательно — по умолчанию создаётся код-подарок
АктивироватьВвод подарочного кода для активации полученной подписки
Мои подаркиТри секции: Активные (ожидают активации), Активированные (использованные), Полученные

Покупка подарка (код-подарок)

Основной сценарий — покупка без указания получателя:
1

Откройте вкладку «Купить»

В Cabinet перейдите в раздел Подарить подписку → вкладка Купить.
2

Выберите тариф и период

Доступны все активные тарифы с соответствующими периодами подписки.
3

Добавьте сообщение (опционально)

Напишите персональное сообщение — до 1000 символов.
4

Оплатите

  • С баланса — списание мгновенное, код генерируется сразу
  • Через платёжную систему — код генерируется после подтверждения оплаты
5

Получите код

После оплаты отображается 12-символьный код подарка. Передайте его получателю любым удобным способом.
Код можно передать двумя способами:
  • Deep link бота: t.me/bot?start=GIFT_XXXXXXXXXXXX — получатель нажимает ссылку и активирует подписку в Telegram
  • Ссылка на Cabinet: cabinet.url/gift?tab=activate&code=TOKEN — получатель активирует код в веб-интерфейсе

Покупка подарка с указанием получателя (классический способ)

По-прежнему можно указать получателя при покупке:
1

Откройте вкладку «Купить»

В Cabinet перейдите в раздел Подарить подписку → вкладка Купить.
2

Выберите тариф и период

Доступны все активные тарифы с соответствующими периодами подписки.
3

Укажите получателя

Введите email или Telegram username@) получателя подарка.
4

Добавьте сообщение (опционально)

Напишите персональное сообщение для получателя — до 1000 символов.
5

Выберите способ оплаты

  • С баланса — списание мгновенное, подарок доставляется сразу
  • Через платёжную систему — после оплаты подарок доставляется автоматически

Активация подарочного кода

В Cabinet

  1. Откройте раздел Подарить подписку → вкладка Активировать
  2. Введите подарочный код (минимум 8 символов)
  3. Подписка активируется мгновенно
  • Новый пользователь: отправьте ссылку t.me/bot?start=GIFT_XXXXXXXXXXXX — после регистрации подписка активируется автоматически
  • Существующий пользователь: при переходе по ссылке подписка активируется сразу
Покупатель не может активировать собственный подарочный код. При попытке API вернёт ошибку 400, а в боте код будет проигнорирован.

Статус подарка

После покупки через платёжную систему отображается страница ожидания с автоматическим опросом статуса оплаты. Когда платёж подтверждён:
  • Код-подарок — отображается подарочный код для передачи получателю
  • С указанием получателя — подарок доставляется получателю автоматически

Процесс покупки

Код-подарок: оплата с баланса

Покупатель                    Cabinet                     API бота
    │                           │                           │
    │  1. Выбирает тариф,      │                           │
    │  оплату (без получателя) │                           │
    │──────────────────────────>│                           │
    │                           │  2. POST /gift/purchase   │
    │                           │  {payment_mode: balance}  │
    │                           │  (без recipient_*)        │
    │                           │──────────────────────────>│
    │                           │                           │  3. Списание с баланса
    │                           │                           │  4. Генерация кода
    │                           │  {status: "ok",           │
    │                           │   is_code_only: true,     │
    │                           │   purchase_token: "XXXX"} │
    │                           │<──────────────────────────│
    │  5. Отображение кода      │                           │
    │  для передачи             │                           │
    │<──────────────────────────│                           │

Код-подарок: оплата через платёжную систему

Покупатель                    Cabinet                     API бота
    │                           │                           │
    │  1. Выбирает тариф,      │                           │
    │  оплату (без получателя) │                           │
    │──────────────────────────>│                           │
    │                           │  2. POST /gift/purchase   │
    │                           │  {payment_mode: gateway}  │
    │                           │──────────────────────────>│
    │                           │  {status: "created",      │
    │                           │   payment_url: "..."}     │
    │                           │<──────────────────────────│
    │  3. Переход на            │                           │
    │  платёжную систему       │                           │
    │──────────────────────────>│                           │
    │                           │                           │
    │  4. Возврат в Cabinet     │  5. Polling статуса       │
    │  (страница ожидания)     │  GET /gift/purchase/{t}   │
    │<──────────────────────────│──────────────────────────>│
    │                           │                           │  6. Webhook: оплата
    │                           │                           │  7. Генерация кода
    │                           │  {status: "paid",         │
    │                           │   is_code_only: true}     │
    │                           │<──────────────────────────│
    │  8. Отображение кода      │                           │
    │<──────────────────────────│                           │

Активация подарочного кода

Получатель                    Cabinet / Бот               API бота
    │                           │                           │
    │  1. Вводит код /          │                           │
    │  переходит по deep link  │                           │
    │──────────────────────────>│                           │
    │                           │  2. POST /gift/activate   │
    │                           │  {code: "XXXXXXXXXXXX"}   │
    │                           │──────────────────────────>│
    │                           │                           │  3. Поиск по префиксу
    │                           │                           │  4. SELECT FOR UPDATE
    │                           │                           │  5. Создание подписки
    │                           │  {status: "activated"}    │
    │                           │<──────────────────────────│
    │  6. Подписка активна      │                           │
    │<──────────────────────────│                           │

Классический способ (с указанием получателя)

При указании recipient_type и recipient_value поведение не изменилось — подписка доставляется получателю после оплаты, как и раньше.

Резолвинг Telegram-получателя

При указании Telegram username бот пытается найти пользователя:
  1. База данных — поиск по username среди существующих пользователей
  2. Bot API — запрос через getChat для получения user_id
  3. Если не удалось найти — покупка создаётся с предупреждением recipient_warning
Если получатель не найден в момент покупки, подарок будет ожидать активации. Когда получатель запустит бота через /start, подписка активируется автоматически.

API эндпоинты

GET /cabinet/gift/config

Конфигурация подарков. Возвращает доступные тарифы, периоды, платёжные системы и баланс. Ответ:
{
  "enabled": true,
  "tariffs": [
    {
      "id": 15,
      "name": "Стандарт",
      "periods": [
        {"days": 30, "price_kopeks": 30000},
        {"days": 90, "price_kopeks": 80000}
      ]
    }
  ],
  "payment_methods": [
    {
      "method_id": "yookassa",
      "display_name": "ЮKassa",
      "min_amount": 100,
      "max_amount": 1000000,
      "sub_options": [
        {"id": "card", "name": "Карта"},
        {"id": "sbp", "name": "СБП"}
      ]
    }
  ],
  "balance_kopeks": 50000,
  "currency_symbol": "₽"
}

POST /cabinet/gift/purchase

Создание подарочной покупки. Поля recipient_type и recipient_value опциональны — если не указаны, создаётся код-подарок. Запрос (код-подарок, без получателя):
{
  "tariff_id": 15,
  "period_days": 30,
  "gift_message": "С днём рождения!",
  "payment_mode": "balance"
}
Запрос (с указанием получателя):
{
  "tariff_id": 15,
  "period_days": 30,
  "recipient_type": "telegram",
  "recipient_value": "@username",
  "gift_message": "С днём рождения!",
  "payment_mode": "balance"
}
Ответ (оплата с баланса, код-подарок):
{
  "status": "ok",
  "purchase_token": "XXXXXXXXXXXX",
  "is_code_only": true,
  "warning": null
}
Ответ (оплата через шлюз):
{
  "status": "created",
  "purchase_token": "XXXXXXXXXXXX",
  "payment_url": "https://yookassa.ru/checkout/...",
  "is_code_only": true,
  "warning": null
}
Поле purchase_token в ответе содержит усечённый код (12 символов). Полный 64-символьный токен никогда не покидает сервер.

GET /cabinet/gift/purchase/

Статус подарочной покупки. Поддерживает поиск по префиксу токена (минимум 8 символов). Доступно только покупателю. Ответ:
{
  "status": "paid",
  "purchase_token": "XXXXXXXXXXXX",
  "is_code_only": true,
  "tariff_name": "Стандарт",
  "period_days": 30
}

POST /cabinet/gift/activate

Активация подарочного кода. Поиск по префиксу (минимум 8 символов). Запрос:
{
  "code": "XXXXXXXXXXXX"
}
Ответ (успех):
{
  "status": "activated",
  "tariff_name": "Стандарт",
  "period_days": 30
}
Ошибки:
КодОписание
400Попытка активировать собственный подарок
404Код не найден (единообразный ответ для предотвращения перебора)
410Код уже активирован
429Превышен лимит запросов

GET /cabinet/gift/pending

Список ожидающих подарков для текущего пользователя (как получателя).

Безопасность

Защита токенов

  • Полный 64-символьный токен никогда не покидает сервер — в API-ответах возвращается усечённый 12-символьный код
  • Для активации требуется минимум 8 символов кода (поиск по префиксу)
  • Единообразные 404-ответы — предотвращают перебор токенов (token enumeration)

Защита от race conditions

  • Активация использует SELECT FOR UPDATE — блокировка строки предотвращает двойную активацию при параллельных запросах

Rate limiting

  • Эндпоинт активации имеет отдельный rate limit
  • Покупка: 5 подарков в минуту

Защита от самоактивации

Покупатель не может активировать собственный код:
  • API возвращает 400 Bad Request
  • В боте deep link с собственным кодом игнорируется

Gateway webhook и код-подарки

Код-подарки (is_code_only=true, is_gift=True без получателя) обрабатываются иначе при получении webhook от платёжной системы:
  • С получателем: webhook → fulfill_purchase() → создание подписки → уведомление получателя
  • Код-подарок: webhook → генерация кода → подписка НЕ создаётся (создаётся при активации кода)
Retry-сервис также исключает код-подарки из повторной обработки «зависших» оплат со статусом PAID.

t.me/bot?start=GIFT_XXXXXXXXXXXX
Префикс GIFT_ обязателен. После него — подарочный код (12 символов).

Сценарии

СитуацияПоведение
Новый пользователь/start GIFT_XXXXXXXXXXXX → регистрация → автоматическая активация подарка
Существующий пользователь/start GIFT_XXXXXXXXXXXX → немедленная активация
Код принадлежит этому пользователюКод игнорируется (без ошибки)
Код уже использованСообщение об ошибке
Неверный кодСообщение об ошибке

Транзакции

Подарочные платежи записываются с типом GIFT_PAYMENT:
  • Сумма отрицательная (списание с баланса покупателя)
  • payment_methodNone для оплаты с баланса, идентификатор провайдера для gateway-оплаты
  • Описание: формат с указанием тарифа (и получателя, если указан)
Подарочные платежи с баланса (payment_method=None) не учитываются в статистике доходов, так как REAL_PAYMENT_METHODS не включает NULL.

Устранение проблем

Раздел подарков не отображается

  1. Функция включена? Проверьте GET /cabinet/branding/gift-enabled
  2. CABINET_ENABLED=true?
  3. Хотя бы один тариф создан и активен?

Код подарка не активируется

  1. Код введён верно? Минимум 8 символов
  2. Это не ваш собственный код? Покупатель не может активировать свой подарок
  3. Код уже был использован? Проверьте вкладку «Мои подарки» → «Активированные»
  4. Проверьте логи:
    docker logs <bot_container> 2>&1 | grep -i "gift.*activate"
    

Получатель не получает подарок (классический способ)

  1. Telegram username указан корректно (с @)?
  2. Получатель запустил бота? Если нет — подарок ожидает активации через /start
  3. Проверьте логи:
    docker logs <bot_container> 2>&1 | grep -i "gift"
    

Ошибка «Нельзя активировать собственный подарок»

Система запрещает активацию собственного подарочного кода. Передайте код другому пользователю.

Ошибка rate limit

Максимум 5 подарочных покупок за 60 секунд. Подождите и попробуйте снова.
  1. Формат ссылки верный? t.me/bot?start=GIFT_XXXXXXXXXXXX
  2. Префикс GIFT_ присутствует?
  3. Бот обновлён до v3.29.0+?