Ugrás a tartalomhoz
Vissza a válogatott munkákhoz
Élő demó · Stripe tesztmód
KROMA logó

KROMA

Elejétől a végéig megépített előrendelős kávézó — élő webshop Stripe fizetéssel és valódi háttérüzem: konyhai kijelző, üzleti nap és kimutatások.

A KROMA webshop nyitóképe

Kitalált specialty kávézó, teljes értékű előrendelős rendszerként megépítve, nem pusztán makettként. Az ügyfelek élő napi menüből válogatnak, összeállítják az italukat, Stripe-pal fizetnek és követik a rendelésüket; a személyzet ugyanazt a rendelést a konyhai kijelzőn kezeli, pénzszámlálással záró üzleti napot visz, és megnézi a kávézó számait — miközben az árakról, a készletről, a fizetés állapotáról és a jogosultságokról Postgres dönt, nem a böngészőre bízzuk őket.

// Adatréteg

Postgres-alapú logika

Az árazás, a készlet, a jogosultságok és a riportok Row Level Security által védett SQL-függvényekben élnek.

// Fizetés

Előbb fizetés, utána rendelés

A kártyás rendelés csak azután jön létre, hogy a Stripe visszaigazolta a fizetést; a sikertelen esetek visszatérítést kapnak.

// Személyzeti felület

Realtime konyhai kijelző

Supabase Realtime a rendeléseken, 30 másodperces lekérdezéses tartalékkal.

// Ellenőrzés

Két rétegben tesztelve

node:test az alkalmazáslogikához, valamint SQL-tesztek, amelyek visszagörgetett tranzakcióban futnak.

// áttekintés

Mi a KROMA

A KROMA egy kitalált specialty kávézó, teljes, éles használatra szabott webalkalmazásként megépítve. Az ügyfél élő napi menüből válogat, összeállítja az italát, kártyával a Stripe-on keresztül vagy a pultnál fizet, és követi a rendelését; a személyzet ugyanazt a rendelést a konyhai kijelzőn kezeli, és ugyanabból az alkalmazásból intézi a készletet, a menüt, a visszatérítéseket és a napvégi pénztárszámolást. Portfólióprojekt — a kávézó kitalált, a fizetések Stripe tesztmódban futnak —, de a mögötte lévő folyamat valódi.

Minden rendelés árazása, készletkezelése és jogosultság-ellenőrzése Postgres-ben történik, így a webshop, a konyhai kijelző és a napi riport ugyanazt a rendelésrekordot olvassa.

// 01. Stratégiai háttér

Miért épül így a KROMA

01 // Probléma

A fizetés gomb még nem webshop

Az előrendelős demók gyakran egy Rendelés gombnál véget érnek: statikus menü, mögötte semmi. A működő rendszert a makettől az választja el, amit ezek a demók kihagynak — a délelőtt közepén elfogyó készlet, a szerveren ellenőrzendő fizetési állapot és a második felület azoknak, akik ténylegesen elkészítik a rendelést.

Kulcsdinamika: Két felület, egy rendelés
02 // Megoldás

Egy rendelésrekord, két felület

A KROMA kétoldalú rendszerként épül fel. Az ügyfél rendelésoldala és a személyzet konyhai kijelzője ugyanazt a rendelésrekordot olvassa és lépteti előre. A Postgres-függvények árazzák a rendeléseket, csökkentik a készletet és eldöntik, ki mit tehet; a Next.js alkalmazás ezeket hívja.

Kulcsdinamika: A szabályok az adatok mellett élnek
03 // Eredmény

Egy bolt, amely maga vezeti a napját

Egy napi Vercel Cron feladat megnyitja az üzleti napot, a személyzet pedig a fiók tartalmát egy generált riporthoz számolva zárja le. A demó biztonságosan megmutatható idegeneknek is: a kártyás fizetések Stripe tesztmódban futnak, a személyzeti PIN pedig valódi írási hozzáférést adó hitelesítő adat, ezért nem nyilvános.

Kulcsdinamika: Cronnal nyitott nap, számolt zárás
// architektúra-alapelv
„A böngésző javasol; a Postgres dönt. Az ügyfél tételazonosítókat, mennyiségeket és módosítóneveket küld — minden árat, készletváltozást és jogosultság-ellenőrzést az adatbázis old meg.”
— Architektúra-alapelv
// 02. Termékbemutató

A KROMA működés közben

Bemutató a ügyféloldali webshopról és a személyzeti felületről — mit csinál az egyes rész, és milyen érzés használni

Ügyféloldal

Amivel a vendég vagy a törzsvásárló találkozik, a menü böngészésétől a rendelés átvételéig

01Webshop

Élő menü webshop

Élő napi menü, szerkesztőségi listaként és nem kártyarácsként bemutatva. A készlet és az árak közvetlenül a Postgres-ből jönnek, így egy elfogyott tétel percen belül elfogyottként jelenik meg.

Képernyőkép a KROMA webshopról: a szerkesztőségi menülista a hozzá tartozó rögzített előnézeti panellel
  • Élő napi menü: A tételek, az árak és a tételenkénti készlet a Postgres-ből érkeznek, és 30 másodpercenként frissülnek. Ha a lekérdezés nem ad vissza semmit, a webshop egy helyi menu.json pillanatképre vált.
  • WebGL mélységtérképes nyitókép: Egy fragment shader a kurzor mozgásával eltolja a nyitókép fotóját egy szürkeárnyalatos mélységtérkép mentén. WebGL hiányában, betöltési hibánál vagy csökkentett mozgás beállításnál állóképre vált.
  • Szerkesztőségi menülista: Teljes szélességű, hajszálvonallal elválasztott sorok, asztali gépen rögzített előnézeti panellel, amely egérrel való rámutatásra vagy billentyűzetfókuszra fotót vált.
  • Kategóriaszűrés: Pirula alakú navigáció, amelynek aktív kitöltése vágás helyett csúszik át a kategóriák között.
  • Tétel testreszabása: Oldalról becsúszó panel a módosítókhoz és változatokhoz, az ár pedig a választással együtt újraszámolódik.
  • Készletállapotok: Az elfogyott tétel felirata „Mára elfogyott”; ha öt vagy kevesebb maradt, „Már csak N van”.
Next.js 16SupabaseFramer MotionWebGLFrissítés 30 mp-enként
02Fizetés és követés

Rendelés, fizetés, követés

Fizessen kártyával a Stripe Checkout oldalán keresztül, vagy válassza a pultnál fizetést, majd kövesse a rendelést egy megosztható oldalon. A kártyás rendelés csak azután jön létre, hogy a Stripe visszaigazolta a fizetést.

  • Vendég vagy fiók: A rendelés fiók nélkül is működik. A bejelentkezett ügyfél kosara a Postgres-ben tárolódik, a vendégkosár pedig bejelentkezéskor összeolvad vele.
  • Stripe Checkout: A kártyás fizetések Stripe tesztmódban futnak. A munkamenet ára szerveroldali árajánlatból származik, és 30 perc után lejár.
  • Fizetés a pultnál: A pultnál leadott rendelés függőben lévőként jön létre; a barista rögzíti, hogy készpénzzel vagy kártyával fizettek, amikor kifizetettnek jelöli.
  • Étrendi ellenőrzés: A mentett étrendek és allergének még fizetés előtt jelzik a kosárban az ütköző tételeket.
  • Rendelés követése: A megosztható /order/[token] oldal mutatja, ahogy a rendelés a kifizetettből az elkészülőn át az elkészültig halad, 15 másodpercenként frissülve. Mielőtt a pulthoz kerülne, az ügyfél lemondhatja, az online fizetést pedig visszatérítjük.
  • Értesítés az elkészültről: Web Push értesítés érkezik, amikor a rendelés elkészült; ha a push nem elérhető, e-mail a tartalék.
  • Nyugták: Nyomtatható nyugtaoldal vonalkóddal és ÁFA-sorral, valamint Resend-en keresztül e-mailben küldött nyugta, ha van megadott cím.
Stripe CheckoutStripe WebhooksWeb Push (VAPID)ResendServer Actions
03Fiókok

Fiók és bélyegkártya

Az e-mailes vagy Google-os bejelentkezés bélyegkártyát, egyérintéses „szokásos rendelést”, rendelési előzményeket és mentett étrendi beállításokat tesz elérhetővé.

  • Bejelentkezés: E-mail és jelszó megerősítő e-maillel és jelszó-visszaállítással, vagy Google OAuth.
  • Bélyegkártya: Csak az italkategóriák érnek bélyegzőt. Az új fiók 2 bélyeggel indul, a teli kártya 10. Az egyenleg a rendelési előzményekből származik, nem tárolt érték, így a lemondott vagy visszatérített rendelés magával viszi a bélyegeit is.
  • Ingyenes tétel: A teli kártya a fizetésnél egy tételegységre váltható be.
  • A szokásos: Az a tétel, amelyet az ügyfél a legtöbbször rendelt, a legutóbbi rendelésének módosítóival, újrarendelésre készen.
  • Beállítások: Megjelenített név és egy külön, a kiadópultnál kiáltott pultnév, étrendek (vegán, vegetáriánus, pescetáriánus, gluténmentes) és kerülendő allergének.
  • Rendelési előzmények: Korábbi rendelések újrarendelés gombbal.
Supabase AuthGoogle OAuthRow Level SecuritySzármaztatott egyenleg

Személyzeti oldal

A háttérüzem a /dashboard címen, valódi fiók és 4 számjegyű PIN mögött

4 számjegyű PIN

Kétrétegű személyzeti bejelentkezés

A pult mögötti iPad hosszú életű munkamenetet tart fenn, hogy a tábla megjelenhessen; az írás jogát egy személy 4 számjegyű PIN-nel oldja fel. A PIN-t kizárólag a Postgres-ben ellenőrzi a pgcrypto, öt hibás megadás 15 percre kizárja az adott személyt, és minden feloldás naplózódik.

Képernyőkép a KROMA személyzeti PIN-bevitelről a sötét konyhai kijelző felületén
04Konyhai kijelző

Rendeléstábla

Sötét, nagy kontrasztú tábla a kiszolgálósornak. A rendelések négy sávon haladnak végig — Kiadásra vár, Készül, Pultnál kész, Átvéve — és az ügyfél oldala követi őket.

Képernyőkép a KROMA konyhai kijelzőről: a rendelések a négy sávon, az életkorjelzőikkel
  • Élő sor: A rendelések táblájára feliratkozott Supabase Realtime újratölti a táblát. Egy kapcsolatjelző mutatja az élő, újracsatlakozó vagy offline állapotot, a 30 másodperces lekérdezés áthidalja a kieséseket, offline állapotban pedig az írás le van tiltva.
  • Életkorjelző: Minden rendelésnél futó időmérő látható, amely 5 perc után frissből melegre, 10 perc után későre vált.
  • Új rendelés csengő: Hang szól, ha új rendelés jelenik meg a táblán, miután az eszközt egy érintéssel élesítették.
  • Továbbléptetés és visszavonás: Egy érintés továbblépteti a rendelést. Egy sávval vissza 90 másodpercen belül bárki mozgathatja; utána már csak vezető vagy tulajdonos.
  • Jegyzetek, sztornók és visszatérítések: Műszakban bárki fűzhet jegyzetet. A vezetők és tulajdonosok sztornózhatnak vagy visszatéríthetnek, az online rendelés Stripe-visszatérítését pedig az alkalmazásból indítják.
  • Kedvezmények és ajándéktételek: A vezetők és tulajdonosok százalékos, fix összegű vagy ingyenes tételt adhatnak, kötelező indoklással.
  • Telepíthető: A tábla teljes képernyős, fekvő tájolású PWA-ként telepíthető.
Supabase RealtimePostgres RPC-kPWAFramer Motion
05Üzleti nap

Nyitás, forgalom, zárás

A forgalom napokba szerveződik. Valaki alapkészlet-számlálással megnyitja a boltot, a zárt bolt nem fogad rendelést, a vezető vagy a tulajdonos pedig a fiók tartalmát egy generált riporthoz számolva zárja le.

  • Nyitás: A nyitás minden tétel készletét az alapszintjére állítja vissza, vagy a nyitóképernyőn megadott darabszámokra. Bármely szerepkör nyithat, és két eszközön egyszerre megnyomott Nyitás egyetlen nyitást eredményez.
  • Zárt bolt: Amíg nincs megnyitott nap, a rendelés leadása a „A pékség zárva van.” hibát adja.
  • Jegyszámok: Minden nap a jegyeit #001-től kezdi számozni.
  • Fizetési mód: A pultnál a barista rögzíti, hogy a rendelést készpénzzel vagy kártyával fizették-e.
  • Pénztárszámolás: A zárás képernyő címletenként kéri a darabszámot, és megmutatja az eltérést a várt készpénzhez képest: váltópénz plusz készpénzbevétel mínusz készpénzes visszatérítések.
  • Zárásvédelem: A nap nem zárható, amíg rendelések vannak a kiadópulton; a hibaüzenet felsorolja a jegyszámaikat.
  • Napi riport: Rendelések, bevétel, nettó és ÁFA; készpénz-, kártya- és online összesítések; kedvezményes, sztornózott, visszatérített és selejtezett összegek; és ami a készletből megmaradt.
  • ÁFA: Az árak ÁFA-val együtt értendők. Az ÁFA kategóriánként kerül kiszámításra (ma mindenre 11%), és minden rendelési sorra pillanatképként mentődik.
Postgres RPC-kÜzleti napokÁFAVercel Cron
06Elemzés

Számok és főkönyv

Vezetői és tulajdonosi nézet a boltról, Postgres-ben összesítve, nem a böngészőben, így a jogosultságellenőrzés ott van, ahol a pénzt összeadják.

  • Bevétel: A bevétel egy választott időszakban, kategória szerint szűrhetően.
  • A pult mögött: Személyenként: megkezdett, sztornózott és visszatérített rendelések, az időzítésekkel együtt, ugyanazokból a rendelési sorokból számítva.
  • Főkönyv: Az auditnapló szűrve és lapozva: feloldások, rendelésmozgatások, kedvezmények, műszakhatárok, nyitás és zárás, valamint menüszerkesztések.
  • Jogosultsági kapu: Minden összesítés újraolvassa a hívó szerepkörét az adatbázisban, és elutasít mindenkit, aki a vezetői szint alatt van.
Postgres RPC-kAuditnaplóSzerepkör alapú védelem
Menükezelés

Az élő menü szerkesztése

A vezetők és tulajdonosok a vezérlőpultból szerkesztik a menüt: átrendezik a tételeket, megnyitják a tétel lapját a módosítószerkesztővel, kezelik a kategóriákat és törlik a tételeket. Minden írás jogosultságot ellenőrző függvényen megy át, a korábbi rendelések pedig megtartják a tétel nevét és árát.

// 03. Architektúra és topológia

Szervermentes full-stack architektúra

Vercel-en futó Next.js alkalmazás egy Supabase projekt előtt; a Stripe, a cron és a push szerveroldali útvonalakon át csatlakozik

topology.ascii
    [ Browser · Customer & Staff PWAs ]                           [ Vercel Cron ]
      │                           │                                     │
      │                           │ HTTPS                               │ Bearer secret
      │                           ▼                                   ▼
      │                    ┌────────────────────────────────────────────────────────────────┐
      │                    │                Next.js 16 · React 19  (Vercel)                 │
      │                    ├────────────────────────────────────────────────────────────────┤
      │                    │ • Server Components & Server Actions                           │
      │                    │ • Route Handlers: Stripe webhook · cron · auth callbacks       │
      │                    │ • Signed cookie that unlocks staff writes                      │
      │                    └───────────┬─────────────────────┬───────────────────┬──────────┘
      │                                │ RPC                 │ API               │ Push · email
      │                                ▼                   ▼                  ▼
      │                     ┌──────────────────────┐  ┌───────────────┐  ┌───────────────────┐
      └─ RPC · Realtime ──►│ Supabase             │  │ Stripe        │  │ Notifications     │
                            ├──────────────────────┤  ├───────────────┤  ├───────────────────┤
                            │ • Postgres + RLS     │  │ • Checkout    │  │ • Web Push (VAPID)│
                            │ • Security-definer   │  │ • Refunds     │  │ • Resend email    │
                            │   RPCs               │  │ • Webhooks    │  └───────────────────┘
                            │ • Auth               │  └───────────────┘
                            │ • Realtime           │
                            └──────────────────────┘

Műszaki döntések és indoklások mátrixa

Hol él az egyes felelősség, és miért ott

Réteg és hatókörTechnológiai választásFő képességek és indoklás
Frontend
Alkalmazás-keretrendszer
Next.js 16 (App Router) + React 19

Szerverkomponensek, szerverműveletek, egyetlen kliensoldali határ

A webshop egy szerverkomponens, amely lekéri a menüt és 30 másodpercenként frissül; egyetlen kliensoldali határ tartja a szűrő- és kosárállapotot. A módosítások szerverműveletek, az útvonalkezelők pedig a Stripe webhookot, a cron feladatot és a hitelesítési visszahívásokat fedik le.
Tárolás
Adatbázis és logika
Supabase Postgres

Üzleti szabályok security-definer RPC-kként, RLS az ügyféladatokon

Az árazás, a készlet, a jegyszámok, a jogosultságok, a visszatérítések és a riportok verziózott migrációkként kezelt SQL-függvényekben élnek, így a webshop, a tábla és a cron feladat ugyanazokon a szabályokon megy át.
Hitelesítés
Azonosítás
Supabase Auth + staff tábla

E-mail/Google az ügyfeleknek; állomás-munkamenet és PIN a személyzetnek

Az ügyfelek Supabase Auth-tal jelentkeznek be. A személyzet egy olvasni képes állomáson dolgozik, az írást pedig személyenkénti PIN oldja fel, így minden módosítás hozzárendelhető valakihez.
Kereskedelem
Fizetések
Stripe Checkout + webhookok

Előbb fizetés, utána létezik a rendelés

A Checkout Session ára adatbázisbeli árajánlatból származik. A rendelés csak a fizetés után jön létre, a webhookból vagy a visszatérő oldalból, és visszatérítést kap, ha nem adható le.
Valós idő
Élő frissítések
Supabase Realtime

Vékonyan feliratkozni, vastagon lekérdezni

A konyhai kijelző a rendelések táblájának változásaira figyel, és egyetlen RPC-n keresztül tölti újra az adatokat, 30 másodperces lekérdezéssel tartalékként. Az ügyfelek rendelésoldalai 15 másodpercenként frissülnek.
Üzemeltetés
Ütemezett feladatok
Vercel Cron

Egyetlen hitelesített napi útvonal 03:00-kor, UTC szerint

A feladat felszabadítja a lejárt online rendeléseket, megnyitja az üzleti napot ugyanazon az RPC-n át, amelyet a barista is használ, és törli az elavult push-feliratkozásokat.
Üzenetküldés
Értesítések
Web Push (VAPID) + Resend

Először push, tartalékként e-mail

Az elkészülési értesítések minimális service workeren át Web Pushként mennek ki; ha egyetlen push sem érkezik meg, e-mail megy, ha van cím. A nyugtákat a Resend küldi e-mailben.
Dizájn
Felület
Tailwind CSS v4 + Framer Motion + WebGL

Hajszálvonalak, mono feliratok, csökkentett mozgást kezelő tartalékok

A szerkezetet egypixeles vonalak adják kártyák vagy árnyékok helyett. A mozgástokenek közösek, és minden ambient animáció csökkentett mozgás mellett álló, jól használható állapotra egyszerűsödik.
// 04. A motorháztető alatt

A pénz, a készlet és a jogosultságok helyességére építve

Négy döntés, amely egyben tartja a rendszert

// 01 • Fizetések és rendelések

Fizetés, mielőtt a rendelés létezne

A kártyás rendelés csak egy már kifizetett Stripe-munkamenetből jön létre, így kifizetetlen kártyás rendelés soha nem kerül a rendelések táblájába.

  • Az adatbázisban árazva: A fizetés előtti árajánlat és a fizetés utáni beszúrás ugyanazt a sorfeloldót használja. Az ügyfél tételazonosítókat, mennyiségeket és módosítóneveket küld; minden árat a menu_items táblából olvasunk.
  • Webhook és visszatérő oldal: A checkout.session.completed és a megerősítő átirányítás is ugyanazt a kezelőt hívja, amely a stripe_session_id alapján idempotens.
  • Visszatérítés hiba esetén: Ha egy kifizetett munkamenetből nem lehet rendelést létrehozni, automatikusan visszatérítésre kerül, munkamenetenkénti idempotenciakulccsal.
  • Ellenőrzött aláírás: A webhook minden érvényes Stripe-aláírás nélküli kérést elutasít.
  • Stripe
  • Webhookok
  • Idempotencia
  • Postgres RPC
// 02 • Adatok és biztonság

A Postgres mint döntéshozó

A Row Level Security és a security-definer függvények az adatok mellé teszik a szabályokat, így egyetlen kliensoldali útvonal sem kerülheti ki őket.

  • Korlátozott ügyféladatok: Az RLS-szabályok az ügyfeleket csak a saját kosaraikra, profiljaikra, kedvenceikre és rendeléseikre korlátozzák.
  • Vendégrendelések tokennel: A vendégeknek nincs szabályuk a rendeléseken. Egy kitalálhatatlan token pontosan egy rendelést nyit meg az order_by_token() függvényen át, amely kihagyja a tokent, a felhasználóazonosítót és a Stripe-oszlopokat.
  • Pillanatképek: A tétel neve, ára, ÁFA-kulcsa és bélyegjogosultsága minden rendelési sorra átmásolódik, így a későbbi menüszerkesztések nem írják át a múltat.
  • Származtatott egyenlegek: A bélyegkártya egyenlege a rendelési előzményeken végzett lekérdezés, nem tárolt számláló.
  • SQL-ben tesztelve: Az SQL-tesztek a valódi sémát egy visszagörgetett tranzakcióban próbálják ki, a node:test pedig az alkalmazásoldali logikát fedi le.
  • Row Level Security
  • security definer
  • Supabase
  • node:test
// 03 • Személyzet és jogosultságok

Szerepkörök, PIN-ek és auditnapló

A tulajdonosi, vezetői és személyzeti szerepköröket egyetlen staff_can() függvény érvényesíti, amelyet az RLS-szabályok és minden író RPC meghív.

  • Egyetlen jogosultságfüggvény: A TypeScript-tükörkép csak a gombok elrejtésére szolgál; a határt az adatbázis jelenti.
  • A szereplő újraolvasása: Minden író RPC a staff táblából olvassa ki a szereplőt, így valaki deaktiválása akkor is leállítja a következő írását, ha a sütije még érvényes.
  • PIN-zárolás: Öt sikertelen PIN 15 percre zárolja a személyt, és staff.locked eseményt ír.
  • Tulajdonosi igénylés: Az első bejelentkezett fiók, amely meghívja a claim_owner() függvényt, tulajdonos lesz, és az ajtó becsukódik mögötte — előre betöltött hitelesítő adat nélkül.
  • Auditnapló: A műveletek egy staff_events naplóba írnak, amelyet a vezetők és tulajdonosok olvashatnak.
  • staff_can()
  • pgcrypto
  • Zárolás
  • Auditnapló
// 04 • Üzemeltetés

Magától működő üzleti nap

Egy napi feladat és egy versenyhelyzet-biztos nyitás kézi lépés nélkül tartja forgalomban a boltot.

  • Napi Cron: A vercel.ts a /api/cron/release-holds útvonalat 03:00-ra ütemezi UTC szerint, CRON_SECRET bearer tokennel hitelesítve.
  • Mit csinál: Felszabadítja a lejárt online rendeléseket, megnyitja a napot az open_service függvényen át (a legkorábban létrehozott aktív személyzeti tag nevében), és törli a 24 óránál régebbi push-feliratkozásokat.
  • Versenyhelyzet-biztos nyitás: A nyitás egy beszúrás a nap elsődleges kulcsára, így az egyidejű nyitások egyetlen napot hagynak, és soha nem állítják vissza a reggeli készletet.
  • Sorosított készlet: A menüsorokat a készletcsökkentéskor for update zárolja, így a két vevő, aki az utolsó tételért nyúl, sorba áll.
  • Számozott jegyek: A jegyszámok a nap sorából származnak, ugyanabban a rendeléslétrehozó függvényben, amely a készletet csökkenti.
  • Vercel Cron
  • open_service
  • Sorzárak
  • Üzleti napok
// 05. Kihívások és akadályok

Mérnöki akadályok és megoldások

Ahol a valódi pénz, a valódi készlet és a valódi személyzet a kézenfekvő megoldást rossznak bizonyította

// 01 • Párhuzamosság és pénz

Két ember, egy utolsó kifli

Az akadály

Az online rendelés már nem foglalja a készletet, amíg az ügyfél a Stripe-nál van, így ketten is kifizethetik az utolsó tételt. Az árajánlat elutasítja egy már elfogyott tételt, de ezt a versenyhelyzetet nem tudja megakadályozni.

A megoldás

A rendeléslétrehozó függvény a készletcsökkentés előtt for update zárral lezárja a menüsorokat. A vesztes beszúrása hibát dob, a fizetéskezelő pedig visszatéríti neki az összeget. Az újrapróbálkozás biztonságos: a rendelések stripe_session_id szerint egyediek, a webhook és a visszatérő oldal ugyanazt a kezelőt használja, a visszatérítés pedig munkamenetenkénti idempotenciakulcsot visel.

Hatás: Egy fizetésből legfeljebb egy rendelés lesz, vagy visszatérítést kap. A tudatosan vállalt kompromisszum, hogy a vesztes fizet, majd visszakapja a pénzét, ahelyett hogy korábban megállítanánk.

  • Sorzárak
  • Idempotencia
  • Automatikus visszatérítés
  • Stripe
// 02 • Adatszállítás

Kosár, amely elfér a Stripe metaadataiban

Az akadály

A rendelést a fizetés után olyan kód hozza létre, amely csak a Checkout Session-t látja, ezért a kosárnak vele kell utaznia — a Stripe metaadat-értékei pedig rövidek.

A megoldás

A packItems() JSON-ná szerializálja a kosarat, és 500 karakteres items_N kulcsokra osztja, legfeljebb 45-re korlátozva, hogy maradjon hely a névnek, a jegyzeteknek és az azonosítóknak. Az unpackItems() újra összeállítja, az adatbázis pedig a tételazonosítókból építi újra és árazza újra a rendelést, ahelyett hogy a metaadatban bízna.

Hatás: Fizetés előtt nem létezik piszkozat-rendelés sor, így egy félbehagyott fizetés semmit sem hagy maga után. A rendelések legfeljebb 50 sorosak lehetnek.

  • Stripe metaadat
  • Darabolás
  • Server Actions
// 03 • Biztonság és azonosítás

Az iPad, amely sosem jelentkezik ki

Az akadály

A pult mögötti eszköz egész nap bejelentkezve marad, így a munkamenete nem tudja megmondani, ki mit csinált — és egy ellopott iPad nem téríthet vissza senkinek semmit.

A megoldás

Egy állomás tartja a hosszú életű munkamenetet, hogy a tábla olvasni tudjon, de egy adatbázis-megkötés megtiltja, hogy az állomásnak PIN-je legyen, így soha nem hozható létre hozzá szereplői munkamenet. A személyek a Postgres-ben ellenőrzött 4 számjegyű PIN-nel oldják fel az írást, és minden RPC újraolvassa a szereplőt. A staff táblát lekérdező személyzeti szabályok rekurzióba futottak, ezért egy security-definer is_staff() segédfüggvény töri meg a kört.

Hatás: Az írások személyhez rendelődnek, a deaktiválás a következő írásnál lép életbe, és a tábla látása soha nem jár együtt a módosítás képességével.

  • PIN + pgcrypto
  • Zárolás
  • security definer
  • RLS
// 06. Technológiai stack

Miből épül

Rétegenkénti bontás a keretrendszerekről, szolgáltatásokról és eszközökről

Frontend és felület
  • Next.js 16
  • React 19
  • Tailwind CSS v4
  • shadcn/ui
  • Base UI
  • Framer Motion
Backend és API
  • Supabase Postgres
  • Stripe Checkout
  • Stripe Webhooks
Valós idő és WSS
  • Supabase Realtime
Háttérsorok
  • Vercel Cron
Tárolás és média
  • ImageKit
Hitelesítés és biztonság
  • Supabase Auth
  • Row Level Security
  • pgcrypto
Kommunikáció
  • Web Push (VAPID)
  • Resend
DevOps és telepítés
  • Vercel
  • pnpm
// 07. Helyi futtatás és értékelés

Próbálja ki élőben és helyben

A kroma.lenardtunya.com webshop minden reggel magától megnyílik, így mindig van élő menü, amelyből rendelhet. A kártyás fizetések Stripe tesztmódban futnak — használja a 4242 4242 4242 4242 számot, bármely jövőbeli lejáratot és bármely CVC-t.

A személyzeti vezérlőpult valódi, éppen ezért a PIN-je nem nyilvános: írási hozzáférést adó hitelesítő adat az élő demóadatokhoz. Megtekintéséhez futtassa helyben a projektet — a seed saját, eldobható PIN-nel érkezik —, vagy keressen meg egy interaktív bemutatóért.

local-setup.sh
# 1. Clone and install
git clone https://github.com/JustTunya/kroma.git
cd kroma
pnpm install

# 2. Start local Postgres + Auth, seeded from supabase/seed.sql
supabase start

# 3. Configure environment variables (see README for each one)
cp .env.local.example .env.local

# 4. Start the app
pnpm dev
# Storefront: http://localhost:3000
# Dashboard:  http://localhost:3000/dashboard

# 5. Unlock the staff dashboard (local seed only)
#    a. Sign up at /auth/sign-up, confirm via the local Inbucket mail UI
#       (`supabase status` prints its URL, usually http://localhost:54324)
#    b. Link that account to the seeded staff row:
#       update staff set user_id = (select id from auth.users
#         where email = 'you@example.com') where display_name = 'Demo Owner';
#    c. Sign in, open /dashboard, enter the seeded PIN 1234, then open the shop

Értékelési közlemények és szimulációs részletek

  • Szimulált kávézó: A KROMA kitalált. A kártyás fizetések Stripe tesztmódban futnak, valódi terhelés nélkül, a webshopon lévő pörkölési és beszerzési szövegek pedig hangulati elemek, nem valódi ellátási lánc.
  • Valódi Web Push, SMS nélkül: Az elkészülési értesítések valódi VAPID Web Push értesítések, e-mail tartalékkal. SMS-es vagy telefonszámos útvonal nincs.
  • PolyForm Noncommercial licenc: A forráskód nyilvános olvasásra, tanulmányozásra, valamint személyes vagy oktatási célú használatra a PolyForm Noncommercial 1.0.0 alapján. A kereskedelmi felhasználáshoz — beleértve egy valódi kávézóként való üzemeltetést is — külön licenc szükséges.

Ismerje meg a(z) KROMA projektet működés közben

Nyilvánosan elérhető műszaki értékeléshez, kódellenőrzéshez és architekturális vizsgálathoz.