Реалізація системи VIP/Premium підписки мобільної гри
Підписка у мобільній грі технічно складніша за разову IAP-покупку: потрібно управляти станом, обробляти отмену, восстановление, upgrade/downgrade між тирами та grace period при неудачному списанні. Помилки тут стоять дорого — користувач втрачає доступ до контенту, за який заплатив, та пишет гневний відзив.
Моделі VIP-підписок у грах
Daily VIP — щоденний бонус поки активна підписка: +20% до дропу монет, +1 безплатна спроба, эксклюзивний щоденний сундук. Низька ціна ($1.99–4.99/місяц), висока конверсія.
Premium підписка — відключення реклами + пакет ресурсів щомісяця + VIP-badge. Ціна $4.99–9.99/місяц.
VIP Levels — кілька тирів (VIP Bronze, Silver, Gold) з зростаючими привілеями. Складніша реалізація, але дозволяє монетизувати різні сегменти аудиторії.
Реалізація на iOS (StoreKit 2)
import StoreKit
class SubscriptionManager: ObservableObject {
@Published var isVIPActive = false
private var updateTask: Task<Void, Error>?
func startListeningForTransactions() {
updateTask = Task.detached {
for await result in Transaction.updates {
await self.handle(result)
}
}
}
private func handle(_ result: VerificationResult<Transaction>) async {
guard case .verified(let transaction) = result else { return }
if transaction.productType == .autoRenewable {
let isActive = transaction.revocationDate == nil
&& (transaction.expirationDate ?? .distantPast) > Date()
await MainActor.run { self.isVIPActive = isActive }
}
await transaction.finish()
}
func checkCurrentEntitlements() async {
for await result in Transaction.currentEntitlements {
await handle(result)
}
}
}
Критично: слухати Transaction.updates потрібно з моменту запуску застосунку — не тільки при відкритті магазину. Інакше пропустите renewal або revocation, поки застосунок працює у фоні.
Реалізація на Android (Google Play Billing Library 6)
class BillingManager(private val context: Context) {
private val billingClient = BillingClient.newBuilder(context)
.setListener { billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
purchases?.forEach { handlePurchase(it) }
}
}
.enablePendingPurchases()
.build()
private fun handlePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
if (!purchase.isAcknowledged) {
val params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(params) { /* handle result */ }
}
updateSubscriptionStatus(purchase.products, isActive = true)
}
}
}
Acknowledge обов'язковий для subscription! Google автоматично скасовує неподтверждені покупки через 3 дні. Забутий acknowledge — найчастіша причина скарг «гроші списали, підписка не активувалась».
Серверна верифікація та синхронізація
Статус підписки не можна зберігати тільки на клієнті. Архітектура:
- Клієнт отримує receipt/purchaseToken після покупки
- Клієнт відправляє його на backend
- Backend верифікує через App Store Server API (iOS) або Google Play Developer API (Android)
- Backend записує статус підписки у БД з датою істечення
- При кожному старті застосунку клієнт отримує актуальний статус з сервера
Apple App Store Server Notifications V2 — webhook, який Apple посилає на ваш endpoint при кожному renewal, cancellation, grace period entry, billing retry. Це реальний час без необхідності polling:
POST /apple/subscription-notifications
{
"signedPayload": "...", // JWT підписаний Apple
"notificationType": "DID_RENEW" | "EXPIRED" | "GRACE_PERIOD_EXPIRED" | ...
}
Grace Period
При неудачному автосписанні (нема грошей на карте) Apple і Google дають grace period: iOS — 6 днів для monthly, Android — 3 дні. У цей період підписка вважається активною, але потрібно показати попередження: «Проблема з оплатою, оновіть платіжні дані».
Без обробки grace period гравці втрачають доступ несподівано та не розуміють чому — чарджбеки та 1-зіркові відзиви.
Управління тирами
Якщо у вас кілька тирів підписки (VIP1, VIP2, VIP3), потрібно обробляти upgrade та downgrade:
-
Upgrade (VIP1 → VIP2): на iOS — через
Product.SubscriptionInfo.upgradePolicies, негайне застосування з пропорційним розрахунком - Downgrade (VIP2 → VIP1): застосовується з початку наступного періоду
Різні product ID для різних тирів потрібно зареєструвати в одній subscriptionGroupId (iOS) / одному basePlanId (Android) — це дозволяє платформі правильно обробляти переходи.
Аналітика підписок
Ключові события для трекінгу: subscription_started, subscription_renewed, subscription_cancelled, subscription_expired, grace_period_entered. Відправляємо у Firebase/Amplitude + Subscription Analytics Dashboard.
Метрики: MRR (Monthly Recurring Revenue), churn rate (% відмінивших за місяц), subscriber LTV. Churn вище 10%/місяц — сигнал, що підписка не дає достатньої цінності.
Терміни: базова підписка з одним тиром, серверною верифікацією та обробкою renewal — 3–5 днів. Система з кількома тирами, App Store Server Notifications, grace period та аналітикою — 7–10 днів.







