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.
- 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
LEARNEDtworzone 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.
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.
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.
lib/import-engine.ts zawiera czyste funkcje parsera i dopasowania reguł. lib/import.ts odpowiada za zapis:
- identyfikator źródła ma postać
typ:slug-nazwy; ImportLedgerEntrymaUNIQUE(sourceKey, externalId), więc rekord raz przetworzony lub odrzucony nie wraca;- pierwsza aktywna reguła według priorytetu przypisuje kategorię;
- brak dopasowania kieruje rekord do
NEEDS_REVIEW; - ręczne przypisanie tworzy regułę
LEARNEDdla nadawcy.
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.
- 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,bcryptjsizoddla sesji, haseł i walidacji wejścia.
Wersje źródłowe są zapisane w package.json.
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 generateUzupełnij .env:
DATABASE_URL— pooled connection string Neon (-poolerw hoście) do ruchu aplikacji;DIRECT_URL— direct/unpooled connection string do migracji;SESSION_SECRETiCRON_SECRET— dwa różne, losowe sekrety; Vercel zaleca minimum 16 znaków dlaCRON_SECRET;STRIPE_SECRET_KEY— kluczsk_test_...;STRIPE_WEBHOOK_SECRET— sekretwhsec_...z lokalnego listenera albo endpointu produkcyjnego;NEXT_PUBLIC_APP_URL—http://localhost:3000lokalnie, 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 devprisma 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 seedjest resetem danych demonstracyjnych: usuwa dane transakcyjne i odtwarza seed. Nie uruchamiaj go na bazie z prawdziwymi danymi.
Wspólne hasło: Slotify-Demo-2026.
| Rola | 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.
Po zalogowaniu Stripe CLI uruchom:
stripe listen --forward-to localhost:3000/api/stripe/webhookSkopiuj 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.
- Utwórz projekt Vercel z repozytorium i dodaj wszystkie zmienne z
.env.exampledo właściwych środowisk. - Ustaw
NEXT_PUBLIC_APP_URLna produkcyjny adres HTTPS. - Przed pierwszym ruchem wykonaj
npx prisma migrate deploy, a następnie — wyłącznie dla bazy demo —npx prisma db seed. - W Stripe dodaj endpoint
https://TWOJA-DOMENA/api/stripe/webhookz czterema eventami wymienionymi wyżej i wpisz jego sekret do Vercel. - 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.
Sprawdzenia lokalne:
npm run lint
npx tsc --noEmit
npm run buildNa 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.
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.