## Переходы статуса брони должны быть условными (WHERE status=...) **Контекст:** `BookingHandler.Cancel` уже использовал `UpdateStatusIfPending` (атомарный `UPDATE ... WHERE status='pending'`), а `Confirm` вызывал безусловный `UpdateStatus(id, "confirmed")`. Это позволяло: - подтвердить уже **отменённую** бронь (`cancelled` → `confirmed`); - получить 200 при подтверждении несуществующего id (0 строк, но без ошибки). **Суть:** Изменение статуса — это переход в конечном автомате, а не «затирание поля». Допустимый переход нужно зашивать в `WHERE`, а число затронутых строк использовать как признак валидности перехода. Иначе появляются недопустимые состояния и race conditions. **Решение:** переиспользовать тот же условный апдейт и для подтверждения: ```go ok, err := h.bookingRepo.UpdateStatusIfPending(r.Context(), id, "confirmed") if err != nil { /* 500 */ } if !ok { writeError(w, http.StatusConflict, "booking cannot be confirmed in its current state", nil) return } ``` Безусловный `BookingRepo.UpdateStatus` удалён как мёртвый и опасный код. **Связанные заметки:** [[atomic-refresh-token-race-condition]], [[backend-validation]] **Источник:** Code review 2026-06-30 (neyrogovnarik), fix в `handlers/bookings.go` + `repository/bookings.go` #golang #backend #state-machine #concurrency #bug