144 lines
14 KiB
Markdown
144 lines
14 KiB
Markdown
# 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 `<link rel="canonical">` fest auf `https://place4bees.com/<pfad>` (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).
|