# place4bees – vereinte Webseite Selbst-hostbare Architektur: **Medusa.js** (Shop-Backend + Admin) + **Astro** (handgebautes Frontend). **place4bees ist die neue, einzige Unternehmensmarke.** "Imkerei Björn Schumann" / "hhonig" taucht im Auftritt nirgends mehr auf (außer ggf. später im Impressum aus rechtlichen Gründen). ## Struktur ``` backend/ Medusa-Backend (Node.js, Turborepo), self-hosted via Docker → apps/backend enthält die eigentliche Medusa-Instanz frontend/ Astro-Frontend docker-compose.yml Postgres + Redis + Medusa-Backend (self-hosted) ``` ## Eine Website, zwei Domains, zwei Bereiche Es gibt **keine zwei Marken mehr**, sondern eine Website mit zwei Bereichen ("Sections"), gesteuert über `frontend/src/lib/sections.ts`: - **Privatkunden** (`private`): Start, Shop, Über uns, Kontakt - **Business** (`business`): Start, Angebot, Anfrage (nur Formular, kein Checkout) Ein **persistenter Tab-Umschalter im Header** ("Privatkunden" / "Business") ist auf jeder Seite sichtbar und wechselt zwischen den Bereichen – unabhängig davon, auf welcher Domain man sich befindet: - **hhonig.de** → `/` zeigt automatisch den Privatkunden-Bereich - **place4bees.com** → `/` zeigt automatisch den Business-Bereich - Auf beiden Domains sind **beide Bereiche vollständig erreichbar** über die stabilen Pfade `/privatkunden` und `/business` (die Tabs verlinken immer auf diese festen Pfade, nie auf das host-abhängige `/`, damit kein Klick-Loop entsteht). Die Zuordnung passiert in `frontend/src/lib/sections.ts` (`resolveSection`, `isBusinessHost`) anhand des `Host`-Headers – rein serverseitig, keine Cookies/Redirects nötig. ## SEO-Konzept - **Canonical-Konsolidierung:** Da dieselben Inhalte technisch auf zwei Domains erreichbar sind, zeigt jede Seite ihren `` fest auf `https://place4bees.com/` (in `Layout.astro`), unabhängig davon, über welche Domain sie aufgerufen wurde. Damit bündelt Google die Rankings auf einer Domain, statt sie zwischen hhonig.de und place4bees.com aufzuteilen. hhonig.de bleibt für Bestandsbesucher/Backlinks nutzbar, ohne hart per 301 umgeleitet zu werden. - **Wichtig vor Go-Live:** Bestehende URLs von hhonig.de, shop.hhonig.de und place4bees.com erfassen und auf die neuen Pfade (`/`, `/shop`, `/business`, …) mappen – als 301-Redirects dort, wo sich die URL-Struktur ändert. - Google Search Console für beide Domains einrichten/aktualisieren, aktualisierte Sitemap einreichen. - Produktseiten sollten vor Launch noch strukturierte Daten (schema.org `Product`) bekommen; der Privatkunden-Bereich zusätzlich `LocalBusiness`. ## Design / Theming Farben sind als semantische Tokens in `frontend/src/styles/global.css` definiert (`--color-brand-primary`, `--color-brand-ink`, …) statt als rohe Tailwind-Farben in den Komponenten. Ein komplettes Reskinning bedeutet: Werte in dieser einen Datei ändern – Komponenten bleiben unangetastet. Für einen eigenen Akzent nur im Business-Bereich gibt es bereits den Haken `[data-section="business"] { ... }` in derselben Datei. **Aktuelles Look & Feel (2. Iteration, bewusst kontrastreicher):** dunkle Ink-Hero- und Footer-Flächen mit gestaffelter Hexagon-Grafik (`.hex-shape` in `global.css`, mehrere überlappende Sechsecke statt einer einzelnen Fläche), große Fraunces-Serifen-Headlines in Creme auf Dunkelbraun, Honiggold als Primärfarbe im Privatkunden-Bereich, Petrolgrün im Business-Bereich (automatisch über `[data-section="business"]`). Shop-Karten mit Schatten, Hover-Lift-Animation und Kategorie-Badges. ## Checkout-Flow Voll funktionsfähig gegen die Medusa Store API, End-to-End getestet: 1. **Produktseite** (`/shop/[handle]`) → `AddToCartButton` legt bei Bedarf einen Cart an (Cookie `cart_id`) und fügt die gewählte Variante hinzu. 2. **Warenkorb** (`/warenkorb`) → Menge ändern / Position entfernen. 3. **Kasse** (`/kasse`) → Adresse, Versandart (aus Medusa geladen), Zahlung. 4. **Bestellbestätigung** (`/bestellung/danke`). Zahlung läuft im Frontend aktuell noch über Medusas eingebauten `pp_system_default`-Provider (kein echtes Zahlungs-Gateway). Das PayPal-Backend ist bereits vorbereitet (siehe unten), die Frontend-Anbindung fehlt noch. Alle Cart/Checkout-Aufrufe laufen direkt vom Browser aus gegen die Medusa Store API (`frontend/src/lib/cart.ts`), ohne eigene Backend-Route dazwischen. ## Zahlungsanbieter: PayPal **Hinweis zur Wahl:** Medusa pflegt offiziell nur eine Stripe-Integration. Für PayPal gibt es kein offizielles Plugin, nur Community-Pakete einzelner kleiner Anbieter – hier verwendet: [`@easypayment/medusa-payment-paypal`](https://www.npmjs.com/package/@easypayment/medusa-payment-paypal) (aktuell das am aktivsten gepflegte). Das ist ein bewusst in Kauf genommenes Risiko (Wartung/Sicherheit liegen bei einem kleinen Drittanbieter, nicht bei Medusa selbst) – wurde nach Rücksprache trotzdem gewünscht. **Bereits erledigt (Backend):** - Plugin installiert, in `medusa-config.ts` registriert (Plugin + zwei Payment-Provider: `paypal` für Smart Buttons/Wallet, `paypal_card` für Karten-Zahlung über PayPal). - Datenbank-Migration ausgeführt. - Beide Provider (`pp_paypal_paypal`, `pp_paypal_card_paypal_card`) für die Europe-Region aktiviert. - Server startet fehlerfrei mit der neuen Konfiguration. **Noch offen (braucht dich):** 1. **PayPal-Konto verbinden:** Medusa Admin → Settings → PayPal → PayPal Connection → zunächst **Sandbox** wählen (Test-Modus, kostenlos und ohne Geschäftskonto-Prüfung sofort über [developer.paypal.com](https://developer.paypal.com/dashboard/) anlegbar) → "Connect to PayPal" oder Client-ID/Secret manuell eintragen. 2. **Frontend-Anbindung fehlt noch:** Das mitgelieferte UI-Paket (`@easypayment/medusa-paypal-ui`) ist explizit für Next.js-Storefronts gebaut und passt nicht zu unserem Astro-Frontend. Die PayPal-Buttons müssen wir selbst gegen PayPal's JS-SDK und die Medusa-Payment-Session bauen – das kann ich erst sinnvoll umsetzen und testen, sobald ein Sandbox-Konto verbunden ist (sonst baue ich ungetesteten Zahlungscode). 3. Sobald Sandbox läuft: echte End-to-End-Bestellung testen, dann auf **Live** umschalten (im selben Admin-Screen, ohne Server-Neustart laut Plugin-Doku). 4. Optional: `PAYPAL_ENCRYPTION_KEY` (beliebiger starker Zufallsstring) in `.env` setzen, damit die gespeicherten PayPal-Zugangsdaten in der Datenbank verschlüsselt statt im Klartext liegen. ## Produktdaten: Preis, MwSt., Grundpreis, Lieferzeit - **Preise sind inkl. MwSt.** und werden auch so angezeigt (Pflicht im B2C-Verkauf in Deutschland). Technisch über Medusas "Price Preferences" gelöst (`/admin/price-preferences`, `is_tax_inclusive: true` für Währung UND Region – beide müssen übereinstimmen, siehe Bugfix unten). - **Zwei Steuersätze eingerichtet** für die Deutschland-Steuerregion: 7% ermäßigt für alle 13 Honig-Produkte (Lebensmittel), 19% Standard für alles andere (Bienenprodukte, Süßigkeiten, Kurse, Gutscheine, Patenschaft). **Bitte mit einem Steuerberater gegenprüfen** – insbesondere Gutscheine und Kurse/Patenschaften können abweichende umsatzsteuerliche Behandlung erfordern, das habe ich nur nach bestem Wissen als Entwickler, nicht als Steuerexperte eingerichtet. - **Grundpreis (Preis pro kg)** wird auf Produktseite und in der Shop-Übersicht angezeigt, wo vorhanden – berechnet aus dem tatsächlichen Nettofüllgewicht (z.B. "500 Gramm" aus dem Variantennamen), nicht aus dem Versandgewicht. Gespeichert als `metadata.net_weight_grams` pro Variante. Nur bei den 13 Honigsorten vorhanden (einzige Produkte, die nach Gewicht verkauft werden – für Gutscheine, Kurse, Kerzen etc. ist kein Grundpreis vorgeschrieben). - **Lieferzeit** ("2-3 Werktage") als `metadata.delivery_time` pro Produkt hinterlegt und auf der Produktseite separat angezeigt (vorher stand das nur unstrukturiert im Beschreibungstext). - **Kategorie-Badge** (Honig, Bienenprodukte, Süßigkeiten mit Honig, Geschenkgutschein, Kurse, Bienenpatenschaften) aus den ursprünglichen Shopify-Tags als `metadata.category` übernommen, auf Shop-Karte und Produktseite sichtbar. **Wichtiger Bugfix:** Medusa hatte Produktpreise und Versandkosten inkonsistent besteuert – Produkte inkl. MwSt. (Bruttopreis blieb gleich), Versand exkl. MwSt. (Steuer wurde zusätzlich draufgerechnet, 10€ Versand wurden im Warenkorb zu 11,90€). Ursache: zwei widersprüchliche Price-Preference-Einträge (`currency_code: eur` → inklusiv, `region_id` Deutschland → exklusiv). Behoben, indem die Region-Preference ebenfalls auf `is_tax_inclusive: true` gesetzt wurde. End-to-End getestet: 7,90€ Honig + 10€ Versand = 17,90€ Gesamtsumme, keine versteckte Aufpreisung mehr. ## Lokal starten ```bash # Postgres + Redis + Backend docker compose up -d # oder Backend lokal für Entwicklung (schneller iterieren): cd backend/apps/backend && npm run dev # läuft auf Port 9001 (9000 war lokal belegt) # Frontend cd frontend && npm run dev # läuft auf Port 4321 ``` Admin-Oberfläche für Produktpflege: **http://localhost:9001/app** (Login wurde angelegt: `admin@hhonig.de` / `ChangeMe123!` – bitte nach dem ersten Login ändern.) **Host-abhängiges Verhalten lokal testen:** ```bash curl -H "Host: hhonig.de" http://localhost:4321/ # → Privatkunden curl -H "Host: place4bees.com" http://localhost:4321/ # → Business ``` (`server.allowedHosts` in `astro.config.mjs` erlaubt diese Test-Hostnamen gegen den Dev-Server.) ## Bereits erledigt - Medusa-Backend lokal lauffähig (Postgres + Redis via Docker), Admin-User angelegt, Admin-UI getestet. - Astro-Frontend mit Tailwind, Node-SSR-Adapter, zentralen Design-Tokens. - Eine Website, zwei Bereiche (Privatkunden/Business) mit persistentem Tab-Umschalter, host-abhängigem Default, konsistent auf beiden Domains erreichbar. - Shop-Anbindung an Medusa Store API funktioniert End-to-End (Produktliste + Produktdetail). - Kompletter Checkout-Flow (Cart → Adresse → Versand → Zahlung → Bestellung) gebaut und End-to-End getestet, Bestellung erscheint im Admin. - **Produktkatalog von shop.hhonig.de migriert:** alle 23 Produkte (Honig, Bienenprodukte, Patenschaften, Kurse, Gutscheine, Süßigkeiten) über Shopifys öffentliche `products.json`-API ausgelesen und per Medusa Admin API angelegt – inkl. Varianten (Größen/Nennwerte), Preisen und Bildern. Migrations-Skript: siehe unten. - Preis-Darstellung im Frontend korrigiert: Medusa v2 speichert Beträge als Dezimalwert (10 = 10,00 €, keine Cent-Umrechnung) – frühere `/100`-Division in Warenkorb/Kasse/Produktseite entfernt. - Produktseite zeigt bei mehreren Größen/Varianten jetzt den zur aktuellen Auswahl passenden Preis (aktualisiert sich beim Wechsel der Variante). - Produktbilder selbst gehostet (nicht mehr auf Shopify-CDN verlinkt), siehe Produktmigration unten. - PayPal-Backend vorbereitet (Plugin, Provider, Region-Aktivierung) – Frontend-Buttons folgen, siehe "Nächste Schritte". - MwSt. (7%/19%, inkl.-Anzeige), Grundpreis (€/kg) und Lieferzeit je Produkt ergänzt, siehe eigener Abschnitt unten. - Redesign: dunkle Hero-/Footer-Flächen mit gestaffelter Hexagon-Grafik, überarbeitete Shop-Karten mit Preis/Grundpreis/Kategorie-Badge. ## Produktmigration (Shopify → Medusa) Einmalig ausgeführtes Node-Skript, kein Teil der laufenden App: holt den Katalog über Shopifys öffentliche `https://shop.hhonig.de/products.json`-API und legt jedes Produkt per Medusa Admin API an (Varianten, Preise, Bilder). Nicht im Repo abgelegt (lief aus dem Scratchpad); bei Bedarf einfach neu schreiben oder erneut anfragen. Bewusste Vereinfachungen beim Import, die noch nachgezogen werden sollten: - ~~Bilder waren extern auf `cdn.shopify.com` verlinkt~~ – **erledigt:** alle 52 Bilder heruntergeladen und über Medusas lokales File-Modul selbst gehostet (`/static/...` auf dem Medusa-Server). Konfiguriert in `medusa-config.ts` (`@medusajs/file-local`, `backend_url` aus `MEDUSA_BACKEND_URL`). Für Produktion: `MEDUSA_BACKEND_URL` auf die echte Backend-Domain setzen, sonst zeigen die Bild-URLs auf `localhost`. - **Tags/Kategorien wurden nicht übernommen** (Medusas Tag-API verlangt vorab angelegte Tag-IDs). Die Shopify-Kategorien (Honig, Bienenprodukte, Bienenpatenschaften, Kurse, Geschenkgutschein, Süßigkeiten) liegen aber in den Rohdaten vor und lassen sich bei Bedarf nachträglich als Medusa-Kategorien/Collections anlegen, sobald es eine Kategorie-Navigation im Frontend gibt. - **Varianten sind auf `manage_inventory: false`** gesetzt (immer bestellbar, keine Lagerbestandsprüfung) – für den Start pragmatisch, sollte aber vor Go-Live durch echte Lagerbestände ersetzt werden, damit nichts verkauft wird, was nicht vorrätig ist. - Ein Medusa-Demo-Produkt ("Medusa T-Shirt") ließ sich nicht löschen, da eine Testbestellung eine Lagerreservierung dafür hält – unschädlich, kann später im Admin entfernt werden, sobald die Testbestellung storniert/gelöscht ist. ## Nächste Schritte 1. PayPal-Sandbox-Konto verbinden (Medusa Admin → Settings → PayPal) und danach die PayPal-Buttons im Frontend bauen (siehe oben). 2. Steuersätze/-zuordnung von einem Steuerberater gegenprüfen lassen (siehe oben), bevor echte Bestellungen laufen. 3. Echte Lagerbestände pflegen statt `manage_inventory: false` (aktuell immer bestellbar, keine Bestandsprüfung). 4. Kontakt-/Anfrage-Formulare (`/kontakt`, `/business/anfrage`) an einen Versand-Endpunkt anbinden (z.B. E-Mail-Versand über eigenes API-Route in Astro). 5. Produktions-Deployment: Reverse-Proxy/Server so einrichten, dass beide Domains (hhonig.de, place4bees.com) auf dieselbe Astro-Instanz zeigen (Host-Header muss unverändert durchgereicht werden) und 301-Redirect-Mapping für geänderte Alt-URLs erstellen. 6. Redis-gestützte Event-Bus/Cache-Module für Produktionsbetrieb ergänzen (aktuell nutzt Medusa lokal In-Memory-Fallback). 7. Echte Medusa-Kategorien/Collections für die 6 Produktgruppen anlegen, sobald eine Kategorie-Navigation im Shop gewünscht ist (aktuell nur als Badge über `metadata.category`, keine Filter-Navigation). 8. Impressum/Rechtliches ergänzen (dort ggf. "Imkerei Björn Schumann" als rechtlicher Betreibername, falls weiterhin relevant).