Skip to content

DmRzAt/Slotify

Repository files navigation

Slotify

Platforma rezerwacji usług lokalnych z trzema rolami, płatnościami Stripe Connect i generycznym modułem importu danych. Projekt demonstracyjny pokazuje pełny przepływ marketplace: klient wybiera termin i płaci, partner zarządza ofertą i rezerwacjami, a administrator weryfikuje partnerów oraz obsługuje wyjątki importu.

Projekt działa wyłącznie w trybie demonstracyjnym. Dane firm, usług i rezerwacji są fikcyjne, a Stripe musi być skonfigurowany w trybie testowym.

Zakres

  • publiczny landing, wyszukiwarka z filtrami, karta usługi i osobna wizytówka partnera /p/[slug];
  • logowanie e-mail/hasło oraz wejście jednym kliknięciem jako Klient, Partner lub Admin;
  • panel klienta: rezerwacje, anulowanie i oceny;
  • panel partnera: CRUD usług, generator dostępności, obsługa rezerwacji, onboarding Stripe Connect Express i podgląd rozliczeń;
  • panel administratora: weryfikacja partnerów, rezerwacje/płatności i import CSV/JSON/mock API;
  • reguły importu edytowalne bez kodu, trwała deduplikacja, kolejka wyjątków i reguły LEARNED tworzone po ręcznej decyzji;
  • dostępność: skip link, semantyczne formularze i tabele, widoczne focus states, obszary dotykowe min. 44 px, komunikaty live oraz prefers-reduced-motion.

Najważniejsze decyzje techniczne

Rezerwacja bez double-bookingu

reserveSlot() zajmuje termin wewnątrz transakcji przez warunkowy UPDATE ... WHERE status = OPEN. Tylko jeden równoległy request może zmienić jeden rekord; drugi otrzyma konflikt. Niezależnym bezpiecznikiem jest UNIQUE na Booking.slotId. Nieopłacony termin jest trzymany 30 minut, czyli tyle samo co sesja Checkout, a następnie zwalniany przez webhook lub cron.

Stripe Connect

Partner przechodzi hostowany onboarding Express w /api/stripe/connect. Gdy konto ma chargesEnabled, Checkout tworzy destination charge: 90% ceny trafia do partnera, a 10% pozostaje jako opłata platformy. Wartości wynikają z Math.round(amountCents * 0.1) i amountCents - fee w /api/bookings. Stripe opisuje destination charges jako płatność platformową z natychmiastowym transferem części środków do jednego connected account; odpowiedzialność za opłaty, zwroty i chargebacki pozostaje po stronie platformy. Zobacz Stripe: Connect charge types.

Webhook /api/stripe/webhook weryfikuje podpis i zapisuje każde zdarzenie w tabeli WebhookEvent z unikalnym stripeEventId, dzięki czemu ponowne dostarczenie przetworzonego eventu nie powtarza operacji.

Import zewnętrzny

lib/import-engine.ts zawiera czyste funkcje parsera i dopasowania reguł. lib/import.ts odpowiada za zapis:

  1. identyfikator źródła ma postać typ:slug-nazwy;
  2. ImportLedgerEntry ma UNIQUE(sourceKey, externalId), więc rekord raz przetworzony lub odrzucony nie wraca;
  3. pierwsza aktywna reguła według priorytetu przypisuje kategorię;
  4. brak dopasowania kieruje rekord do NEEDS_REVIEW;
  5. ręczne przypisanie tworzy regułę LEARNED dla nadawcy.

Dane i role

Schemat ma 14 modeli/tabel: User, PartnerProfile, Service, AvailabilitySlot, Booking, Payment, Review, WebhookEvent, Notification, ImportBatch, ImportCategory, ImportRule, ImportRecord i ImportLedgerEntry. Pełne relacje i indeksy są w prisma/schema.prisma.

Sesja to podpisany JWT HS256 w cookie HttpOnly, SameSite=Lax, z flagą Secure w produkcji i ważnością 7 dni; implementacja znajduje się w lib/auth.ts. Autoryzacja jest ponownie sprawdzana w route handlerach, nie tylko w warstwie routingu.

Stack

  • Next.js 16.2.10, React 19.2.4, TypeScript 5 i Tailwind CSS 4;
  • PostgreSQL na Neon oraz Prisma ORM 6.19.3 — wersja 6 jest przypięta świadomie;
  • Stripe 22.3.2, Stripe Checkout i Connect Express;
  • jose, bcryptjs i zod dla sesji, haseł i walidacji wejścia.

Wersje źródłowe są zapisane w package.json.

Uruchomienie lokalne

Wymagania: Node.js 20.9+ (minimum wskazane w lokalnej dokumentacji zainstalowanego Next.js 16), projekt PostgreSQL w Neon i konto Stripe w trybie testowym. Zobacz także Next.js: installation.

npm ci
Copy-Item .env.example .env
npx prisma generate

Uzupełnij .env:

  • DATABASE_URL — pooled connection string Neon (-pooler w hoście) do ruchu aplikacji;
  • DIRECT_URL — direct/unpooled connection string do migracji;
  • SESSION_SECRET i CRON_SECRET — dwa różne, losowe sekrety; Vercel zaleca minimum 16 znaków dla CRON_SECRET;
  • STRIPE_SECRET_KEY — klucz sk_test_...;
  • STRIPE_WEBHOOK_SECRET — sekret whsec_... z lokalnego listenera albo endpointu produkcyjnego;
  • NEXT_PUBLIC_APP_URLhttp://localhost:3000 lokalnie, docelowy adres HTTPS na produkcji.

Neon udostępnia oba warianty connection stringa w oknie Connect; host połączenia pooled zawiera -pooler. Neon zaleca direct connection dla migracji ORM. Zobacz Neon: connection pooling i Neon: ręczne połączenie z Vercel.

Zastosuj przygotowaną migrację i odtwórz dane demo:

npx prisma migrate deploy
npx prisma db seed
npm run dev

prisma migrate deploy stosuje oczekujące migracje, a prisma db seed uruchamia skrypt skonfigurowany w package.json; potwierdzają to dokumentacje Prisma Migrate i Prisma v6: seeding.

prisma db seed jest resetem danych demonstracyjnych: usuwa dane transakcyjne i odtwarza seed. Nie uruchamiaj go na bazie z prawdziwymi danymi.

Konta demo

Wspólne hasło: Slotify-Demo-2026.

Rola E-mail Start
Klient klient@demo.slotify.pl /klient
Partner — Studio Blask partner@demo.slotify.pl /partner
Administrator admin@demo.slotify.pl /admin

Te same konta są dostępne przez przyciski „Zaloguj jako…” bez wpisywania hasła. Definicje są w lib/demo.ts, a tworzenie danych w lib/seed-demo.ts.

Webhook Stripe lokalnie

Po zalogowaniu Stripe CLI uruchom:

stripe listen --forward-to localhost:3000/api/stripe/webhook

Skopiuj pokazany sekret whsec_... do STRIPE_WEBHOOK_SECRET. Flaga --forward-to przekazuje eventy z sandboxa do lokalnego endpointu zgodnie z dokumentacją Stripe webhooków.

Obsługiwane eventy:

  • checkout.session.completed;
  • checkout.session.expired;
  • payment_intent.payment_failed;
  • account.updated.

Do testowej płatności użyj numeru karty 4242 4242 4242 4242, dowolnej przyszłej daty i dowolnego CVC — to karta sukcesu opisana w dokumentacji testowej Stripe.

Deploy na Vercel

  1. Utwórz projekt Vercel z repozytorium i dodaj wszystkie zmienne z .env.example do właściwych środowisk.
  2. Ustaw NEXT_PUBLIC_APP_URL na produkcyjny adres HTTPS.
  3. Przed pierwszym ruchem wykonaj npx prisma migrate deploy, a następnie — wyłącznie dla bazy demo — npx prisma db seed.
  4. W Stripe dodaj endpoint https://TWOJA-DOMENA/api/stripe/webhook z czterema eventami wymienionymi wyżej i wpisz jego sekret do Vercel.
  5. Wdróż ponownie po każdej zmianie zmiennych środowiskowych.

vercel.json rejestruje dwa zadania raz dziennie, o 03:00 i 03:30 UTC. Vercel automatycznie wysyła wartość CRON_SECRET w nagłówku Bearer; endpointy odrzucają zarówno brak sekretu, jak i błędny nagłówek. Na planie Hobby harmonogram może wykonać się w dowolnym momencie wskazanej godziny i nie może działać częściej niż raz dziennie. Zobacz Vercel: managing Cron Jobs.

Kontrola jakości

Sprawdzenia lokalne:

npm run lint
npx tsc --noEmit
npm run build

Na 19 lipca 2026 wszystkie trzy polecenia przechodzą. Produkcyjny build raportuje 42 ścieżki aplikacji: liczba pochodzi z listy Route (app) w next build i obejmuje strony oraz route handlery, ale nie osobny wpis Proxy.

E2E głównych przepływów i Lighthouse zostały wykonane na działającej bazie Neon. Pozostał pełny test Stripe Checkout z lokalnym listenerem oraz poprawnym sekretem webhooka.

Struktura

app/                 strony App Router i route handlery
components/          formularze, akcje klienckie i wspólne UI
lib/                 auth, rezerwacje, Stripe, import i seed
prisma/              schema oraz migracja 0_init
proxy.ts             kontrola dostępu na wejściu do paneli
vercel.json          harmonogram resetu demo i zwalniania holdów

Gotowe opisy do portfolio i ofert znajdują się w PORTFOLIO_COPY.md.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors