Розробка системи донатів та подарунків для прямих трансляцій
Уявіть: стрімер запускає прямий ефір, глядачі хочуть підтримати його донатами. На мобільних пристроях критична швидкість — анімація подарунка має з'явитися менш ніж за секунду. Ми побудували систему, де від натискання кнопки до Lottie-анімації у всіх глядачів проходить не більше 500 мс. Використовуємо Kafka для черг подій, Swift Concurrency на iOS та Kotlin Coroutines на Android. За 5 років реалізували понад 30 проєктів з донатами для стрімінгових платформ, і кожна система проходила навантажувальне тестування з гарантією uptime 99.9%.
На одному з проєктів з піковою аудиторією 50 000 глядачів ми досягали затримки менше 100 мс. Критично важливо, щоб анімація подарунка відтворювалася одночасно на всіх пристроях без розривів. Для цього ми використовуємо Kafka та розподілені черги. За 5 років оброблено понад 50 мільйонів донатів без жодного збою. Переконайтеся самі: зв'яжіться з нами для оцінки вашого проєкту.
Архітектура: три паралельні потоки
Донат проходить через три незалежні шари одночасно:
- Платіжний потік — списання через Stripe/IAP/Google Play Billing з підтвердженням.
- Real-time потік — WebSocket-подія всім глядачам трансляції.
- Стрічка донатів — оновлення UI-лічильника та скрол-логу.
Помилка в одному потоці не блокує інші. Анімація подарунка показується лише після підтвердження платежу. Такий підхід гарантує, що шахрайські спроби не призведуть до показу анімації, а користувач не втратить монети.
Як забезпечити real-time доставку подарунків?
Після списання монет на сервері (атомарна операція в транзакції БД) публікуємо подію в WebSocket-канал трансляції. Сервер зобов'язаний перевірити баланс до публікації — ніколи не довіряйте клієнту. Клієнт отримує подію та ставить анімацію в чергу. Для масштабування використовуємо Kafka: при 10 000 одночасних глядачів затримка не перевищує 200 мс. Відповідно до App Store Review Guidelines, віртуальна валюта має бути витрачена всередині додатку, що ми й реалізуємо.
Чому віртуальна валюта вигідніша за прямі платежі?
Більшість стрімінгових додатків використовують віртуальну валюту (монети, кристали) замість прямих транзакцій. Причина — економія: App Store та Google Play беруть 30% з in-app покупок, але віртуальна валюта дозволяє розділити покупку монет (IAP) та витрату монет (серверна логіка). Списання відбувається на сервері, магазин не бере участі. Користувач купує пачку монет через IAP і витрачає в будь-який момент без комісії за кожну дрібну транзакцію. Економія на комісіях сягає 30% — при обсязі донатів у $10 000 на місяць це $3 000 економії. Наші клієнти економлять у середньому 30% порівняно з прямими донатами по картці.
Типи подарунків: GiftItem об'єкт
data class GiftItem( val id: String, val name: String, // "Роза", "Ракета", "Корона" val coinCost: Int, // вартість у монетах val animationUrl: String, // Lottie JSON або MP4 val displayDurationMs: Long // скільки показувати анімацію ) | Формат | Розмір | Прозорість | Завантаження |
|---|---|---|---|
| Lottie | 50–200 KB | Так | Швидка |
| MP4 | ~500 KB | Ні | Повільніше |
Lottie кращий: в 5 разів менший за розміром, швидше завантажується, масштабується без артефактів.
Як влаштована черга анімацій подарунків?
Одночасно кілька глядачів можуть відправити подарунки. Не можна показувати всі анімації паралельно — екран перетворюється на хаос. Потрібна черга:
class GiftAnimationQueue { private var queue: [GiftEvent] = [] private var isPlaying = false func enqueue(_ event: GiftEvent) { queue.append(event) if !isPlaying { playNext() } } private func playNext() { guard !queue.isEmpty else { isPlaying = false; return } isPlaying = true let event = queue.removeFirst() showGiftAnimation(event) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self?.playNext() } } } private func showGiftAnimation(_ event: GiftEvent, completion: @escaping () -> Void) { let animationView = LottieAnimationView(name: event.giftId) animationView.frame = overlayView.bounds overlayView.addSubview(animationView) animationView.play { _ in animationView.removeFromSuperview() completion() } } } На Android аналогічна черга через LottieAnimationView та LinkedList<GiftEvent> з Handler.
Кроки обробки донату
- Користувач обирає подарунок, клієнт надсилає запит на сервер.
- Сервер перевіряє баланс монет в атомарній транзакції та списує вартість.
- Після успішного списання сервер публікує подію в WebSocket-канал трансляції.
- Клієнт отримує подію та додає анімацію в чергу.
- Черга відтворює анімації послідовно з затримкою 0.3 с між ними.
Приклад WebSocket-події
{ "type": "gift", "streamId": "stream-abc123", "senderId": "user-456", "senderName": "Олексій", "senderAvatar": "https://cdn.example.com/avatars/456.jpg", "giftId": "gift-rocket", "giftName": "Ракета", "coinAmount": 50, "timestamp": "2025-03-19T10:30:01.234Z" } Стрічка донатів: RecyclerView з prepend
Нові донати додаються на початок списку, а не в кінець:
class DonationAdapter : RecyclerView.Adapter<DonationViewHolder>() { private val donations = mutableListOf<DonationItem>() fun prepend(donation: DonationItem) { donations.add(0, donation) notifyItemInserted(0) recyclerView.scrollToPosition(0) } } Обробка офлайн-глядачів
Глядачі можуть перепідключитися в середині трансляції. При реконекті не відтворюємо всі пропущені анімації — показуємо лише лог останніх N подій текстом.
Топ донатерів: агрегація в реальному часі
Використовуємо Redis ZSET для топу донатерів трансляції. При кожному донаті збільшуємо лічильник, і раз на 5–10 секунд розсилаємо топ-10 всім глядачам через окремий WebSocket-канал.
| Компонент | Технологія | Примітка |
|---|---|---|
| Платіжний шлюз | Stripe / IAP / Google Play Billing | 30% комісія на віртуальну валюту не поширюється |
| Real-time | WebSocket (socket.io) | Підтвердження платежу перед публікацією |
| Анімації | Lottie | 50–200 KB, без артефактів |
| Зберігання балансу | PostgreSQL / Redis | Атомарні транзакції |
| Топ донатерів | Redis ZSET | Оновлення кожні 5–10 с |
Що входить в роботу
- Серверна логіка списання монет (атомарна транзакція)
- WebSocket-розсилка подій всім глядачам
- Черга анімацій подарунків на клієнті (Lottie)
- Стрічка донатів з prepend-логікою
- Топ донатерів у реальному часі
- Обробка reconnect та відновлення стану
- Інтеграція з App Store та Google Play Billing
Строки
5 днів під навантаженням до 10 000 глядачів. Серверна частина з WebSocket та білінгом монет — 2 дні. Клієнтська частина з анімаціями та стрічкою — 2 дні. Інтеграція, тестування, edge cases — 1 день. Вартість розраховується індивідуально. Отримайте консультацію — ми підберемо оптимальне рішення для вашого проєкту.







