GH GambleHub

Обработка ошибок и UX-объяснения

1) Зачем это важно

Ошибка — это не «красный текст», а продолжение сценария. Хороший UX ошибок:
  • объясняет что случилось и что делать дальше,
  • сохраняет введенные данные и предотвращает потерю прогресса,
  • дает безопасный повтор или альтернативный путь,
  • остается доступным (SR/клавиатура) и не раскрывает лишнего.

2) Типология ошибок (для интерфейса)

1. Валидация данных (4xx client): пустые/неверные поля, формат, длина, конфликт правил.
2. Бизнес-правила: лимиты, гео-ограничения, KYC/KYB, дубли, недоступные слоты.
3. Права/пермиссии: роль, доступ к ресурсу, возрастные ограничения.
4. Сеть/сервер: таймаут, оффлайн, 5xx, перегрузка, rate limit.
5. Конфликты/состояние: 409/412 (данные изменились), гонки, блокировки.
6. Отсутствие ресурса: 404/410, удалено/перенесено.
7. Платежные и рисковые: отклонение банком/PSP, антифрод, лимиты ответственной игры.

3) Каналы и уровень отображения

Подбираем «громкость» под контекст:
КаналКогдаПример
Inline у поляВалидация/подсказка«Минимум 8 символов»
Под блоком/формойОшибка шага«Не удалось сохранить. Попробуйте еще раз.»
Тост (role=status)Неблокирующие события«Файл загружен с ошибками, подробности внизу»
Баннер на страницеВажные, но не блокирующие навигацию«Оффлайн. Показаны кешированные данные»
Модалка (role=alertdialog)Блокирующие рисковые шаги«Сессия истекла. Войдите снова»
Страница ошибки404/5xx, критические падения«Страница не найдена» / «Извините, сбой на нашей стороне»

Правило: не прячьте критичное в тосты/hover. Там, где пользователь смотрит, там и сообщение.

4) Копирайтинг ошибок

Структура: причина → следствие → действие.
Тон: честный, нейтральный, без вины.
Конкретика: укажите поле/условие, избегайте кодов и стеков.
Кнопка-действие: «Повторить», «Изменить карту», «Сбросить фильтры», «Открыть чат».
Чувствительные данные: не показывать (маскировка PAN, личные атрибуты).

Примеры

Хорошо: «Платеж не прошел: банк отклонил операцию. Попробуйте другой способ или повторите позже».
Плохо: «Ошибка 500. Что-то пошло не так».
Хорошо: «Лимит дневных расходов достигнут. Установите новый лимит или попробуйте завтра».
Хорошо: «Файл слишком большой (макс. 25 МБ). Сожмите или загрузите несколько файлов».

5) Поведение и фокус (A11y)

Ошибка выводится в фокусный контекст: переводим фокус к первому ошибочному полю.
Живые регионы: `role="status"` (polite) для инфо, `role="alert"` (assertive) — для критичных.
Видимое `:focus-visible`, контраст ≥ AA, альтернативы цвету (иконка/текст).
Сообщение привязываем к полю через `aria-describedby`.

html
<label for = "pwd "> Password </label>
<input id="pwd" name="password" aria-describedby="pwd-err" aria-invalid="true">
<p id = "pwd-err" role = "alert"> Minimum 8 characters </p>

6) Ретраи, backoff и идемпотентность

Повтор предлагается, если шанс успеха есть (сетевые сбои, 5xx, rate limit).
Экспоненциальный backoff 1–2–4–8 с, лимит попыток, понятная кнопка «Повторить».
Критичные операции (ставки/платежи): обязательный Idempotency-Key → исключаем дубли.
Откаты оптимистичных обновлений — четкий визуальный возврат + пояснение.

js async function retry(fn, attempts=3){
let wait=1000; for(let i=0; i<attempts; i++){
try{ return await fn(); }catch(e){ if(i===attempts-1) throw e; await new Promise(r=>setTimeout(r,wait)); wait=2; }
}
}

7) Оффлайн, таймауты и частичный контент

Оффлайн: показываем баннер «Нет подключения», доступ к кешу (read-only), очередь синхронизации.
Таймауты: UI-таймаут (3–5 с) → состояние «Ожидаем подтверждение…» с безопасным повтором/отменой.
Частичный успех: сохраняем то, что удалось; маркируем «не синхронизировано».

8) Конфликты и конкурентность

409/412: данные устарели. Предложить «Обновить» и показать дифф (что изменилось).
Блокировки: информируем, кто держит блок, и сколько времени, кнопка «Запросить доступ».

9) Образцы UI-шаблонов

Баннер страницы:
html
<div class="banner banner--error" role="alert">
<strong> Connection failed. </strong> Shows cached data.
<button class =" btn btn--ghost" id = "retry "> Retry </button>
</div>
Модалка критической ошибки:
html
<div role="alertdialog" aria-labelledby="err-title" aria-describedby="err-desc">
<h2 id = "err-title "> Session expired </h2>
<p id = "err-desc "> Sign in again to continue. </p>
<button class = "btn "> Sign in </button>
<button class =" btn btn--ghost"> Home </button>
</div>
React ErrorBoundary (с ID корреляции):
tsx function Fallback({ id, onRetry }: { id: string; onRetry: ()=>void }) {
return (
<div role="alert" className="banner banner--error">
<strong> We couldn't load the page. </strong>
<div> Try again. Код: <code>{id}</code> <button onClick={()=>navigator. clipboard. writeText (id)}> Copy </button> </div>
<button onClick = {onRetry}> Retry </button>
</div>
);
}

10) Токены ошибок (дизайн-система)

json
{
"error": {
"tones": { "danger": "#", "warning": "#", "info": "#" },
"aria": { "polite": true, "assertive": true },
"timing": { "toastMs": 3500, "retryBackoffMs": [1000,2000,4000] },
"layout": { "fieldGap": 8, "bannerIcon": 20 }
}
}
CSS-пресеты:
css
.banner--error { background: var(--bg-danger); color: var(--on-danger); padding: 12px 16px; border-radius: 12px; }
.field-error { color: var(--role-danger); margin-top: 6px; font-size:.875rem; }

11) Безопасность и приватность

Не выводим стек-трейсы, внутренние ID, пути БД.
Маскируем чувствительные значения (карты, документы).
Сообщения не должны подсказывать злоумышленнику (например, что аккаунт существует).
Для поддержки — ID корреляции вместо деталей.

Структурированный лог (бэкенд):
json
{"level":"error","event":"payment_fail","correlation_id":"c-8f1...","user_id":"u-","route":"/pay","psp_code":"DO_NOT_EXPOSE_TO_USER"}

12) Метрики и контроль

INP и доля Long Tasks в момент ошибки (ошибка не должна «вешать» UI).
Retry success rate, ошибки на 1000 действий, время до восстановления.
CTR на «Помощь/Чат», процент брошенных форм.
Тепловые карты: где чаще всего возникают field-errors.

13) QA-чек-лист

Доступность

  • Фокус на первом ошибочном поле; `aria-describedby`/`aria-invalid` выставлены.
  • Критичные сообщения — `role="alert"`; контраст ≥ AA.

Поведение

  • Данные формы не теряются при ошибке.
  • Есть понятный `Retry` и корректный backoff.
  • Оффлайн-режим/кеш работают; баннер видим.

Копирайтинг

  • Причина → действие; без технического жаргона и обвинений.
  • Тексты локализуются и не ломают сетку.

Безопасность

  • Нет утечки PII/секретов; показываем только безопасные коды/ID.
  • Идемпотентность включена для критичных операций.

14) Специфика iGaming

Ставка:
  • UI сразу фиксирует `busy`; при задержке > 3 с — «Ожидаем подтверждение…».
  • При fail: честный статус («рынок закрылся», «коэффициент изменился») + безопасный `Retry`.
  • Идемпотентный ключ, чтобы исключить двойную ставку.
Платеж/вывод:
  • Различаем «отказ банка/PSP» vs «сбой сервера». Для первого — «Выберите другой способ», для второго — `Retry`.
  • Прозрачные шаги KYC/AML; ссылки «Почему это нужно?».
Ответственная игра и лимиты:
  • Тон заботливый, без давления. «Лимит достигнут — сделайте паузу или обновите лимит».
  • Без вспышек/неона; контраст AAA, доступность при SR.
Гео/лицензия:
  • Четко объясните ограничения и предложите «Ознакомиться с правилами/поддержка».

15) Анти-паттерны

«Что-то пошло не так» без действий и контекста.
Сброс формы после ошибки.
Прятать критичное в тост на 3 секунды.
Только цвет без текста/иконки.
Бесконечные ретраи без возможности отмены.
Показывать внутренние коды/стек-трейсы.

16) Документация в дизайн-системе

Компоненты: `FieldError`, `FormError`, `PageBanner`, `AlertDialog`, `ErrorBoundary`.
Токены тонов/контраста/таймингов, пресеты a11y и примеры ARIA.
Карта типовых сценариев (валидация, сеть, права, платежи) с текстовыми шаблонами.
«Do/Don’t»: реальные экраны до/после с метриками отказов/успехов.

Краткое резюме

Делайте ошибки понятными и управляемыми: говорите человеческим языком, сохраняйте введенные данные, предлагайте безопасный повтор и альтернативы, уважайте доступность и приватность. Тогда даже нештатные ситуации сохраняют доверие и не прерывают путь пользователя — особенно в критичных сценариях ставок и платежей.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.