Разработка мобильного приложения для продажи билетов на мероприятия
Главная техническая боль ticketing-приложения — конкурентная продажа. Когда 500 человек одновременно нажимают «Купить» на последние 10 мест, приложение обязано корректно обработать это без двойных продаж, зависаний и некорректных статусов. Это не frontend-задача — это проектирование серверной части с пессимистичными блокировками или резервированием через очередь. Но мобильный клиент тоже должен быть готов к сценарию «место уже занято».
Резервирование мест и race condition
Стандартная схема: пользователь выбирает место → сервер создаёт reservation с TTL 10 минут → пользователь платит → reservation конвертируется в ticket. Если оплата не прошла за 10 минут — место освобождается.
На мобильном клиенте таймер резервирования — это CountDownTimer (Android) или Timer.scheduledTimer (iOS), синхронизированный с серверным TTL. Не с моментом нажатия кнопки на клиенте, а с reservation.expiresAt из ответа сервера. Разница часовых поясов и дрейф системных часов устройства убьют UI-таймер, если синхронизировать неправильно.
// Android: синхронизация таймера с серверным expiresAt
class ReservationViewModel : ViewModel() {
private val _timeLeft = MutableStateFlow(0L)
val timeLeft: StateFlow<Long> = _timeLeft
fun startCountdown(expiresAt: Instant) {
viewModelScope.launch {
while (true) {
val remaining = ChronoUnit.SECONDS.between(Instant.now(), expiresAt)
if (remaining <= 0) {
_timeLeft.emit(0)
onReservationExpired()
break
}
_timeLeft.emit(remaining)
delay(1000)
}
}
}
}
При истечении резервирования — не просто показываем ошибку. Автоматически предлагаем ближайшие доступные места, если поток реального времени (WebSocket или SSE) это поддерживает.
Схема мест и интерактивный зал
Для концертных залов и театров нужна интерактивная схема. На iOS — UICollectionView с кастомным UICollectionViewLayout или Canvas в SwiftUI для залов с нестандартной геометрией. На Android — Canvas + GestureDetector для pinch-to-zoom.
Схема загружается как JSON с координатами каждого сиденья, секцией и статусом (available, reserved, sold, disabled). Обновление статусов в реальном времени — через WebSocket: как только кто-то зарезервировал место, всем подключённым клиентам летит { type: "seat_reserved", seatId: "A-15" }.
SVG-схемы залов — популярный вариант для кросс-платформенных решений. React Native с react-native-svg или Flutter с flutter_svg + GestureDetector рендерят SVG с возможностью tap по элементам.
Электронные билеты и валидация на входе
Каждый купленный билет получает уникальный UUID и QR-код, сгенерированный на сервере. Хранить секрет в QR не нужно — достаточно ticketId, верификация происходит на сервере при сканировании. QR не должен быть статичным изображением в PDF — генерируем его динамически из ticketId на клиенте через ZXingObjC (iOS) или zxing-android-embedded, чтобы не хранить растровое изображение в базе.
Приложение контролёра — отдельный экран или отдельное приложение со сканером AVFoundation/CameraX, POST на /tickets/{id}/validate, ответ: valid / already_used / invalid. Кэшируем валидированные ticketId локально на устройстве контролёра на случай отсутствия интернета — синхронизация после восстановления связи.
Платёжный флоу
Эквайринг — ЮKassa, CloudPayments или Stripe. На iOS дополнительно Apple Pay (PKPaymentRequest), на Android — Google Pay. Для b2b сегмента — выставление счёта по email с оплатой через СБП.
После оплаты немедленная отправка PDF-билета на email через SendGrid/Postmark и push-уведомление через FCM/APNs с deep link на экран «Мои билеты».
Типичные ошибки
Double booking. Без SELECT FOR UPDATE или distributed lock на уровне reservationId + seatId два пользователя могут создать резервирование на одно место. На PostgreSQL — advisory locks или ON CONFLICT DO NOTHING при вставке резервирования.
QR на скриншоте. Не ставьте защиту от скриншотов — это ломает UX. Вместо этого QR должен быть одноразовым при валидации: первый скан — ОК, второй — already_used.
Этапы и сроки
Проектирование модели данных (залы, секции, места, события) → схема мест → резервирование с TTL → оплата → электронные билеты → приложение контролёра → тестирование нагрузки.
6–10 недель для полноценного приложения с интерактивной схемой зала, real-time обновлениями и приложением контролёра. Стоимость рассчитывается индивидуально после анализа требований.







