Files
place4bees.com/README.md
T
2026-07-06 13:37:08 +02:00

14 KiB
Raw Blame History

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 (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 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

# 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:

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).