Реалізація акційних пропозицій (Special Offers) мобільної гри
Special Offers — обмежені за часом пропозиції з високою цінністю, що показуються у правильний момент правильному гравцю. Це не «знижка 20% на усе», це таргетований оффер: новачку на 3-й день — starter pack, гравцю застрявшему на рівні 15 — потрібний йому бустер, churning-користувачу перед видаленням — winback оффер зі 50% знижкою.
Різниця в конверсії між «показати всім» та «показати потрібному сегменту у потрібний момент» — 3–5x.
Архітектура системи офферів
Серверна конфігурація. Усі офферы — на сервері. Клієнт запрашивает «активні офферы для цього гравця» при вході та при відкритті магазину. Сервер повертає тільки ті, які підходять по умовам:
{
"offerId": "starter_pack_d3",
"title": "Стартовий набір",
"products": [
{"type": "gems", "amount": 500},
{"type": "chest", "itemId": "epic_chest", "amount": 3},
{"type": "resource", "itemId": "gold", "amount": 10000}
],
"iapProductId": "com.mygame.starter_pack_d3",
"originalPrice": 9.99,
"discountPercent": 70,
"validUntil": "2025-03-28T23:59:59Z",
"triggerConditions": {
"daysSinceInstall": {"min": 2, "max": 4},
"hasNeverPurchased": true,
"minLevel": 5
},
"maxPurchases": 1
}
triggerConditions перевіряються на сервері — клієнт не видит логіку сегментації, тільки готовий список офферів.
Триггери показу
Оффер не просто висить у магазині — він вспливає в контексті. Триггери:
Contextual trigger. Гравець програв рівень 3 рази підряд — показуємо оффер з бустером саме для цього рівня. Сервер знает поточний рівень гравця та його спроби.
Time trigger. День 3 після установки — кращий момент для starter pack: гравець уже вложився, зрозумів механіку, готов потратити перший долар.
Re-engagement trigger. Гравець не заходив 5 днів — при поверненню показуємо winback оффер. Реалізується через push-сповіщення з deeplink на екран оффера.
Achievement trigger. Гравець щойно прошов крутой рівень або відкрив новий контент — емоціональний пік, кращий момент для пропозиції.
Таймер та urgency
struct OfferTimerView: View {
let expiresAt: Date
@State private var timeRemaining: String = ""
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("Залишилось: \(timeRemaining)")
.onReceive(timer) { _ in
let remaining = expiresAt.timeIntervalSinceNow
if remaining > 0 {
timeRemaining = formatDuration(remaining)
} else {
// Оффер істеку — приховуємо
NotificationCenter.default.post(name: .offerExpired, object: nil)
}
}
}
}
Час істечення перевіряється на сервері при клику «Купити» — таймер на клієнті тільки для UI. Неможна купити прострочений оффер, навіть якщо клієнт не оновив стан.
A/B-тестування офферів
Firebase Remote Config дозволяє тестувати різні варіанти оффера:
- Група A: 500 gems + 3 chest за $1.99
- Група B: 300 gems + 5 chest + 1 skin за $1.99
Метрика: conversion rate (купили / побачили). Тест на 1000+ гравців на кожну групу, статистична значимість >95%.
Для складнішої персоналізації використовуємо Firebase ML або собственный recommendation engine на основі сегментів: новички, mid-spenders, whales, churning users.
Обмеження покупок та anti-abuse
maxPurchases: 1 на рівні сервера — оффер можна купити тільки один раз. Перевірка: перед покупкою сервер дивится purchase_history по offerId + userId.
Деякі офферы «one-time-ever» (стартовий пакет — ніколи більше не показуємо), інші — повторюються з обмеженням за часом (flash sale раз у тиждень). Логіка у конфігу оффера, не у клієнті.
Інтеграція з IAP
Special Offer — це окремий продукт у App Store/Google Play з унікальним productId. Неможна динамічно змінити ціну існуючого IAP-продукту — це обмеження платформ. Тому кожен ціновой тир оффера = окремий registered product.
Apple дозволяє Promotional Offers (знижка на subscription для існуючих покупців) та Introductory Offers (знижка для нових абонентів) — це вбудовані механізми, які не вимагають окремих product ID.
Терміни: базова система з 3–5 типами офферів, серверною конфігурацією та таймером — 2–3 дні. Повна система з персоналізацією, A/B-тестами, contextual triggers та аналітикою — 1–1.5 тижня.







