Перейти к содержанию

Специальные предложения

Скидка, которую сервер предлагает пользователю после того, как тот закрыл основной paywall. Предложение живёт минутами: у него есть серверный дедлайн, после которого продукт по этой цене купить нельзя.

Всё, что здесь описано, работает только для зарегистрированного аккаунта. Гостю предложения не выдаются - он и заплатить не может, а экран со скидкой и нерабочей кнопкой хуже, чем его отсутствие.

Флоу целиком

пользователь закрыл основной paywall
POST /v1/offers/paywall-dismissed   → предложение или null
    null → показать обычный paywall, ничего больше делать не нужно
показать экран предложения с таймером на expires_in секунд
POST /v1/payments {product_id из предложения}  → обычная оплата, см. «Оплата»

Дополнительно:

GET  /v1/offers/current        → то же предложение, без побочных эффектов (баннер, возврат на экран)
POST /v1/offers/{id}/dismiss   → пользователь закрыл экран предложения

Получить предложение

curl -X POST "$BASE_URL/v1/offers/paywall-dismissed" -H "Authorization: Bearer $TOKEN"
{
  "id": "…",
  "code": "first_paywall_discount",
  "expires_in": 600,
  "is_dismissed": false,
  "product": {
    "id": "…", "code": "discount_30d", "title": "30 дней",
    "price_minor": 29900, "credits": 1000, "badge": "Только сейчас"
  }
}
Поле Что с ним делать
id идентификатор выдачи: он нужен для dismiss, не для оплаты
code машинное имя предложения - для вашей аналитики
expires_in секунды до конца окна; заводите таймер от этого числа
is_dismissed экран уже закрывали: показывайте баннер, а не полноэкранное предложение
product что и почём продаётся; product.id идёт в POST /v1/payments

Ответ null (а не ошибка) означает, что предложения нет: оно уже выдавалось столько раз, сколько разрешено, у пользователя есть оплаченные покупки, или сейчас ничего не настроено. Это штатный ответ - показывайте обычный paywall.

Таймер серверный

expires_in приходит числом секунд, а не временем истечения: часы устройства на предложение не влияют. Перезапуск приложения окно не продлевает - повторный POST /v1/offers/paywall-dismissed вернёт то же самое предложение с уже уменьшившимся expires_in. Проверяйте остаток через GET /v1/offers/current, а не по своему сохранённому значению.

Оплата предложения

Отдельной ручки нет: предложение оплачивается обычным POST /v1/payments с product_id из product.id. Дальше всё как в разделе Оплата - страница ПЦ, SSE, зачисление кредитов на сервере.

HTTP code Когда
403 forbidden окно закрылось, предложение уже оплачено или его не выдавали этому пользователю

Это и есть «после десяти минут оплата по скидке невозможна»: проверка идёт на сервере, до регистрации заказа в ПЦ.

Цена продления обязана быть на экране

Если предложение продаёт подписку со скидкой на первый период, экран обязан явно показывать, сколько и как часто спишется дальше, по базовой цене. Это требование App Store и Google Play (и закона о защите прав потребителей), а не пожелание: без него приложение отклоняют на ревью.

Закрыть предложение

curl -X POST "$BASE_URL/v1/offers/{id}/dismiss" -H "Authorization: Bearer $TOKEN"   # → 204

Отмечает, что пользователь закрыл экран. Скидка при этом не сгорает: до конца окна предложение остаётся в GET /v1/offers/current (с is_dismissed: true), и его по-прежнему можно оплатить - по сценарию пользователь возвращается к нему через баннер в шапке. Заканчивает предложение только время или оплата.

Чужая или несуществующая выдача - 404 not_found. Повторный dismiss - снова 204.