Iron Lab
Work in Progress
Web Development

Iron Lab

Web fiktivní posilovny zaměřený na tvorbu turniketového systému. Web je v robo tech stylu, kdo by nechtěl naprat sílu legendárního Tonnyho S.. Moderní styl není to jediné unikátní na tomto projektu. Další unikátností je vstup 24/7 díky terminálovému systému, stačí se na webu zadat email, zaplatiti poplatek a otevře se vám váš vlastní svět. Díky QR kódu který naleznete v emailu se dostanete přes stránku /terminál přímo do budovy.

// Jak to funguje

Od nákupu až ke vstupu

Nejde jen o nákup. Po zaplacení musí vzniknout vstupenka, QR kód a e-mail, se kterým pak člověk projde turniketem.

Nákup je jen začátek. Po zaplacení se musí rozjet celý řetězec a na jeho konci stojí člověk před turniketem s QR kódem v mobilu.

Checkout→Payment→Ticket→QR email→Turnstile scan
PAYMENT

Stripe + mock

Celý proces jde vyzkoušet i bez skutečné platby.

TICKET

QR token

Vstupenka má vlastní platnost.

ENTRY

Scan endpoint

Turniket ověří, jestli je vstupenka platná.

PRIVACY

Encrypted profile

Osobní údaje jsou zašifrované.

Platba, vstupenka, vstup a ochrana osobních údajů mají každé svůj úkol. V dalších kapitolách ukazuju, jak je to spojené.

// Kousek kódu

Zkušební platba pro vývoj

KÓD 01 · MOCK CHECKOUTbackend/app/routers/payments.py
async def _mock_checkout(pass_type: str, user: dict | None = None, guest_email: str | None = None) -> str:
    """
    Skips Stripe entirely: records a "paid" payment, issues the ticket and
    sends the real e-mail — so the whole flow (except the actual card payment)
    is exercised. Controlled by the admin panel's "mock payments" toggle.
    """
    now = datetime.now(timezone.utc)
    payment_doc = {
        "user_id": str(user["_id"]) if user else None,
        "guest_email": guest_email,
        "pass_type": pass_type,
        "stripe_session_id": None,
        "status": "paid",
        "amount": 0,
        "currency": "czk",
        "mock": True,
        "created_at": now,
    }
    result = await payments_col.insert_one(payment_doc)
    await create_ticket_and_notify(
        pass_type, str(result.inserted_id), user=user, guest_email=guest_email
    )
Co to děláZasimuluje úspěšnou platbu, aby šlo vyzkoušet celý proces bez skutečných peněz.Technicky: Simuluje úspěšnou platbu, zapíše payment dokument a zavolá stejný fulfillment jako reálný checkout.
Proč na tom záležíPři vývoji nemusím posílat opravdovou platbu a přesto se zkouší vznik vstupenky i e-mail.Technicky: Je významný pro testování celého procesu. Nemusíš při každém vývoji posílat reálnou platbu, ale zároveň testuješ vznik ticketu a odeslání e-mailu.
// Kousek kódu

Po platbě vznikne vstupenka

KÓD 02 · FULFILLMENTbackend/app/services/fulfillment.py
async def create_ticket_and_notify(
    pass_type: str, payment_id: str, user: dict | None = None, guest_email: str | None = None
) -> dict:
    user_id = str(user["_id"]) if user else None
    ticket_doc = build_ticket_document(user_id, pass_type, payment_id)
    if guest_email:
        ticket_doc["guest_email"] = guest_email

    await tickets_col.insert_one(ticket_doc)

    if user:
        data_key = unwrap_data_key(user["data_key_wrapped"])
        profile = decrypt_profile(data_key, user["encrypted_profile"])
        to_email = user["email"]
        name = profile.get("name", "")
    else:
        to_email = guest_email
        name = "zákazníku"

    qr_base64 = make_qr_png_base64(ticket_doc["token_id"])
    send_ticket_email(to_email, name, ticket_doc["label"], qr_base64)

    return ticket_doc
Co to děláVytvoří vstupenku, QR kód a pošle ji e-mailem.Technicky: Vytvoří ticket, uloží ho, případně dešifruje profil uživatele, vygeneruje QR a pošle e-mail.
Proč na tom záležíTenhle krok je oddělený, takže funguje stejně pro zkušební i skutečnou platbu.Technicky: Tady je oddělená business operace „platba je hotová, vystav vstup“. Díky samostatné service ji může volat Stripe webhook i mock payment bez duplicitní logiky.
// Kousek kódu

Ochrana osobních údajů

KÓD 03 · DATA KEYbackend/app/crypto.py
def generate_data_key() -> bytes:
    """A fresh symmetric key, unique per user."""
    return Fernet.generate_key()


def wrap_data_key(data_key: bytes) -> str:
    """Encrypt a user's data key with the master key, for storage."""
    return _master_fernet.encrypt(data_key).decode()


def unwrap_data_key(wrapped: str) -> bytes:
    """Decrypt a user's data key using the master key."""
    return _master_fernet.decrypt(wrapped.encode())


def encrypt_profile(data_key: bytes, profile: dict) -> str:
    f = Fernet(data_key)
    payload = json.dumps(profile).encode()
    return f.encrypt(payload).decode()
Co to děláKaždý uživatel má vlastní klíč, kterým se zamykají jeho osobní údaje.Technicky: Každý uživatel má vlastní datový klíč, který je zabalený master klíčem; profil se šifruje tímto uživatelským klíčem.
Proč na tom záležíKdyž se klíč smaže, osobní údaje se nedají přečíst. Hodí se při žádosti o smazání dat.Technicky: Nejde jen o „zašifruj JSON“. Samostatný klíč pro uživatele umožňuje crypto-shredding: odstraněním zabaleného klíče se osobní data stanou prakticky nečitelná.
// Jak to funguje

Co dělá turniket

Turniket načte QR kód a zeptá se serveru, jestli je vstupenka platná. Pokud ano, pustí člověka dál. U jednorázové vstupenky se vstup odečte.

// Jak to funguje

E-mail s QR kódem

Vstupenka přijde e-mailem, QR kód je vidět přímo v textu i v příloze. Funguje s libovolným e-mailovým poskytovatelem.

// Jak to funguje

Co může selhat

Tady se potkává hodně služeb: platba, databáze, e-mail a QR kód. Kdyby po platbě selhal e-mail, měl by člověk zaplaceno bez vstupenky. Proto je vydání vstupenky oddělené a jde ho zopakovat.

// Jak to funguje

A ještě něco navíc

Součástí je i administrace, správa účtu, pravidelný úklid dat a stránka o ochraně osobních údajů. Je to blíž malému skutečnému systému než ukázkovému nákupu.

// Pod kapotou

Z čeho je projekt postavený

Žádná technologie tu není jen pro parádu. Tady je rychlý přehled, kdo za co odpovídá.

FRONTEND

Next.js

BACKEND API

FastAPI

DATABÁZE

MongoDB

PLATBY

Stripe

VSTUPENKY

QR tickets

ŠIFROVÁNÍ DAT

Fernet encryption

// Shrnutí

Co je na projektu podstatné?

Nejde mi jen o to, jak to vypadá. Baví mě hlavně to, co je pod tím: kudy data tečou, co se validuje, co může selhat a proč jsem to udělala právě takhle.

Napiš mi