P R O J E K T YK O N T A K T

[12]

BAZARTY.COM (PAYLOAD + MONGODB)

TIER B · ROK 2026 · STATUS: DZIAŁA · JĘZYKI: TYPESCRIPT

Rozbudowa zastanej bazy kodu: multi-tenant, Stripe Connect, escrow

[FIG. 1] MISJA

Umiem wejść w zastaną, dojrzałą bazę kodu i rozbudować ją bez łamania jej konwencji — a to zdarza się w pracy częściej niż greenfield. bazarty.com istnieje w dwóch architekturach — to rozbudowany wariant na Payload CMS + MongoDB (repo: marketplace-maci); wariant Supabase opisany jest osobno. Ten projekt zacząłem właśnie od cudzej, dojrzałej bazy (tutorial Antonia: multi-tenant + Stripe Connect). Najpierw zrobiłem reverse-engineering bazy do .planning/codebase/* (architektura, konwencje, integracje), a jej wzorce zamknąłem we własnych skillach Claude generujących kod zgodny z zastanymi regułami (payload-collection dla kolekcji Payload, trpc-router dla routerów tRPC). Dopiero na tym dołożyłem realny handel: czat kupujący↔sprzedawca na websocketach, pełny cykl zamówienia (potwierdzenie / spór / recenzja), maile transakcyjne, oceny, bibliotekę zakupów i ~25 stron marketingowo-prawnych. Architekturę prowadzę jako multi-tenant z izolacją danych najemców: każdy sprzedawca ma sklep na własnej subdomenie, a płatności z podziałem środków obsługuje Stripe Connect, który automatycznie zdejmuje prowizję platformy. Stack: Next.js 15 + React 19, Payload CMS + MongoDB, type-safe tRPC 11. Przegląd kodu przez AI wpiąłem w CI, żeby jakość nie zależała od mojej dyscypliny w danym dniu. Sercem platformy jest zaufanie w transakcji między nieznajomymi: własny mechanizm escrow wstrzymuje wypłatę sprzedawcy do potwierdzenia odbioru (auto-accept po 48 h, release wypłaty 2 dni później przez cron), a zwrot przy reklamacji jest zawsze pokryty. Skala: 19 kolekcji Payload, 17 routerów tRPC, ~41 tysięcy linii TypeScriptu i ponad 900 commitów — z czego zdecydowana większość to moja samodzielna rozbudowa forka.

[FIG. 2] ARCHITEKTURA

najedź na blok, by zobaczyć opis

[FIG. 3] WYZWANIA

[+][CH-01]

Rozbudowa cudzej dojrzałej bazy to inna dyscyplina niż pisanie od zera: każda moja zmiana mogła po cichu złamać konwencję, o której istnieniu nie wiedziałem. Zanim dopisałem pierwszą linijkę, zrobiłem reverse-engineering bazy do .planning/codebase/* — ARCHITECTURE, CONVENTIONS, INTEGRATIONS — a wzorce (kolekcja Payload, router tRPC) zamknąłem we własnych skillach Claude, które generują nowy kod od razu w stylu zastanego. Konwencje przestały być wiedzą plemienną — stały się egzekwowalne.

[+][CH-02]

Dla rękodzieła stock to często dokładnie 1 sztuka — a dwóch kupujących klikających „kup” w tej samej chwili nie może obaj jej dostać. Payload local API robi read-modify-write (find → sprawdź stan → update), czyli klasyczny wyścig TOCTOU: obie sesje czytają stock: 1, obie przechodzą walidację, obie dekrementują. Zamiast walczyć z frameworkiem, w tym jednym miejscu świadomie zszedłem poniżej ORM do surowego sterownika Mongo: warunkowy atomowy dekrement, w którym warunek $gte i $inc wykonują się w jednej niepodzielnej operacji — modifiedCount === 1 znaczy „zarezerwowane”, zero nadsprzedaży. Rezerwacja wpina się przed utworzeniem sesji Stripe, a rollback zwraca stan przy wygaśnięciu sesji. To jedyne miejsce w całej aplikacji omijające Payload — i jest opisane w kodzie, dlaczego.

[+][CH-03]

Escrow zbudowałem dwa razy tego samego dnia. Pierwsza wersja używała stripe'owego delay_days — wstrzymania wypłaty liczonego od płatności. Działała, ale chroniła źle: okno liczy się od zapłaty, nie od realnego odbioru, więc przy wolnej dostawie środki mogły wyjść do sprzedawcy, zanim kupujący w ogóle zobaczył paczkę. Wyrzuciłem ją po kilku godzinach i przebudowałem model: konta sprzedawców przechodzą na payouts=manual, a wypłatę zwalnia mój własny sweeper — dokładnie 2 dni po potwierdzeniu odbioru przez kupującego, z guardem idempotencji po stripePayoutId i kwotą liczoną jako lustro matematyki checkoutu, żeby nie przekroczyć salda o grosz. Gotowość wyrzucenia działającego kodu, gdy model ryzyka jest błędny — i zapisanie w historii dlaczego — uważam za najcenniejszą lekcję tego projektu.

[+][CH-04]

Integracja z Furgonetką (agregator kurierów) nauczyła mnie pokory wobec niedodokumentowanych API: tworzenie przesyłki łamało się na formacie adresu (ulica jednym polem vs rozbite street + building_number) i wersji API, a objawem dla użytkownika było „0 opcji dostawy” w checkoucie. Dwie kolejne poprawki tego samego dnia pogorszyły sprawę — obie złamały tworzenie paczki — więc trzeci commit był revertem do znanej-działającej wersji z maina, z jawnym uzasadnieniem w komunikacie. Dopiero potem przyszła właściwa naprawa: migracja na endpoint calculate-price, twarde limity wymiarów paczki i — najważniejsze — zamiana cichego zera na komunikat mówiący, czego konkretnie brakuje (np. dwuczłonowa nazwa odbiorcy, minimalne wymiary). Dyscyplina „revert do known-good zamiast nawarstwiania fixów” została mi z tego na stałe.

[FIG. 4] WARSTWA AI

AI nie siedzi w produkcie, tylko w procesie jego budowy — i to systemowo: własne skille Claude (payload-collection, trpc-router) generują kod zgodny z konwencjami zastanej bazy, agent-debugger pomaga w diagnozie, a każdy PR przechodzi przez AI code-review w CI (claude-code-review.yml). To odpowiedź na pytanie, jak skalować dyscyplinę pracy z odziedziczonym kodem.

[FIG. 2A] CI-RECENZENT

.github/workflows/claude-code-review.yml · on: pull_request

▸ commit a3f9e21feat(chat): typing indicator

[◎] CZEKAM NA PR

  • modules/chat/server: procedura tRPC bez .input() — dodaj schemat zod
  • use-chat-socket: brak cleanupu w useEffect — leak socketu przy unmount

AI-NATIVE PROCES, NIE AI-PRODUKT: REVIEW KAŻDEGO PR ROBI CLAUDE W CI

[FIG. 5] GALERIA

bazarty.com (Payload + MongoDB) — fig 5
[FIG. 5] 01/02
bazarty.com (Payload + MongoDB) — fig 6
[FIG. 6] 02/02

[FIG. 6] STACK & LINKI

NEXT.JS 15REACT 19PAYLOAD CMSMONGODBTRPC 11TANSTACK QUERYSTRIPE CONNECTSHADCN/RADIXTAILWIND V4VERCEL BLOB
KOMPETENCJE W TYM PROJEKCIE: NEXT.JS · PAYLOAD CMS · STRIPE · TRPC · MONGODB