Реалізація реклами з вознаграженням (Rewarded Ads) у мобільному додатку
Rewarded — єдиний рекламний формат, який користувачі дивляться добровільно. Конверсія в перегляд там, де він вбудований органічно в механіку (додаткове життя, бонусна валюта, розблокування рівня), досягає 40–70% від показів кнопки. Неудивітельно, що eCPM у rewarded в 3–10 разів вищий за інтерстиціал.
Але у цього формату є специфіка: фіксація моменту видачі награди. Якщо реалізувати неправильно, користувачі отримують награди без перегляду — або не отримують після чесного перегляду. Обидва варіанти руйнують довіру.
Де конкретно ломається логіка видачі наград
Помилка №1: видаємо награду в onAdDismissed.
// НЕПРАВИЛЬНО
rewardedAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdDismissedFullScreenContent() {
giveReward() // користувач міг закрити на 3-й секунді
}
}
onAdDismissedFullScreenContent спрацьовує при будь-якому закритті — і після повного перегляду, і після натиснення кнопки «закрити» на початку. Нагорода потрібна лише в onUserEarnedReward:
rewardedAd?.show(activity) { rewardItem ->
// Цей callback = користувач досмотрів до кінця
val rewardAmount = rewardItem.amount
val rewardType = rewardItem.type
giveReward(rewardType, rewardAmount)
}
Помилка №2: клієнтська верифікація.
Видавати награду прямо в onUserEarnedReward — нормально для простих випадків (додати життя у пам'ять). Але якщо нагорода впливає на серверне стан (віртуальна валюта, premium-контент) — потрібна серверна верифікація через SSV (Server-Side Verification).
Схема роботи SSV:
- Додаток запитує rewarded з
customData— рядок з userId та nonce - Після перегляду рекламна мережа (AdMob/IronSource) робить GET-запит на ваш verification endpoint
- Параметри запиту підписані ECDSA-ключем мережі (публічний ключ можна отримати з
https://www.gstatic.com/admob/reward/verifier-keys.json) - Ваш сервер верифікує підпис, нагороджує
- Клієнт отримує підтвердження через WebSocket або polling
// Передаємо userId в customData при завантаженні
val serverSideVerificationOptions = ServerSideVerificationOptions.Builder()
.setCustomData("userId:${currentUser.id};nonce:${UUID.randomUUID()}")
.build()
val adRequest = AdRequest.Builder().build()
RewardedAd.load(context, AD_UNIT_ID, adRequest, object : RewardedAdLoadCallback() {
override fun onAdLoaded(ad: RewardedAd) {
ad.setServerSideVerificationOptions(serverSideVerificationOptions)
rewardedAd = ad
}
})
SSV критичен для будь-якого додатку, де валюта конвертується в реальні цінності або впливає на PvP-баланс.
UX-управління показом
Кнопку «Подивитися рекламу» показувати лише коли реклама завантажена — коли rewardedAd != null. Сіра неактивна кнопка гірше, ніж скрита. Завантаження починаємо заблаговременно: одразу після попереднього показу або при відкритті екрана, де кнопка може з'явитися.
Якщо завантаження не вдалося — приховуємо кнопку, не показуємо помилку користувачу. Retry з експоненціальним backoff (1 → 2 → 4 → 8 сек) реалізуємо тихо у фоні.
Rewarded Interstitial
Менш знаний, але корисний формат — RewardedInterstitialAd. На відміну від rewarded, не вимагає явної згоди користувача перед показом (немає opt-in діалога), але все одно дає награду за перегляд. Добре працює в точках природних пауз: старт рівня, завантажувальний екран. eCPM нижче rewarded, але вищий за interstitial.
Графіки роботи
Базова реалізація rewarded без SSV — 1–2 дні. З серверною верифікацією та інтеграцією в ігрову економіку — 2–3 дні. Стоимость — за підсумками аналізу проекту та вимог до верифікації.







