[16]
FERMENT
▒ TIER C · ROK 2026 · STATUS: DZIAŁA · JĘZYKI: TYPESCRIPT
Sklep z własnym charakterem wizualnym i realnym handlem na funkcjach brzegowych
[FIG. 1] MISJA
Projektuję produkty z własnym charakterem wizualnym zamiast generycznego szablonu sklepu: FERMENT dostał retro-pixelową estetykę — boot-screen jak ze starego terminala, font bitmapowy VCR OSD Mono, pixel-vortex i obracającą się monetę 3D w modelu GLB. Pod tą warstwą wdrażam realny handel: checkout Stripe z payment intent i webhookiem weryfikującym podpis, tworzenie zamówień i pełny ich cykl, panel administracyjny do produktów i zamówień. Integruję też lokalnego operatora logistycznego, nie tylko globalne API — polski kurier Furgonetka wycenia i śledzi przesyłkę. Całą logikę pieniędzy i wysyłki prowadzę funkcjami brzegowymi na Deno (Supabase Edge Functions): pięć funkcji za murem, dzięki czemu sekrety Stripe i token Furgonetki nigdy nie trafiają do przeglądarki. Produkt budowałem przez agenta generatywnego (Lovable) na pełnym stacku Supabase i przy okazji nauczyłem się diagnozować, kiedy błąd jest konfiguracyjny, a nie kodowy — to zupełnie inna ścieżka naprawy. To także wizualne źródło całego języka VCR/boot-terminal tego portfolio. Skala jest niepozorna tylko z wierzchu: 23 migracje SQL z 40 politykami RLS, 11 Edge Functions w Deno, a strona główna to nie lista produktów, lecz mozaika kafli czterech typów (produkt / styl / animacja / gra) o rozmiarach od 1×1 do 4×4 — wszystko wstawiane jednym formularzem admina.
[FIG. 2] ARCHITEKTURA
najedź na blok, by zobaczyć opis
[FIG. 3] WYZWANIA
[+][CH-01]
Płatności zaprojektowałem według zasady zero zaufania do klienta: cena liczy się po stronie serwera (nie jest brana z DOM-u), payment intent powstaje w edge function z kwotą z bazy, a webhook Stripe weryfikuje podpis, zanim domknie zamówienie. Zaliczyłem też realny incydent — webhook Stripe wskazywał na stary URL Vercela z wcześniejszej iteracji projektu, więc zamówienia nie domykały się mimo udanych płatności; diagnozę i naprawę (przekierowanie na Supabase) udokumentowałem w .lovable/plan.md. Od tamtej pory routing webhooków sprawdzam jako pierwszy podejrzany.
[+][CH-02]
Wysyłka okazała się serią zasadzek na dwóch frontach naraz. API Furgonetki wymagało niestandardowego uwierzytelnienia — działający wariant to OAuth z Basic auth danych klienta plus grant_type=password, do tego poprawny format paczki (stały „gabaryt B” InPostu jako uniwersalny wymiar dla odzieży), zanim wycena w ogóle zaczęła zwracać ceny. A widget mapy InPost oddawał kod pocztowy wybranego punktu w losowym polu — postCode, post_code, postalCode, zip — albo wcale, czasem tylko wewnątrz adresu opisowego. Odczyt jest dziś defensywny do bólu: skleja kilkanaście kandydujących pól w jeden string, przepuszcza przez regex \d{2}-\d{3}, a jako ostatnią deskę ratunku skanuje cały obiekt przez JSON.stringify. Osiem commitów o rosnącej desperacji w nazwach („read” → „parse” → „extract” → „improve”) nauczyło mnie nie zakładać kształtu cudzych danych.
[+][CH-03]
Do sklepu wpiąłem pełną, grywalną retro-grę arcade „FERMENT RUN” — samodzielny plik 3194 linii vanilla JS na canvasie, bez żadnego frameworka gier. Silnik ma własną czcionkę bitmapową (każdy znak jako siatka 5×7 zakodowana stringiem, renderowana piksel po pikselu), pętlę delta-time uniezależniającą prędkość od klatkażu urządzenia i wirtualną klawiaturę dla mobile — dopracowywaną w kolejnych poprawkach, bo dotyk „wyciskał” strzałki inaczej niż klawiatura. Tablica wyników jest serwerowa: gra, nie mając klienta supabase-js, uderza wprost w REST API Supabase kluczem anon, a limit dziesięciu rozgrywek trzyma cookie (uczciwie: to bariera UX, nie zabezpieczenie). Kafel gry otwiera pełnoekranowy iframe z animacją wygaszania kineskopu CRT w czterech fazach — bo skoro estetyka terminala, to do końca.
[+][CH-04]
Zamiast statycznych zdjęć część produktów to obracające się modele 3D — i cały pipeline musiał być obsługiwalny przez nietechnicznego admina. Formularz wstawiania przyjmuje plik GLB/GLTF, wrzuca go do Supabase Storage i daje dwanaście kontrolek transformacji: skala, pozycja i rotacja startowa per oś plus niezależne przełączniki auto-obrotu na każdej z trzech osi — z podglądem na żywo. W gridzie sklepu react-three-fiber czyta te parametry i kręci modelem w useFrame z delta-time, a preloader ładuje wszystkie modele z wyprzedzeniem, żeby siatka nie zacinała się przy pierwszym renderze; sam komponent 3D jest lazy-loadowany, więc klient bez modeli nie płaci za three.js. Mobile wymagał osobnej rundy poprawek — WebGL na telefonach wybacza znacznie mniej.
[FIG. 4] WARSTWA AI
W runtime nie działa żaden model — AI zbudowało sam sklep: cały development prowadził agent Lovable (683 z 685 commitów), z artefaktami planowania w repo, łącznie z diagnozą webhooka Stripe, która skończyła się poprawką konfiguracyjną zamiast kodowej. To dla mnie drugi, obok Claude Code, przećwiczony wariant pracy AI-native — i lekcja, czym różnią się oba harnessy w praktyce.
[FIG. 2A] SEKRETY ZA MUREM
PRZEGLĄDARKA
0 SEKRETÓW
Vite/React widzi tylko publiczny klucz anon i ceny do wyświetlenia.
EDGE FUNCTIONS (DENO)
→ najedź na funkcję, by zobaczyć, co robi za murem
[FIG. 5] GALERIA











