All diagrams //1 of 1

Open Appointment Single Booking
🍋
Appointment Single Booking
27 Jul, 2026
flowchart TD
    %% ===== SINGLE APPOINTMENT (1:1) — worktree1 (4c62bbd7, FA-631-streamline) =====
    Start(["Kunde wählt Trainer + freien TimeSlot"])

    subgraph BUCHUNG["1 · Buchung"]
        direction TB
        Card{"Zahlungsmethode<br/>hinterlegt?"}
        NoCard["Abbruch:<br/>Karte erforderlich"]
        Accepted["Appointment: ACCEPTED<br/>Order: OPEN<br/>TimeSlot: geschlossen"]
    end

    subgraph ZAHLUNG["2 · Zahlung (Off-Session, ChargeStoredPaymentMethodAction)"]
        direction TB
        Processing["Order: PAYMENT_PROCESSING"]
        Reauth["3DS / SCA nötig<br/>Kunde authentifiziert (Reauth)"]
        Hook{"Stripe Webhook"}
        FailSync["Order: PAYMENT_FAILED<br/>Appointment: DECLINED<br/>TimeSlot: wieder offen<br/>(sync Karten-Ablehnung)"]
        FailAsync["Order: PAYMENT_FAILED<br/>Appointment bleibt ACCEPTED<br/>Slot bleibt zu<br/>(async Webhook)"]
        Paid["Order: PAID<br/>Appointment: BOOKED<br/>+ Handshake-Code, Rechnung,<br/>Benachrichtigung Kunde + Trainer"]
    end

    subgraph DURCH["3 · Durchführung"]
        direction TB
        Handshake["Appointment: HANDSHAKE_DONE"]
        Missed["Handshake verpasst<br/>Appointment bleibt BOOKED"]
        Req["TrainingCompletionRequest: PENDING<br/>(Kunde & Trainer)"]
        Completed["Appointment: COMPLETED"]
        Support["Streitfall (occurred=false)<br/>Support / Operator prüft"]
    end

    subgraph ABSCHLUSS["4 · Abschluss"]
        Reviewed["Appointment: REVIEWED"]
    end

    subgraph STORNO["Storno (solange Start in der Zukunft)"]
        direction TB
        CancelC["Order: CANCELED<br/>Appointment: CANCELED_BY_CUSTOMER<br/>TimeSlot: wieder offen"]
        CancelT["Order: CANCELED<br/>Appointment: CANCELED_BY_SUPPLIER<br/>Kunde benachrichtigt"]
    end

    Start --> Card
    Card -- "nein" --> NoCard
    Card -- "ja · «Kunde» bestätigt Buchung" --> Accepted

    Accepted -- "«System» Off-Session Charge" --> Processing
    Processing -- "«Stripe» requires_action (3DS)" --> Reauth
    Reauth --> Hook
    Processing -- "«Stripe» sofortige Ablehnung (CardException)" --> FailSync
    Processing -- "«System» wartet auf Webhook" --> Hook
    Hook -- "payment_intent.succeeded" --> Paid
    Hook -- "payment_intent.payment_failed (async)" --> FailAsync

    FailSync -. "«Kunde» retryPayment (reaktiviert Termin/Slot, Order->OPEN)" .-> Processing
    FailAsync -. "«Kunde» retryPayment (Order->OPEN, erneute Belastung)" .-> Processing

    Paid -- "«Trainer» scannt Kunden-QR bei Session-Start" --> Handshake
    Paid -. "kein Handshake" .-> Missed
    Handshake -- "«System» complete-ended (nach Endzeit)" --> Completed
    Missed -- "«System» detect-missed (Endzeit + 15 min)" --> Req
    Req -- "beide bestätigen: fand statt" --> Completed
    Req -- "occurred = false" --> Support
    Completed -- "«Kunde/Trainer» Bewertung abgeben" --> Reviewed

    Accepted -. "«Kunde» storniert" .-> CancelC
    Paid -. "«Kunde» storniert · >12h: Gratis + Refund · <12h: KEIN Refund" .-> CancelC
    FailAsync -. "«Kunde» storniert" .-> CancelC
    Paid -. "«Trainer» storniert · immer Refund wenn PAID" .-> CancelT
    Accepted -. "«Trainer» storniert" .-> CancelT

    classDef good fill:#d4f4dd,stroke:#1a7f37,color:#0b3d1c;
    classDef bad fill:#ffd7d5,stroke:#c1121f,color:#5c0a0f;
    classDef wait fill:#fff3cd,stroke:#b8860b,color:#5c4400;
    classDef sys fill:#dbe9ff,stroke:#1f6feb,color:#0a2a5e;

    class Start,Card,Hook sys;
    class Accepted,Processing,Reauth,Missed,Req wait;
    class Paid,Handshake,Completed,Reviewed good;
    class NoCard,FailSync,FailAsync,Support,CancelC,CancelT bad;