Przejdź do treści

Rezerwacje (Reservation)

Ostatnia aktualizacja dokumentacji: 2026-02-26 Stan synchronizacji z kodem: ✅ Zsynchronizowany

Serce systemu zajmij.to — pełny cykl życia rezerwacji od utworzenia, przez płatność, aż po zwrot i opinię.


1. Opis ogólny

Domena Rezerwacji odpowiada za cały proces rezerwowania obiektów i zapisów na zajęcia. To jak elektroniczna recepcja, która pracuje 24 godziny na dobę, 7 dni w tygodniu — przyjmuje rezerwacje, pobiera płatności, wysyła potwierdzenia i obsługuje zwroty.

System obsługuje dwa typy rezerwacji:

  • Rezerwacje godzinowe — wynajem sal, boisk, sprzętu na określone godziny
  • Zapisy na zajęcia — uczestnictwo w zajęciach grupowych (fitness, basen, warsztaty)

2. Architektura domeny

2.1 Modele danych

Model Tabela Opis
Reservation reservations Główny model rezerwacji — dane klienta, cena, statusy, polimorficzne powiązanie z obiektem (Item) lub zajęciami (ActivityItem)
ReservationSlot reservation_slots Pojedynczy termin (slot) w ramach rezerwacji — data, godziny, cena, status płatności
ReservationPayment reservation_payments Płatność za rezerwację — kwota, metoda, status transakcji, URL płatności P24
ReservationPaymentRefund reservation_payment_refunds Zwrot płatności — kwota, metoda zwrotu, status, potwierdzenie
ReservationPaymentReceipt reservation_payment_receipts Paragon PDF — numer, data wystawienia, ścieżka PDF, data wysyłki
ReservationPaymentLog reservation_payment_logs Log operacji płatniczych — kanał, poziom, typ zdarzenia, dane P24
ReservationPaidLog reservation_paid_logs Audyt zmian płatności — snapshot stanu before/after, akcja, kto zmienił
ReservationPaymentReminderLog reservation_payment_reminder_logs Log wysłanych przypomnień o płatności — typ, data wysłania
WaitingReservation waiting_reservations Tymczasowa rezerwacja oczekująca na potwierdzenie email (soft-lock)
WaitingReservationSlot waiting_reservation_slots Slot tymczasowej rezerwacji — blokuje termin w harmonogramie

2.2 Diagram relacji

erDiagram
    Reservation ||--o{ ReservationSlot : "hasMany"
    Reservation ||--o{ ReservationPayment : "hasMany"
    Reservation ||--o{ ReservationPaidLog : "hasMany"
    Reservation ||--o{ ReservationPaymentReminderLog : "hasMany"
    Reservation }o--|| Organization : "belongsTo"
    Reservation }o--o| Client : "belongsTo"
    Reservation }o--o| Pricing : "belongsTo"
    Reservation ||--o| Review : "hasOne"
    Reservation ||--o{ Complaint : "hasMany"
    Reservation ||--o{ Note : "morphMany"
    Reservation ||--o{ Schedule : "morphMany"
    Reservation ||--o{ StatusLog : "morphMany"

    ReservationSlot }o--|| Reservation : "belongsTo"
    ReservationSlot ||--o| Schedule : "morphOne"
    ReservationSlot ||--o{ ReservationPaymentRefund : "hasMany"
    ReservationSlot }o--o| User : "cancelledBy"

    ReservationPayment }o--|| Reservation : "belongsTo"
    ReservationPayment ||--o{ ReservationPaymentRefund : "hasMany"
    ReservationPayment ||--o| ReservationPaymentReceipt : "hasOne"
    ReservationPayment }o--o| User : "creator"

    ReservationPaymentRefund }o--|| ReservationPayment : "belongsTo"
    ReservationPaymentRefund }o--o| ReservationSlot : "belongsTo"
    ReservationPaymentRefund }o--o| User : "processor"
    ReservationPaymentRefund }o--o| User : "manualConfirmer"

    ReservationPaymentReceipt }o--|| ReservationPayment : "belongsTo"

    ReservationPaymentLog }o--o| Reservation : "belongsTo"
    ReservationPaymentLog }o--o| ReservationPayment : "belongsTo"
    ReservationPaymentLog }o--o| Organization : "belongsTo"

    ReservationPaidLog }o--|| Reservation : "belongsTo"
    ReservationPaidLog }o--o| ReservationPayment : "belongsTo"
    ReservationPaidLog }o--o| ReservationPaymentRefund : "belongsTo"
    ReservationPaidLog }o--o| User : "changer"

    WaitingReservation }o--|| Organization : "belongsTo"
    WaitingReservation ||--o{ WaitingReservationSlot : "hasMany"

    WaitingReservationSlot }o--|| WaitingReservation : "belongsTo"
    WaitingReservationSlot ||--o| Schedule : "morphOne"

2.3 Struktura tabel w bazie danych

Tabela reservations

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Unikalny identyfikator
uuid uuid Nie Publiczny identyfikator (do linków)
reservationable_type string Nie Typ obiektu: Item lub ActivityItem
reservationable_id uuid Nie ID obiektu/zajęć
organization_id uuid (FK) Nie Organizacja
client_id uuid (FK) Tak null Powiązany klient
access_code string Nie Kod dostępu (ukryty w API)
first_name string Nie Imię rezerwującego
last_name string Nie Nazwisko rezerwującego
email string Nie Email rezerwującego
mobile_phone string Nie Telefon rezerwującego
total_price float Nie Cena łączna
total_net decimal(12,2) Tak null Kwota netto
total_vat decimal(12,2) Tak null Kwota VAT
vat_rate string(10) Tak null Stawka VAT
price_breakdown json Tak null Rozbicie cenowe per slot
pricing_id uuid (FK) Tak null Użyty cennik
status enum Nie Status: pending, confirmed, canceled, rejected
payment_status enum Nie not_required Status płatności
is_company boolean Nie false Czy firma
company_name text Tak null Nazwa firmy
nip string Tak null NIP
regon string Tak null REGON
street, street_number, postal_code, city string Tak null Adres
commune, district, voivodeship string Tak null Dane TERYT
reservationable_snapshot json Nie Snapshot obiektu/cennika w momencie rezerwacji
ip_address string Tak null IP rezerwującego
user_agent string Tak null User-Agent przeglądarki
accepted_rodo boolean Nie false Zgoda RODO
accepted_rodo_path string Tak null Ścieżka do treści RODO
accepted_terms boolean Nie false Akceptacja regulaminu
accepted_terms_path string Tak null Ścieżka do regulaminu
accepted_reservationable_statute boolean Nie false Akceptacja regulaminu obiektu
accepted_reservationable_statute_path string Tak null Ścieżka do regulaminu obiektu
consents_accepted_at timestamp Tak null Data akceptacji zgód
show_price boolean Nie true Czy pokazywać cenę
price_confirmed boolean Nie false Potwierdzenie ceny przez admina
price_confirmed_at timestamp Tak null Data potwierdzenia ceny
price_confirmed_by uuid (FK) Tak null Kto potwierdził cenę
send_notifications boolean Nie true Czy wysyłać powiadomienia
review_reminder_sent_at timestamp Tak null Data wysłania przypomnienia o opinii
created_at, updated_at timestamp Nie Znaczniki czasu

Tabela reservation_slots

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
reservation_id uuid (FK) Nie Rezerwacja nadrzędna
date date Nie Data rezerwacji
time_from time Nie Godzina rozpoczęcia
time_to time Nie Godzina zakończenia
start_at datetime Nie Pełna data+godzina start
end_at datetime Nie Pełna data+godzina koniec
price float Tak null Cena slotu
is_paid boolean Nie false Czy opłacony
paid_at timestamp Tak null Data opłacenia
paid_amount decimal(10,2) Nie 0 Opłacona kwota
refunded_amount decimal(10,2) Nie 0 Zwrócona kwota
status enum Nie active Status: active, cancelled
cancelled_at timestamp Tak null Data anulowania
cancelled_by uuid (FK) Tak null Kto anulował
cancellation_reason string Tak null Powód anulowania

Tabela reservation_payments

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
reservation_id uuid (FK) Nie Rezerwacja
amount decimal(10,2) Nie Kwota płatności
currency string(3) Nie PLN Waluta
status enum Nie pending Status transakcji
payment_method enum Nie przelewy24 Metoda: przelewy24, manual, free
external_transaction_id string Tak null ID transakcji P24
payment_url string Tak null URL do płatności P24
paid_at timestamp Tak null Data opłacenia
expires_at timestamp Tak null Data wygaśnięcia
created_by uuid (FK) Tak null Kto utworzył (admin)
metadata json Tak null Dodatkowe dane

Tabela reservation_payment_refunds

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
reservation_payment_id uuid (FK) Nie Płatność źródłowa
reservation_slot_id uuid (FK) Tak null Opcjonalnie powiązany slot
amount decimal(10,2) Nie Kwota zwrotu
status enum Nie pending Status: pending, pending_gateway, pending_manual, completed, failed
refund_type enum Nie full Typ: full, partial, slot_cancellation, forced_cancellation, item_block, activity_cancellation
reason text Tak null Powód zwrotu
external_refund_id string Tak null ID zwrotu P24
admin_notes text Tak null Notatki admina
refund_method enum Tak null Metoda: gateway, transfer, cash, auto, undefined
confirmation_number string Tak null Numer potwierdzenia (unikalny)
confirmation_generated_at timestamp Tak null Data wygenerowania potwierdzenia
confirmation_sent_at timestamp Tak null Data wysłania potwierdzenia
confirmation_sent_to string Tak null Email odbiorcy potwierdzenia
manual_confirmed_at timestamp Tak null Data ręcznego potwierdzenia
manual_confirmed_by uuid (FK) Tak null Kto ręcznie potwierdził
processed_by uuid (FK) Tak null Kto przetworzył
processed_at timestamp Tak null Data przetworzenia
metadata json Tak null Dodatkowe dane

Tabela reservation_payment_receipts

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
reservation_payment_id uuid (FK) Nie Płatność
receipt_number string Nie Numer paragonu (unikalny)
issued_at timestamp Nie Data wystawienia
pdf_path string Tak null Ścieżka do PDF
sent_to_email string Tak null Email odbiorcy
sent_at timestamp Tak null Data wysłania

Tabela reservation_payment_logs

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
organization_id uuid Tak null Organizacja
reservation_id uuid Tak null Rezerwacja
reservation_payment_id uuid Tak null Płatność
reservation_payment_refund_id uuid Tak null Zwrot
channel string(50) Nie Kanał: p24_gateway, security, consistency, payment
level string(20) Nie Poziom: info, warning, error, critical
event_type string(100) Nie Typ zdarzenia (np. signature_verified, callback_processed)
message string(500) Nie Wiadomość
context json Tak null Kontekst (kwoty, ID, itp.)
ip_address string(45) Tak null IP żądania
user_agent text Tak null User-Agent
request_id string(100) Tak null ID żądania (korelacja logów)
p24_session_id string(100) Tak null ID sesji P24
p24_order_id string Tak null ID zamówienia P24
p24_request_id string Tak null ID żądania P24

Tabela reservation_paid_logs

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
reservation_id uuid (FK) Nie Rezerwacja
reservation_payment_id uuid (FK) Tak null Płatność
reservation_payment_refund_id uuid (FK) Tak null Zwrot
action enum Nie Typ akcji (23 wartości — patrz sekcja Statusy)
previous_total_price / new_total_price decimal(10,2) Tak null Cena przed/po
previous_refund_amount / new_refund_amount decimal(10,2) Tak null Kwota zwrotu przed/po
previous_is_paid / new_is_paid boolean Tak null Opłacona przed/po
previous_is_refund / new_is_refund boolean Tak null Czy zwrot przed/po
previous_paid_at / new_paid_at timestamp Tak null Data płatności przed/po
previous_refund_at / new_refund_at timestamp Tak null Data zwrotu przed/po
initiated_refund_amount decimal(10,2) Tak null Kwota zainicjowanego zwrotu
changed_by uuid (FK) Tak null Kto zmienił
reason text Tak null Powód zmiany

Tabela waiting_reservations

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
organization_id uuid (FK) Nie Organizacja
reservationable_type string Nie Typ obiektu (polimorfizm)
reservationable_id uuid Nie ID obiektu
first_name, last_name, email, mobile_phone string Nie Dane kontaktowe
is_company, company_name, nip, regon Tak Dane firmy
street, street_number, postal_code, city, commune, district, voivodeship string Tak null Adres
total_price decimal(10,2) Tak null Cena
total_net, total_vat, vat_rate Tak null Dane VAT
pricing_id uuid Tak null Cennik
price_breakdown json Tak null Rozbicie cenowe
show_price boolean Nie true Widoczność ceny
slots_data json Tak null Dane slotów (JSON)
ip_address, user_agent string Tak null Dane przeglądarki
confirmation_token string(64) Nie Token potwierdzenia (unikalny, ukryty)
accepted_rodo, accepted_terms, accepted_reservationable_statute boolean Nie false Zgody
consents_accepted_at timestamp Tak null Data akceptacji zgód
expires_at datetime Tak null Czas wygaśnięcia

Tabela waiting_reservation_slots

Kolumna Typ Nullable Default Opis
id uuid (PK) Nie Identyfikator
waiting_reservation_id uuid (FK) Nie Rezerwacja oczekująca
date date Nie Data
time_from, time_to string Nie Godziny
start_at, end_at datetime Nie Pełne daty
price decimal(10,2) Tak null Cena

3. Endpointy API

3.1 Rezerwacje

GET /api/reservations

  • Opis: Lista rezerwacji z filtrami (paginacja, sortowanie)
  • Autoryzacja: Admin, Supervisor, Employee (middleware role)
  • Query Parameters: Standardowe filtry Spatie QueryBuilder + relations[]

GET /api/reservations/{reservationId}

  • Opis: Szczegóły rezerwacji z relacjami (complaints, statusLogs)
  • Autoryzacja: Admin, Supervisor, Employee (middleware role)

POST /api/reservations (auth)

  • Opis: Tworzenie rezerwacji przez administratora
  • Autoryzacja: Policy update na Item/Activity
  • Zachowanie: Tworzy rezerwację bezpośrednio (bez soft-lock)

POST /api/no-auth/reservations (noAuth)

  • Opis: Tworzenie rezerwacji przez mieszkańca (gość)
  • Autoryzacja: Brak (throttle public-store)
  • Zachowanie: Tworzy WaitingReservation z soft-lock, wysyła email potwierdzający

PATCH /api/reservations/{reservationId}

  • Opis: Aktualizacja rezerwacji (dane klienta, sloty, cena)
  • Autoryzacja: Policy update na Reservation

PATCH /api/reservations/{reservationId}/change-status

  • Opis: Zmiana statusu rezerwacji
  • Autoryzacja: Policy update na Reservation

PATCH /api/reservations/{reservationId}/change-send-notifications

  • Opis: Włączenie/wyłączenie powiadomień dla rezerwacji
  • Autoryzacja: Policy update na Reservation

PUT /api/reservations/{reservationId}/confirm-price

  • Opis: Potwierdzenie ceny rezerwacji przez admina
  • Autoryzacja: Policy update na Reservation

GET /api/reservations/{reservationId}/cancellation-info (auth + noAuth)

  • Opis: Informacje o polityce anulowania i kwotach zwrotu
  • Autoryzacja: Auth: dowolny zalogowany | NoAuth: throttle public-find

GET /api/no-auth/reservations/{reservationId}/find

  • Opis: Wyszukanie rezerwacji przez UUID + access_code (dla gościa)
  • Autoryzacja: Brak (throttle public-find)

POST /api/no-auth/confirm-reservation-email

  • Opis: Potwierdzenie emaila rezerwacji (konwersja WaitingReservation → Reservation)
  • Autoryzacja: Brak (throttle public-store)

3.2 Płatności

GET /api/reservation-payments (auth + noAuth)

  • Opis: Lista płatności z filtrami
  • Autoryzacja: Auth: Admin/Supervisor/Employee | NoAuth: throttle public-find

POST /api/reservation-payments (auth + noAuth)

  • Opis: Tworzenie nowej płatności (online P24, manual, free, dopłata)
  • Autoryzacja: Auth: Policy update na Reservation | NoAuth: throttle public-store

GET /api/reservation-payments/{paymentId}

  • Opis: Szczegóły płatności
  • Autoryzacja: Admin, Supervisor, Employee

POST /api/reservation-payments/{paymentId}/mark-as-completed

  • Opis: Ręczne oznaczenie płatności jako opłaconej (manual payments)
  • Autoryzacja: Policy update na ReservationPayment

POST /api/reservation-payments/{paymentId}/cancel

  • Opis: Anulowanie oczekującej płatności
  • Autoryzacja: Policy update na ReservationPayment

POST /api/reservation-payments/{paymentId}/refund

  • Opis: Inicjowanie zwrotu (pełny lub częściowy)
  • Autoryzacja: Policy update na ReservationPayment

GET /api/reservation-payments/{paymentId}/receipt (auth + noAuth)

  • Opis: Pobranie paragonu PDF
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-find

POST /api/reservation-payments/{paymentId}/send-receipt

  • Opis: Wysłanie paragonu emailem
  • Autoryzacja: Policy update na ReservationPayment

POST /api/reservation-payments/{paymentId}/verify-p24-status (auth + noAuth)

  • Opis: Weryfikacja statusu transakcji P24
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-store

POST /api/reservation-payment-callback (auth + noAuth)

  • Opis: Webhook callback z Przelewy24 po płatności
  • Autoryzacja: Throttle payment-callback, weryfikacja sygnatury SHA384

3.3 Zwroty

GET /api/reservation-payment-refunds (auth + noAuth)

  • Opis: Lista zwrotów z filtrami
  • Autoryzacja: Auth: Admin/Supervisor/Employee | NoAuth: throttle public-find

POST /api/reservation-payment-refunds/{refundId}/confirm

  • Opis: Potwierdzenie ręcznego zwrotu (pending_manual → completed)
  • Autoryzacja: Policy update na ReservationPaymentRefund

GET /api/reservation-payment-refunds/{refundId}/download (auth + noAuth)

  • Opis: Pobranie potwierdzenia zwrotu PDF
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-find

POST /api/reservation-payment-refunds/{refundId}/send

  • Opis: Wysłanie potwierdzenia zwrotu emailem
  • Autoryzacja: Policy update na ReservationPaymentRefund

POST /api/reservation-payment-refund-callback (auth + noAuth)

  • Opis: Webhook callback z P24 po zwrocie
  • Autoryzacja: Throttle payment-callback, weryfikacja sygnatury

3.4 Sloty

GET /api/reservation-slots (auth + noAuth)

  • Opis: Lista slotów rezerwacji
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-find

GET /api/reservation-slots/{slotId} (auth + noAuth)

  • Opis: Szczegóły slotu
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-find

POST /api/cancel-reservation-slots (auth + noAuth)

  • Opis: Anulowanie wybranych slotów rezerwacji
  • Autoryzacja: Auth: Policy update na Reservation | NoAuth: throttle public-store

3.5 Załączniki

GET /api/reservation-attachments (auth + noAuth)

  • Opis: Lista załączników rezerwacji
  • Autoryzacja: Auth: dowolny | NoAuth: throttle public-find

POST /api/reservation-attachments (auth + noAuth)

  • Opis: Dodanie załącznika (zdjęcia, PDF, dokumenty)
  • Autoryzacja: Auth: Policy update na Reservation | NoAuth: throttle public-store
  • Akceptowane formaty: JPEG, PNG, GIF, WebP, MP4, MOV, AVI, PDF, DOC, DOCX

DELETE /api/reservation-attachments/{attachmentId} (auth + noAuth)

  • Opis: Usunięcie załącznika
  • Autoryzacja: Auth: Policy update na Reservation | NoAuth: throttle public-store

3.6 Logi płatności

GET /api/reservation-paid-logs

  • Opis: Lista logów zmian płatności (audyt)
  • Autoryzacja: Admin, Supervisor, Employee

4. Logika biznesowa

4.1 Główne procesy

Proces tworzenia rezerwacji (mieszkaniec / noAuth)

  1. Walidacja danych (formularz, dostępność slotów, deadline, blokady)
  2. Obliczenie ceny na podstawie cennika (CalculatePriceForReservationable)
  3. Utworzenie WaitingReservation z WaitingReservationSlot (soft-lock)
  4. Każdy WaitingReservationSlot tworzy wpis w Schedule (blokuje termin)
  5. Wysłanie emaila z linkiem potwierdzającym (ReservationEmailConfirmationNotification)
  6. Po kliknięciu linku:
  7. Walidacja tokenu i ważności (confirmation_token, expires_at)
  8. Usunięcie WaitingReservation (i wpisów Schedule)
  9. Utworzenie Reservation z ReservationSlot (nowe wpisy Schedule)
  10. Wygenerowanie identyfikatorów (UUID, access_code)
  11. Przypisanie/utworzenie klienta (FindOrCreateClientForReservation)
  12. Zbudowanie snapshotu (BuildReservationSnapshots)
  13. Ustalenie statusu płatności (DetermineReservationPricingAndStatus)
  14. Wysłanie powiadomień (SendNewReservationNotifications)

Proces tworzenia rezerwacji (admin / auth)

  1. Walidacja danych (formularz, dostępność slotów)
  2. Bezpośrednie utworzenie Reservation z ReservationSlot (bez soft-lock)
  3. Wygenerowanie identyfikatorów, snapshot, ustalenie ceny
  4. Powiadomienia do klienta i managera

Proces płatności online (Przelewy24)

  1. Utworzenie ReservationPayment (status: pending)
  2. Rejestracja transakcji w P24 (P24PaymentCreator)
  3. Wygenerowanie URL płatności i podpisu SHA384
  4. Klient przekierowany do P24
  5. P24 wysyła callback (ProcessPrzelewy24Callback):
  6. Weryfikacja sygnatury (P24CallbackValidator)
  7. Weryfikacja statusu transakcji (VerifyP24TransactionStatus)
  8. Oznaczenie płatności jako completed (MarkPaymentAsCompleted)
  9. Dystrybucja kwoty do slotów (DistributePaymentToSlots)
  10. Aktualizacja payment_status rezerwacji
  11. Wygenerowanie paragonu (ReceiptService)
  12. Wysłanie powiadomień (SendPaymentConfirmedNotification)

Proces zwrotu

  1. Walidacja (RefundValidator): status completed/partially_refunded, kwota ≤ available
  2. Ustalenie metody zwrotu (DetermineRefundMethod)
  3. Utworzenie ReservationPaymentRefund
  4. Jeśli gateway: wysłanie do P24 (InitiateGatewayRefundCallP24RefundApi)
  5. Jeśli manual: status pending_manual, oczekuje na potwierdzenie admina
  6. Po zakończeniu (CompleteRefund):
  7. Aktualizacja refunded_amount na slotach (UpdateSlotsRefundedAmountsFromRefund)
  8. Aktualizacja payment_status płatności i rezerwacji
  9. Wygenerowanie potwierdzenia zwrotu (RefundConfirmationService)
  10. Wysłanie powiadomień (SendRefundProcessedNotification)

4.2 Reguły biznesowe

  1. Rezerwacja nie może kolidować z istniejącą rezerwacją — system sprawdza harmonogram
  2. Anulowanie jest możliwe tylko w statusach pending lub confirmed
  3. Edycja rezerwacji możliwa tylko w statusach pending lub confirmed
  4. Slot w przeszłości nie może być zarezerwowany (SlotNotInPastRule)
  5. Rezerwacja wymaga deadline'u — X godzin przed terminem (ReservationDeadlineRule)
  6. Minimalny zwrot to 0.01 PLN (MIN_REFUND_AMOUNT)
  7. Maksymalnie 50 oczekujących zwrotów na płatność (MAX_PENDING_REFUNDS_PER_PAYMENT)
  8. Duplikaty zwrotów wykrywane w oknie 60 sekund (DUPLICATE_WINDOW_SECONDS)
  9. Zwrot bramkowy: max 3 próby z opóźnieniem 2 sekund (MAX_RETRY_ATTEMPTS, RETRY_DELAY_SECONDS)
  10. Niezalogowany użytkownik przy anulowaniu slotów — zwrot zawsze pending_manual
  11. Gdy refunded_amount >= price slotu → automatyczne anulowanie slotu
  12. Gdy wszystkie sloty anulowane → automatyczne anulowanie rezerwacji
  13. Dopłata: jeśli amount > remaining, system automatycznie zwiększa total_price
  14. Blokada obiektu anuluje tylko kolidujące sloty, nie całe rezerwacje
  15. Odwołanie zajęć anuluje wszystkie rezerwacje powiązane z danym ActivityItem
  16. Cena nie może być zmniejszona poniżej opłaconej kwoty (wymaga zwrotu)
  17. Rezerwacja wymaga akceptacji regulaminu obiektu, jeśli obiekt go posiada (ReservationableStatuteRequiredRule)

4.3 Walidacje (Form Requests)

StoreReservationControllerRequest — tworzenie rezerwacji

Pole Reguły
reservationable_type required, string, in:Item,ActivityItem
reservationable_id required, uuid, exists
first_name required, string, max:255
last_name required, string, max:255
email required, email, max:255
mobile_phone required, string, max:32
is_company sometimes, boolean
company_name required_if:is_company,true
nip required_if:is_company,true
slots required, array, min:1
slots.*.date required, date
slots.*.time_from required, date_format:H:i
slots.*.time_to required, date_format:H:i, after:slots.*.time_from
accepted_rodo required, boolean, accepted
accepted_terms required, boolean, accepted

Custom Rules: - SlotAvailabilityRule — sprawdza czy termin nie jest zajęty - SlotNotInPastRule — termin nie może być w przeszłości - ReservationDeadlineRule — deadline rezerwacji - ItemBlockConflictRule — brak kolizji z blokadami - ReservationableAllowedRule — obiekt pozwala na rezerwację - ReservationableStatuteRequiredRule — regulamin obiektu wymagany - FullDayReservationRule — rezerwacja pełnodniowa (jeśli dotyczy) - ActivityItemHasAvailableSlotsRule — wolne miejsca na zajęciach - VerifyReservationCorrectDataRule — spójność danych

RefundReservationPaymentControllerRequest — zwrot

Pole Reguły
amount required, numeric, min:0.01
reason required, string, max:500
refund_method required, in:gateway,transfer,cash,auto
slot_id nullable, uuid, exists:reservation_slots,id

Custom Rules: - MaxRefundAmountPaymentRule — kwota ≤ dostępna na płatności - MaxRefundAmountReservationRule — kwota ≤ dostępna na rezerwacji


5. Autoryzacja i uprawnienia

Polityki (Policies)

Model Akcja Admin/Supervisor Employee Warunki
Reservation view ✅ (ta sama organizacja) belongsToOrganization
Reservation update ✅ (ta sama organizacja) ✅ (manager obiektu) Supervisor: organizacja, Employee: isItemManager
ReservationPayment view ✅ (ta sama organizacja) Przez rezerwację
ReservationPayment update ✅ (ta sama organizacja) ✅ (manager obiektu) Przez rezerwację
ReservationPaymentRefund update ✅ (ta sama organizacja) ✅ (manager obiektu) Przez payment → rezerwacja
ReservationSlot update ✅ (ta sama organizacja) ✅ (manager obiektu) Przez rezerwację
ReservationPaidLog view ✅ (ta sama organizacja) Przez rezerwację
ReservationPaidLog update Logi niemodyfikowalne
ReservationPaymentReceipt update ✅ (ta sama organizacja) ✅ (manager obiektu) Przez payment → rezerwacja

Middleware

Kontroler Metody chronione Rola
ReservationController index, show, update, changeStatus, changeSendNotifications, confirmPrice Admin, Supervisor, Employee
ReservationPaymentController show, markAsCompleted, sendReceipt, cancel, refund Admin, Supervisor, Employee
ReservationPaymentRefundController confirm, send Admin, Supervisor, Employee
ReservationPaidLogController index Admin, Supervisor, Employee
ReservationSlotController — (brak middleware) Dostęp przez Policy
ReservationAttachmentController — (brak middleware) Dostęp przez Policy

6. Eventy i efekty uboczne

Observer: ReservationObserver

Hook Co robi
creating Wywołuje CreatingReservationObserver — generowanie UUID, access_code, przypisanie klienta
updated Wywołuje UpdatedReservationObserver — obsługa zmian statusu, harmonogramu, powiadomień
deleting Wywołuje DeletingReservationObserver — czyszczenie harmonogramów i powiązanych danych

Observer: StatusLogObserver

Automatycznie loguje każdą zmianę statusu do tabeli status_logs (polimorficzna relacja).

Atrybuty wyliczane (Attr)

Atrybut Model Opis
is_paid Reservation payment_status ∈ [paid, partially_refunded, not_required]
paid_at Reservation Data najnowszej potwierdzonej płatności
is_refund Reservation payment_status ∈ [refunded, partially_refunded]
refund_at Reservation Data najnowszego zwrotu
total_paid Reservation payments_amount - total_refunded
total_refunded Reservation Suma completed refundów
available_for_refund Reservation total_paid - total_already_refunded
can_add_review Reservation Status confirmed + termin zakończony + brak opinii
can_cancel_reservation Reservation Status cancellable + deadline nie minął
is_completed, is_pending, is_expired ReservationPayment Na podstawie statusu i expires_at
total_refunded, available_for_refund ReservationPayment Z relacji refundów
has_receipt ReservationPayment Czy istnieje paragon
is_active, is_cancelled ReservationSlot Na podstawie statusu
is_fully_paid, is_partially_paid ReservationSlot paid_amount vs price
available_for_refund ReservationSlot paid_amount - refunded_amount
is_completed, is_pending ReservationPaymentRefund Na podstawie statusu
is_pending_gateway, is_pending_manual ReservationPaymentRefund Szczegółowe statusy pending
has_confirmation, is_confirmation_sent ReservationPaymentRefund Czy jest/wysłano potwierdzenie
can_confirm_manual ReservationPaymentRefund Czy można ręcznie potwierdzić

7. Powiadomienia

System wysyła automatyczne powiadomienia na każdym etapie:

Powiadomienie Klasa Kiedy Odbiorca
Potwierdzenie emaila ReservationEmailConfirmationNotification Po złożeniu rezerwacji (noAuth) Klient
Rezerwacja utworzona ReservationCreatedNotification Po potwierdzeniu emaila / utworzeniu Klient
Potwierdzenie płatności ReservationPaymentConfirmedNotification Po opłaceniu (callback P24 / manual) Klient
Płatność oczekująca ReservationPaymentPendingNotification Po utworzeniu płatności P24 Klient
Płatność nieudana ReservationPaymentFailedNotification Po nieudanej płatności P24 Klient
Przypomnienie o płatności ReservationPaymentReminderNotification CRON — przed terminem płatności Klient
Przypomnienie o rezerwacji ReservationReminderNotification CRON — dzień przed terminem Klient
Paragon (e-receipt) ReservationReceiptNotification Po wygenerowaniu paragonu Klient
Potwierdzenie ceny ReservationPriceConfirmedNotification Po potwierdzeniu ceny przez admina Klient
Anulowanie rezerwacji ReservationCancelledNotification Po anulowaniu Klient
Anulowanie (blokada obiektu) ReservationCancelledDueToItemBlockNotification Po blokadzie obiektu Klient
Anulowanie (odwołanie zajęć) ReservationCancelledDueToActivityCancellationNotification Po odwołaniu zajęć Klient
Zwrot przetworzony ReservationRefundProcessedNotification Po zakończeniu zwrotu Klient
Potwierdzenie zwrotu (PDF) RefundConfirmationNotification Po wysłaniu potwierdzenia zwrotu Klient
Prośba o opinię ReservationReviewReminderNotification CRON — po zakończeniu rezerwacji Klient
Nowa rezerwacja (manager) NewReservationForItemManagerNotification Po utworzeniu Manager obiektu
Anulowanie slotu (manager) SlotCancelledForManagerNotification Po anulowaniu slotu Manager obiektu
Nowy uczestnik (trener) NewParticipantForTrainerNotification Po zapisie na zajęcia Trener

8. Komendy CRON

Komenda Opis Harmonogram
reservation:send-reminders Wysyłanie przypomnień o nadchodzących rezerwacjach Codziennie
reservation:send-payment-reminders Przypomnienia o nieopłaconych rezerwacjach Codziennie
reservation:send-review-reminders Przypomnienia o dodaniu opinii (po zakończeniu) Codziennie
reservation:cleanup-expired-payments Oznaczanie wygasłych płatności P24 jako failed Co kilka minut
reservation:cleanup-waiting Usuwanie wygasłych WaitingReservation (soft-lock cleanup) Co kilka minut
reservation:cleanup-payment-logs Usuwanie starych logów płatności Codziennie/tygodniowo
reservation:reconcile-pending-refunds Uzgadnianie statusów oczekujących zwrotów P24 Codziennie
reservation:create-bulk Masowe tworzenie rezerwacji (narzędzie) Na żądanie

9. Powiązania z innymi domenami

Domena Powiązanie
Zasoby (Item) Rezerwacja dotyczy konkretnego obiektu — polimorficzna relacja reservationable
Aktywności (Activity) Zapisy na zajęcia poprzez ActivityItem — polimorficzna relacja reservationable
Cenniki (Pricing) System pobiera aktualny cennik (pricing_id) dla kalkulacji ceny
Klienci (Client) Automatyczne tworzenie/przypisanie klienta (FindOrCreateClientForReservation)
Organizacje (Organization) Każda rezerwacja należy do organizacji
Harmonogramy (Schedule) Każdy slot tworzy wpis w kalendarzu (sprawdzanie kolizji)
Powiadomienia (Notification) 18 typów powiadomień email w cyklu życia
Opinie (Review) Klient może dodać opinię po zakończeniu rezerwacji (hasOne)
Reklamacje (Complaint) Klient może zgłosić reklamację (hasMany)
Notatki (Note) Wewnętrzne notatki do rezerwacji (morphMany)
Dziennik statusów (StatusLog) Każda zmiana statusu logowana (morphMany, StatusLogObserver)
Media Załączniki do rezerwacji (Spatie MediaLibrary) + potwierdzenia zwrotów PDF
Użytkownicy (User) price_confirmed_by, cancelled_by, created_by, processed_by
Dokumenty (Document) Generowanie paragonów i potwierdzeń zwrotów (AbstractDocumentGenerator)

10. Konfiguracja

Konfiguracja Przelewy24

Plik: config/transfers24.php

Parametr Env Variable Opis
merchant_id PRZELEWY24_MERCHANT_ID ID sprzedawcy
pos_id PRZELEWY24_POS_ID ID punktu sprzedaży
crc PRZELEWY24_CRC Klucz CRC do podpisów
report_key PRZELEWY24_REPORT_KEY Klucz raportowy
test_server PRZELEWY24_TEST_SERVER Tryb testowy (default: true)
url_return /payment/return (frontend)
url_status /api/no-auth/reservation-payment-callback (webhook)
url_refund_status /api/no-auth/reservation-payment-refund-callback
time-limit 15 minut na transakcję
encoding UTF-8

Polityka anulowania

Każdy obiekt (Item) może mieć własną politykę:

Parametr Opis Przykład
cancellation_deadline_hours Ile godzin przed terminem można anulować 24
cancellation_policy_description Tekst polityki wyświetlany klientowi "Anulowanie możliwe do 24h przed..."

Terminy rezerwacji

Parametr Opis Przykład
reservation_deadline_hours Ile godzin przed terminem można rezerwować 2
reservation_start_time Od której godziny możliwe rezerwacje 06:00
reservation_end_time Do której godziny możliwe rezerwacje 22:00

Stałe konfiguracyjne (ReservationEnumService)

Stała Wartość Opis
MIN_REFUND_AMOUNT 0.01 Minimalny zwrot (PLN)
MAX_RETRY_ATTEMPTS 3 Max prób zwrotu P24
RETRY_DELAY_SECONDS 2 Opóźnienie między próbami
DUPLICATE_WINDOW_SECONDS 60 Okno wykrywania duplikatów
MAX_PENDING_REFUNDS_PER_PAYMENT 50 Max oczekujących zwrotów na płatność

11. Statusy i enumy

Statusy rezerwacji

stateDiagram-v2
    [*] --> pending: Utworzenie
    pending --> confirmed: Potwierdzenie email
    pending --> canceled: Anulowanie
    pending --> rejected: Odrzucenie
    confirmed --> canceled: Anulowanie
    confirmed --> [*]: Zakończenie
    canceled --> [*]
    rejected --> [*]
Status Opis
pending Rezerwacja oczekuje na potwierdzenie emaila
confirmed Rezerwacja potwierdzona, oczekuje na płatność lub realizację
canceled Rezerwacja anulowana (przez klienta, admina lub system)
rejected Rezerwacja odrzucona przez administratora

Statusy płatności rezerwacji (payment_status)

Status Opis
not_required Płatność nie jest wymagana (rezerwacja bezpłatna)
pending Oczekuje na płatność
partially_paid Częściowo opłacona (przy płatnościach ratalnych / dopłatach)
paid W pełni opłacona
refunded Zwrot pełnej kwoty
partially_refunded Częściowy zwrot

Statusy transakcji (ReservationPayment.status)

Status Opis
pending Oczekuje na płatność
completed Opłacona
failed Nieudana
cancelled Anulowana
refunded Pełny zwrot
partially_refunded Częściowy zwrot
reverted Cofnięta

Statusy zwrotów (ReservationPaymentRefund.status)

Status Opis
pending Oczekuje na przetworzenie
pending_gateway Wysłano do P24, oczekuje na callback
pending_manual Oczekuje na ręczne potwierdzenie admina
completed Zwrot zakończony
failed Zwrot nieudany

Metody płatności

Metoda Opis
przelewy24 Płatność online przez Przelewy24 (BLIK, karta, przelew)
manual Płatność ręczna (gotówka, przelew poza systemem)
free Bezpłatna rezerwacja

Metody zwrotu

Metoda Opis
gateway Automatyczny zwrot przez Przelewy24
transfer Ręczny przelew bankowy
cash Zwrot gotówką
auto Automatyczny zwrot systemowy
undefined Metoda nieokreślona (oczekuje na wybór)

Typy zwrotu

Typ Opis
full Pełny zwrot (anulowanie przez klienta/admina)
partial Częściowy zwrot
slot_cancellation Zwrot za anulowanie pojedynczego slotu
forced_cancellation Wymuszone anulowanie
item_block Zwrot z powodu blokady obiektu
activity_cancellation Zwrot z powodu odwołania zajęć
manual Ręczny zwrot

Typy anulowania

Typ Opis
by_resident Anulowane przez mieszkańca
by_admin Anulowane przez admina
forced Wymuszone anulowanie
due_to_item_block Z powodu blokady obiektu
due_to_activity_cancellation Z powodu odwołania zajęć

Akcje w PaidLog

23 typy akcji logowanych w reservation_paid_logs:

Akcja Opis
created Utworzenie rezerwacji
paid Opłacenie
refund Zwrot
payment_created Utworzenie płatności
payment_completed Zakończenie płatności
payment_failed Nieudana płatność
payment_cancelled Anulowanie płatności
refund_initiated Inicjacja zwrotu
refund_completed Zakończenie zwrotu
price_confirmed Potwierdzenie ceny
price_changed Zmiana ceny
price_zeroed Wyzerowanie ceny
price_increased_for_doplata Zwiększenie ceny (dopłata)
price_reduced_after_cancellation Zmniejszenie ceny po anulowaniu płatności
cancelled_by_resident Anulowanie przez mieszkańca
cancelled_by_admin Anulowanie przez admina
cancelled_forced Wymuszone anulowanie
cancelled_due_to_item_block Anulowanie (blokada obiektu)
cancelled_due_to_activity_cancellation Anulowanie (odwołanie zajęć)
slots_cancelled Anulowanie slotów
manual_payment_marked Ręczne oznaczenie płatności
manual_payment_reverted Cofnięcie ręcznej płatności
manual_refund_marked Ręczne oznaczenie zwrotu
free_reservation_confirmed Potwierdzenie bezpłatnej rezerwacji

12. Dopłaty (Multiple Payments)

System obsługuje scenariusz dopłat, gdy rezerwacja ma już częściowe lub pełne opłaty.

Jak to działa?

sequenceDiagram
    participant A as Admin
    participant S as System
    participant R as Rezerwacja
    participant P as Płatność
    participant SL as Sloty

    Note over R: total_price=2952, total_paid=2952 (w pełni opłacona)
    A->>S: Dodaj dopłatę (amount=1000, mark_completed=true)
    S->>S: Auto-zwiększa total_price do 3952
    S->>P: Tworzy płatność (completed, 1000 PLN)
    S->>SL: Dystrybuuje 1000 PLN do slotów
    S->>R: Aktualizuje payment_status
    Note over R: total_price=3952, total_paid=3952

Parametry API dla dopłat

{
  "reservation_id": "uuid",
  "payment_method": "manual",
  "amount": 1000,
  "mark_completed": true,
  "paid_at": "2026-02-06",
  "reason": "Dopłata za dodatkowy sprzęt"
}

Zasady dopłat

Warunek Zachowanie
Brak completed payments amount = nowa cena całkowita (stare zachowanie)
Są completed payments amount = kwota dopłaty
amount > pozostałe do zapłaty Automatycznie zwiększa total_price
amount nie podane Tworzy płatność na pełną pozostałą kwotę

13. Mechanizm soft-lock

Mechanizm soft-lock (tymczasowej blokady)

Po złożeniu rezerwacji przez niezalogowanego użytkownika, system tworzy WaitingReservation wraz z WaitingReservationSlot dla każdego wybranego terminu. Każdy WaitingReservationSlot tworzy wpis w harmonogramie (Schedule), co blokuje termin dla innych użytkowników — formularz rezerwacji pokazuje go jako niedostępny.

Jeśli użytkownik nie potwierdzi rezerwacji w wyznaczonym czasie, komenda CRON reservation:cleanup-waiting automatycznie czyści wygasłe WaitingReservation wraz z ich slotami i wpisami w harmonogramie — termin wraca jako dostępny.

Zabezpieczenia anty-troll:

  • Konfigurowalny czas na potwierdzenie (domyślnie do 60 min)
  • Rate limiting na endpoint składania rezerwacji (throttle:public-store)
  • Honeypot fields w formularzu (website, company_website, fax_number)

14. Bezpieczeństwo płatności

Weryfikacja callbacków P24

  • Każdy callback weryfikowany podpisem SHA384 (P24CallbackValidator)
  • Porównanie amount, session_id, order_id z oryginalnymi danymi
  • Logowanie prób fraud (PAYMENT_LOG_CHANNEL_SECURITY)
  • Zdarzenia bezpieczeństwa: signature_mismatch, session_mismatch, order_mismatch, amount_mismatch

Logi płatności

Szczegółowe logi w reservation_payment_logs:

Kanał Opis
p24_gateway Komunikacja z Przelewy24
security Zdarzenia bezpieczeństwa
consistency Sprawdzanie spójności danych
payment Ogólne zdarzenia płatności
Poziom Opis
info Normalne operacje
warning Potencjalne problemy
error Błędy
critical Krytyczne błędy bezpieczeństwa

15. Dane audytowe (Snapshot)

Przy tworzeniu rezerwacji system zapisuje snapshot obiektu w polu reservationable_snapshot:

Snapshot dla Item

{
  "_pricing_snapshot": {
    "id": "uuid",
    "name": "Cennik standardowy",
    "prices": [...],
    "vat_rate": "23%"
  },
  "_client_snapshot": {
    "first_name": "Jan",
    "last_name": "Kowalski",
    "email": "jan@example.com"
  },
  "_vat_rate_source": "pricing",
  "name": "Sala gimnastyczna",
  "address": "ul. Szkolna 1"
}

Snapshot dla ActivityItem

{
  "_pricing_snapshot": {...},
  "_client_snapshot": {...},
  "_vat_rate_source": "pricing",
  "activity_name": "Fitness",
  "activity_item_date": "2026-03-01",
  "trainer_name": "Anna Nowak"
}

16. Kluczowe klasy biznesowe

Action/Other — główne klasy

Klasa Opis
CreateReservation Tworzenie rezerwacji (auth)
CreateWaitingReservation Tworzenie tymczasowej rezerwacji (noAuth soft-lock)
UpdateReservation Aktualizacja rezerwacji
CancelReservation Anulowanie rezerwacji
SlotCreator Tworzenie slotów z danymi
SlotValidator Walidacja dostępności slotów
SlotCancellation Logika anulowania slotów
SlotScheduleManager Zarządzanie wpisami harmonogramu
P24PaymentCreator Rejestracja transakcji w P24
P24CallbackHandler Obsługa callbacków P24
P24CallbackValidator Weryfikacja podpisów callbacków
PaymentFactory Tworzenie płatności wg metody
InitiateRefund Inicjowanie procesu zwrotu
InitiateGatewayRefund Zwrot przez P24
CompleteRefund Finalizacja zwrotu
RefundValidator Walidacja parametrów zwrotu
RefundDistributor Dystrybucja zwrotu do slotów
ReceiptService Generowanie/wysyłanie paragonów
ReceiptGenerator Generator pliku PDF paragonu
RefundConfirmationService Generowanie potwierdzeń zwrotów
RefundConfirmationGenerator Generator PDF potwierdzenia zwrotu
DistributePaymentToSlots Dystrybucja płatności do slotów
CalculatePriceForReservationable Obliczanie ceny rezerwacji
BuildReservationSnapshots Budowanie snapshotów danych
FindOrCreateClientForReservation Automatyczne tworzenie/przypisanie klienta
SendNewReservationNotifications Wysyłanie powiadomień o nowej rezerwacji
HandleReservationStatusChange Obsługa zmian statusu rezerwacji

Scopes (Query Scopes)

Scope Model Opis
active() ReservationSlot Sloty ze statusem active
cancelled() ReservationSlot Sloty anulowane
paid() ReservationSlot Sloty opłacone
unpaid() ReservationSlot Sloty nieopłacone
withAvailableRefund() ReservationSlot Sloty z dostępnym zwrotem
pending() ReservationPayment Płatności oczekujące
completed() ReservationPayment Płatności zakończone
notExpired() ReservationPayment Niewygasłe płatności
pending() ReservationPaymentRefund Zwroty oczekujące
completed() ReservationPaymentRefund Zwroty zakończone
pendingManual() ReservationPaymentRefund Zwroty pending_manual
pendingGateway() ReservationPaymentRefund Zwroty pending_gateway
forSlot(slotId) ReservationPaymentRefund Zwroty dla konkretnego slotu
withConfirmation() ReservationPaymentRefund Zwroty z potwierdzeniem
sent() / notSent() ReservationPaymentReceipt Paragony wysłane/niewysłane
withPdf() ReservationPaymentReceipt Paragony z PDF
reservationsWithoutReview() Reservation Rezerwacje bez opinii
canAddComplaint() Reservation Rezerwacje kwalifikujące się do reklamacji

17. Media Collections

Model Reservation rejestruje dwie kolekcje mediów (Spatie MediaLibrary):

Kolekcja Akceptowane typy Opis
attachments JPEG, PNG, GIF, WebP, MP4, MOV, AVI, PDF, DOC, DOCX Załączniki do rezerwacji
refund_confirmations PDF Potwierdzenia zwrotów (z custom property refund_id)

18. Znane ograniczenia i TODO

Ważne ograniczenia

  • Rezerwacje nie mogą kolidować — system automatycznie blokuje możliwość zarezerwowania zajętego terminu
  • W przypadku blokady obiektu (ItemBlock), system anuluje tylko kolidujące sloty z automatycznym zwrotem — pozostałe terminy pozostają aktywne
  • W przypadku odwołania zajęć (ActivityItem is_canceled = true), system anuluje wszystkie rezerwacje powiązane z daną sesją
  • Cena nie może być zmniejszona poniżej już opłaconej kwoty — wymaga zwrotu
  • Niezalogowany użytkownik przy anulowaniu slotów — zwrot zawsze pending_manual