Digitales Produkt Landingpage - Funktionsweise & Übersicht

Funktionsweise & Übersicht

Zielgruppe: Alle (Administratoren, Shop-Benutzer, Entwickler)

Was ist das Modul?

DigitalProductPage stellt für jeden Shop-Benutzer eine oder mehrere öffentlich erreichbare Landingpages bereit. Eine Landingpage ist eine Seite mit fester URL, die per QR-Code, Mail oder Messenger weitergegeben werden kann – typische Einsatzfälle:

  • digitale Speisekarte eines Restaurants

  • Veranstaltungs- oder Event-Karte

  • Saisonkarte (Mittagstisch, Catering, Sommerkarte)

  • Visitenkarten-Ersatz mit personalisiertem Profil

Das Modul unterscheidet sich vom älteren DigitalProduct-Modul: dort bekommt jede Bestellung eine eigene Landingpage. Hier verwaltet stattdessen jeder Shop-Benutzer seine eigenen Landingpages über die Shop-Oberfläche – die Bestellung der personalisierten Druckdaten (z.B. ein Flyer mit aufgedrucktem QR-Code) ist davon entkoppelt.

URL-Aufbau

Jede Landingpage wird über eine URL mit drei Bausteinen erreicht:

wide760/page/{prefix}/{name}[/{tab}]]]>

Baustein

Beschreibung

Beispiel

{prefix}

Öffentliche Benutzer-ID, eindeutig pro Shop-Benutzer. Wahlweise eine automatisch generierte ULID oder ein normalisierter Wert aus einem Benutzerfeld (z.B. Kundennummer).

01HV2XKQP4MNRST8WQZJ6BYDE oder 854121

{name}

Vom Shop-Benutzer wählbarer Name (Slug) der konkreten Landingpage. Eindeutig pro Benutzer.

pizzeria-venezia

{tab}

Optionaler Unter-Tab innerhalb derselben Landingpage (z.B. einzelne Speisekartenbereiche).

mittag, getraenke

Vollständige Beispiele:

wide760

Wichtig: Es gibt keinen Root-Endpunkt /page/{prefix} ohne Slug. Wer nur den Prefix kennt, bekommt eine 404 — so kann durch reines Probieren des Prefixes nicht festgestellt werden, ob ein Benutzer existiert oder welche Landingpages er hat.

Prefix-Strategie

Der {prefix} macht den entscheidenden Unterschied zwischen sicher und enumerierbar:

Strategie

URL-Beispiel

Eigenschaft

ULID (Standard)

/page/01HV2XKQP4MNRST8WQZJ6BYDE/pizzeria-venezia

Nicht erratbar, garantiert eindeutig, kein Pflichtfeld nötig.

Aus Benutzerfeld (opt-in)

/page/854121/pizzeria-venezia

Sprechender, kann aber doppelt vorkommen oder leer sein. Erfordert sorgfältige Konfiguration (siehe Admin-Anleitung).

Egal welche Strategie gewählt wird, das Modul speichert die fertige öffentliche ID zentral und modulübergreifend ab, nicht direkt am Benutzer. So bleibt die Benutzerverwaltung unverändert, und derselbe Mechanismus ist später auch für andere Entitäten (Artikel, Bestellungen) wiederverwendbar.

Slug-Historie

Wenn ein Shop-Benutzer den Namen einer Landingpage ändert, wird der alte Name in einer Historie gespeichert, damit bereits gedruckte QR-Codes auf den alten Namen weiterhin funktionieren (HTTP 301 auf den neuen Namen). Der Benutzer sieht in der Verwaltung, wie oft jeder alte Name noch aufgerufen wurde, und kann ihn explizit freigeben, sobald er sich sicher ist, dass keine alten QR-Codes mehr im Umlauf sind.

Beim Umbenennen und Löschen wird der History-Eintrag unbedingt angelegt — auch wenn die alte Adresse nie aufgerufen wurde. Hintergrund: Eine neue Landingpage kann durchaus per QR-Code gedruckt werden, bevor der erste Scan passiert; ohne History-Eintrag gäbe es für den gedruckten QR-Code keine Weiterleitung. Historie-Einträge, die nie benutzt wurden, räumt der Benutzer selbst über die „Freigeben"-Aktion auf.

Release-Pipeline: Vom Bestellabschluss zum Tab

Wenn ein Shop-Benutzer einen Artikel mit Printess-Feld-Zuordnung bestellt und die Bestellung freigegeben wird, greift die Release-Pipeline:

  1. Slug-Rename: Der im Printess-Formular eingegebene Landingpage-Name wird mit dem aktuellen Slug verglichen. Weicht er ab, wird die Landingpage automatisch umbenannt (der alte Slug wandert in die Historie und löst weiterhin eine 301-Weiterleitung aus).

  2. Tab-Erstellung: Der im Printess-Formular eingegebene Tab-Name wird als neuer Tab unterhalb der Landingpage angelegt.

  3. Nachbestellungs-Handling: Ist die aktuelle Bestellung eine Nachbestellung (Reorder), wird der Tab der Vorbestellung in den Papierkorb verschoben und ein neuer Tab mit dem aktuellen Inhalt angelegt. So bleibt die Zuordnung zur alten Bestellung in der Historie erhalten.

  4. Kollisions-Suffix: Existiert bereits ein aktiver Tab mit demselben Slug, bekommt der neue Tab ein Datums-Suffix (z.B. mittag-new-2026-04-15-143200). Der Shop-Benutzer sieht in der Verwaltung einen „Neuer Tab wartet"-Hinweis und kann den alten Tab per Ein-Klick-Ersetzen ablösen.

  5. Header-Medium auflösen: Ist am Artikel ein Header-Medium-Feld zugeordnet, wird der vom Kunden zurückgegebene URL-Wert geprüft und als Bild oder Video auf der Landingpage des zugehörigen Tabs angezeigt.

  6. Tagged-PDF: Die Screen-Version der Bestellposition wird so erzeugt, dass die PDF-Tag-Struktur erhalten bleibt (siehe unten).

Tab↔Bestellposition-Verknüpfung

Jeder Tab wird bei der Erstellung über die Release-Pipeline mit genau einer Bestellposition verknüpft. Diese 1:1-Verknüpfung:

  • verhindert das Löschen eines Tabs, solange eine aktive Bestellposition verknüpft ist (Delete-Schutz),

  • ermöglicht die Anzeige der verknüpften Bestell-ID als Badge in der Shop-Verwaltung,

  • bleibt bei Nachbestellungen nachvollziehbar, weil der abgelöste alte Eintrag mit einem Zeitstempel markiert wird, statt gelöscht zu werden.

Tagged-PDF (Screen-Version)

Für Landingpage-Artikel erzeugt die Release-Pipeline eine Tagged-PDF als Screen-Version. Im Gegensatz zu normalen Printess-Screen-Versionen wird diese PDF ohne Ghostscript-Optimierung gespeichert, weil GS die PDF-Tag-Struktur (Überschriften-Hierarchie) zerstören würde. Diese Tags werden auf der Landingpage ausgelesen, um ein navigierbares Kapitelverzeichnis darzustellen.

Papierkorb für Tabs

Tabs (Sub-Slugs) können vom Shop-Benutzer in einen Papierkorb verschoben werden — z.B. wenn die Mittagskarte einer Saison nicht mehr aktuell ist, ihre Verknüpfung mit der ursprünglichen Bestellung aber für eine spätere Nachbestellung erhalten bleiben soll. Beim Löschen wird der Slug automatisch um ein Datums-Suffix erweitert (z.B. mittagmittag-deleted-2026-04-14-113910), damit der ursprüngliche Slug sofort wieder für einen neuen Tab verfügbar ist. Aufruf-Statistiken bleiben erhalten.

Beim Wiederherstellen versucht das System, den ursprünglichen Slug zurückzusetzen — geht das nicht (weil ein neuer Tab den Slug inzwischen belegt hat), bleibt der Tab mit dem Suffix bestehen und der Benutzer kann ihn manuell umbenennen.

Ein endgültiges Löschen aus der Datenbank gibt es im Shop-UI nicht; das wäre eine manuelle Support-Aktion.

Öffentliche Landingpage-Darstellung

Die Landingpage unter /page/{prefix}/{name}[/{tab}] ist eine eigenständige Seite — komplett vom Shop-Layout losgelöst, eigenes HTML-Dokument mit eigenem CSS/JS. Das Design orientiert sich an einer dunklen, mobil-optimierten Menükarten-Darstellung (primärer Use-Case: QR-Code-Scan auf dem Smartphone).

Seitenstruktur

Bereich

Beschreibung

Header

Sticky, zeigt den Firmennamen des Shop-Benutzers, Burger-Button für Side-Menu

Banner

Optionaler Bild-/Video-Hero (pro Tab konfigurierbar, dreistufiger Fallback; siehe unten). Dismissible, fadet beim Scrollen aus

Suchfeld

Textsuche über PDF-Inhalte

Tab-Bar

Horizontale Tab-Buttons (nur sichtbar bei > 1 Tab), sticky unterhalb Header

PDF-Viewer

Pro Tab ein Container für die gerenderten PDF-Seiten

Info-Bereich

Adress- und Kontaktdaten des Shop-Benutzers

Side-Menu

Slide-in-Menü rechts: Tab-Switcher, Kapitelnavigation (aus PDF-Tags), Info-Button

Back-to-Top

Scroll-Button, erscheint ab 150px Scrolltiefe

PDF-Serving-Endpoint

Pro Tab wird das personalisierte PDF über einen eigenen Endpoint geladen:

wide760

Aspekt

Detail

Authentifizierung

Keine (sessionless, wie die Seite selbst)

Identifikation

tabPublicId = ULID des Tabs (stabil, ändert sich nie)

Cache-Busting

?v= Parameter ist ein Zeitstempel, der sich bei PDF-Regeneration ändert

Cache-Header

Mit ?v=: Cache-Control: public, max-age=31536000, immutable (1 Jahr). Ohne ?v=: Cache-Control: public, max-age=300 (5 Min)

Session-Cookies

Werden aus der PDF-Response entfernt, damit Browser-Caching funktioniert

Template-Architektur

Die Seite nutzt eine Vererbungs-Struktur mit überschreibbaren Blöcken, damit Kunden-Templates einzelne Bereiche anpassen können. Alle Block-Namen tragen den Prefix dpp- und sind nach Release permanent (dürfen nicht umbenannt werden).

Das CSS liegt als externe, browser-cachebare Datei vor. Alle Farben nutzen CSS-Variablen (--dpp-color-*) und sind damit auf ein zukünftiges Theming vorbereitet (aktuell nicht konfigurierbar). Zusätzlich wird das Shop-eigene Custom-CSS geladen, sodass Shop-Administratoren Feinheiten per Backend-CSS anpassen können.

JavaScript-Architektur

Die Landingpage nutzt zwei getrennte JavaScript-Komponenten:

  • PdfViewer: PDF-Rendering, Kapitelnavigation aus dem PDF-Tag-Tree (Überschriften-Hierarchie), Textsuche mit Highlight-Overlays, Sidebar-Toggle.

  • DppApp: UI-Shell mit Banner-Dismiss (Scroll oder Close-Klick), Back-to-Top-Button, Info-Section-Toggle, Sidebar-Backdrop-Close und Tab-URL-Sync.

Beim Tab-Wechsel greifen beide Komponenten ineinander: Der PdfViewer tauscht das PDF aus, DppApp aktualisiert die Browser-URL und tauscht das Banner. Der Browser-Back-Button navigiert korrekt zwischen den Tabs zurück.

Landing Title (Anzeigename)

Jede Landingpage hat neben dem URL-Slug einen optionalen Title (sprechender Anzeigename). Während der Slug URL-kompatibel sein muss (nur Kleinbuchstaben, Bindestriche, keine Sonderzeichen), kann der Title beliebige Zeichen enthalten — z.B. „Pizzeria Venezia" statt pizzeria-venezia.

Eigenschaft

Slug

Title

Verwendung

URL-Pfad (/page/.../pizzeria-venezia)

HTML <title>, Header, Side-Menu

Zeichen

Nur URL-sichere Zeichen (normalisiert)

Beliebig (Umlaute, Leerzeichen, Sonderzeichen)

Eindeutigkeit

Eindeutig pro Shop-Benutzer

Nicht eindeutig (mehrere Landings können gleich heißen)

Pflicht

Ja

Nein (Fallback auf Firmenname, dann Slug)

Editierbar

Shop-Verwaltung + Release-Pipeline

Shop-Verwaltung

Fallback-Kette für die Anzeige:

  1. Title (wenn gesetzt)

  2. Firmenname aus der Benutzeradresse

  3. Slug

Der Title wird in der Shop-Verwaltung über ein Inline-Formular mit zwei Feldern bearbeitet: „Anzeigename" und „URL-Pfad". Beide können unabhängig voneinander geändert werden.

Dreistufige Header-Medien

Das Hero-Medium oberhalb des PDF-Viewers (Banner) kann in drei Stufen konfiguriert werden. Der effektive Wert wird pro Tab live aufgelöst — es gibt keine Persistierung auf Landing-Ebene, d.h. Änderungen an Shop- oder Artikel-Defaults schlagen sofort auf alle bestehenden Tabs durch, die keinen eigenen Printess-Return haben.

Fallback-Kette pro Tab (niedrigste → höchste Priorität)

Stufe

Quelle

Gesetzt durch

Wann wirkt's?

1. Shop-Default

Modul-Konfigurationsseite — Medien-URL, Medientyp, Poster-URL, Alt-Text

Administrator

Für alle Tabs des Shops ohne höhere Stufe

2. Artikel-Override

Artikel-Einstellungen im Backend — gleiche vier Felder

Administrator

Für Tabs, deren Bestellposition auf diesen Artikel zeigt

3. Printess-Return

Vom Kunden im Printess-Editor gewählter Bild-/Video-Link

Release-Pipeline beim Freigeben der Bestellung

Für genau diesen Tab (1:1 zur Bestellposition)

Die Auflösung greift pro Feld: Ein Printess-Return liefert typischerweise nur URL + Typ, Poster-URL und Alt-Text fallen weiterhin auf Artikel- oder Shop-Ebene zurück.

Typ-Erkennung (Bild vs. Video)

Aus einer URL allein ist nicht eindeutig, ob es sich um ein Bild oder ein Video handelt. Daher läuft beim Setzen einer URL genau einmal ein HEAD-Request mit folgenden Regeln:

Bedingung

Verhalten

Nur <https://>

Andere Schemata werden abgelehnt.

Host nicht im privaten/internen Netzbereich

SSRF-Schutz; DNS-Resolution vor dem Request.

Content-Type in der Whitelist: gängige Bild-Formate (JPEG, PNG, WebP, AVIF, GIF) und Video-Formate (MP4, WebM, OGG)

Wird als Bild oder Video erkannt und gespeichert.

HEAD schlägt fehl (Timeout, 404)

Fallback auf Extension-Parse (.jpg/.mp4/…). Gelingt auch das nicht, wird das Ergebnis verworfen und der Admin bekommt eine Fehlermeldung.

Das Ergebnis wird auf allen drei Ebenen zusammen mit der URL persistiert. Die öffentliche Landingpage macht nie einen Laufzeit-HEAD.

Rendering

  • Bild-Medium: wird mit hoher Lade-Priorität eingebunden und für den initial sichtbaren Tab zusätzlich per Preload-Hinweis im HTML-Head referenziert.

  • Video-Medium: wird als Autoplay-Loop ohne Ton eingebettet, mit optionalem Poster-Bild als Platzhalter während des Ladens. Kein Preload (Mobile-Datenvolumen).

  • Keine URL auf keiner Stufe: Banner-Bereich wird auf Höhe 0 geschrumpft, kein Layout-Shift.

  • Tab-Wechsel: Das Banner wird live getauscht — ohne Seiten-Reload. Hat der Benutzer den Banner des aktuellen Tabs geschlossen und der nächste Tab hat dieselbe Banner-URL, bleibt er geschlossen.

  • Layout: Fester Seitenverhältnis-Rahmen (Standard 16:9) plus object-fit: cover — kein Layout-Shift beim Swap oder Erstladen.

Scope / Nicht enthalten

Die Umsetzung beschränkt sich bewusst auf das Hero-Medium. Nicht enthalten:

  • Farbtheme-Overrides (CSS-Variablen sind vorbereitet, können aber noch nicht über die drei Stufen gesetzt werden)

  • Layout-Varianten (Höhe, Position, Overlay-Text)

  • Datei-Upload als Eingabe (aktuell nur URL oder Printess-Return)

  • Shop-Benutzer-UI für manuelle Landing-Overrides (die dritte Stufe entsteht ausschließlich aus dem Printess-Editor-Feld)

QR-Code-Landingpage-Auswahl im Editor

Für Artikel, die einen QR-Code auf eine Landingpage drucken sollen — ohne dass der Artikel selbst Teil der Landing-Personalisierung ist — gibt es eine zusätzliche Artikel-Einstellung. Typische Einsatzfälle: Visitenkarten, Flyer, Tischaufsteller, die nur auf eine bestehende Landingpage verweisen sollen, aber keine eigene Landingpage erzeugen.

Funktionsprinzip

Am Artikel wird ein Printess-Listenfeld (z.B. ein Select-List) als QR-Code-Feld zugeordnet. Beim Öffnen des Printess-Editors wird dieses Feld dynamisch mit allen aktiven Landing- und Tab-URLs des Shop-Benutzers befüllt. Der Endkunde sieht im Editor ein Dropdown, wählt eine Adresse, und diese URL wird 1:1 als Wert des Listenfelds gesetzt und vom Printess-Template als QR-Code gerendert.

Unterschied zu den anderen Artikel-Settings

Aspekt

Prefix / Slug / SubSlug / Header-Medium

QR-Code-Listenfeld

Feldtyp

Freitext (Textbox, Textfeld)

Listenfeld (Select-List o.ä.)

Filter im Picker

Nur Freitext-Felder aktiv

Nur Listenfelder aktiv, Freitext disabled mit Suffix „Freitext – nicht kompatibel"

Auslöser für Release-Pipeline

Ja — löst Slug-Rename / Tab-Create / Tagged-PDF aus

Nein — reiner Dropdown-Seed, keine Release-Auswertung

Ziel-Feld im Editor

Wird vom Kunden frei editiert / als URL angezeigt

Wird vom Kunden aus fester Liste gewählt

Listenaufbau

Die Liste wird pro Editor-Open-Aufruf neu gebaut:

  1. Lazy-Create: Hat der Shop-Benutzer noch keine Landingpage, wird eine Default-Landingpage inkl. URL-Prefix angelegt — gleicher Pfad wie beim regulären Editor-Open.

  2. Filter: Nur aktive Landingpages und aktive Tabs.

  3. Sortierung: Pro Landingpage zuerst die Hauptseite, dann die Tabs in ihrer festgelegten Reihenfolge. Bei mehreren Landingpages werden diese in der Reihenfolge aus der Benutzer-Verwaltung angeordnet.

  4. Label: Pro Landingpage der Anzeigename (Fallback auf Slug). Pro Tab: „<Landing-Name> – <Tab-Name>".

  5. Wert: Die vollständige öffentliche URL der Landingpage bzw. des Tabs.

Default-Wert im Editor

Der erste Listeneintrag (= Hauptseite der ersten aktiven Landingpage) wird zusätzlich als Vorbelegung gesetzt, sodass das Dropdown beim ersten Editor-Aufruf nicht leer erscheint. Beim Re-Editieren einer bereits personalisierten Bestellung wird der vorher gewählte Wert vom Kunden beibehalten.

Keine Release-Integration

Der Release-Handler greift auf das gewählte Listenfeld nicht zu. Es wird nicht protokolliert, welche URL der Kunde als QR-Code bestellt hat, und es werden keine Slug- oder Tab-Historien-Einträge erzeugt. Die gedruckte URL ist einzig aus dem QR-Code des gerenderten Druckstücks ablesbar.

Sicherheit

Maßnahme

Schutz vor

Kein Root-Endpunkt /page/{prefix}

Enumeration der Benutzer über reines Prefix-Probieren

ULID als Standard-Prefix

Erratbarkeit der Benutzer-IDs

noindex, nofollow Meta-Tag auf jeder Landingpage

Indexierung durch Suchmaschinen

Disallow: /page/ in robots.txt

Crawling der Landingpages

Sessionless Route

Kein Cookie, kein Session-File bei anonymen Aufrufen

Rate Limiting auf /page/-Routen (45 Requests/Minute pro IP, HTTP 429 bei Überschreitung)

Brute-Force-Enumeration und Scraping

Eindeutige öffentliche IDs pro Mandant/Shop

Doppelte Prefixe innerhalb desselben Shops

Delete-Schutz für Tabs mit aktiver Bestellpositions-Verknüpfung

Versehentliches Löschen von Tabs, die noch mit einer aktiven Bestellposition verknüpft sind

Inaktive Tabs werden auf der öffentlichen Landingpage nicht angezeigt

Versehentliche Sichtbarkeit deaktivierter Inhalte

HTTPS-Pflicht + Block von privaten/internen Netzen + MIME-Whitelist bei Header-Medien

SSRF über externe Header-Medien-URLs, Einbettung falsch deklarierter Content-Types

Glossar

Begriff

Bedeutung

Shop-Administrator

Mitarbeiter im Backend, der das Modul für einen Shop aktiviert und konfiguriert.

Shop-Benutzer

Endkunde des Shops, der seine eigenen Landingpages über die Shop-Oberfläche pflegt.

Prefix

Öffentliche Benutzer-ID, erster Pfadteil nach /page/.

Slug

Vom Shop-Benutzer wählbarer Name der Landingpage.

Sub-Slug / Tab

Optionaler Unter-Tab innerhalb einer Landingpage (z.B. „Mittag", „Getränke").

ULID

„Universally Unique Lexicographically Sortable Identifier" – 26-stellige, nicht erratbare ID.

Release-Pipeline

Automatische Verarbeitung beim Bestellabschluss: Slug-Rename, Tab-Erstellung, Nachbestellungs-Handling, Header-Medium auflösen, Tagged-PDF-Erzeugung.

Tab↔Bestellposition-Verknüpfung

1:1-Zuordnung zwischen einem Tab und einer Bestellposition. Ermöglicht Delete-Schutz und Order-Badge in der Shop-Verwaltung.

Tagged-PDF

Screen-Version-PDF mit erhaltener Tag-Struktur (Überschriften-Hierarchie). Wird ohne Ghostscript-Optimierung gespeichert, damit die Kapitelstruktur auf der Landingpage ausgelesen werden kann.

Pending Replacement

Zustand, bei dem ein neuer Tab mit Kollisions-Suffix (z.B. -new-2026-04-15-143200) auf die Ablösung des alten gleichnamigen Tabs wartet. Der Shop-Benutzer kann per Ein-Klick-Aktion ersetzen.

DppApp

JavaScript-UI-Shell der öffentlichen Landingpage. Steuert Banner-Dismiss, Back-to-Top, Info-Section, Sidebar-Backdrop und Tab-URL-Sync.

PdfViewer

JavaScript-Komponente für das PDF-Rendering inklusive Kapitelnavigation aus den PDF-Tags und Textsuche mit Highlight-Overlays.

Landing Title

Optionaler sprechender Anzeigename einer Landingpage (z.B. „Pizzeria Venezia"). Unabhängig vom URL-Slug, beliebige Zeichen erlaubt. Wird in HTML <title>, Header und Side-Menu angezeigt.

Cache-Buster

?v=...-Parameter an der PDF-URL. Ändert sich bei PDF-Regeneration (z.B. Nachbestellung), sodass der Browser-Cache die neue Version lädt.

Header-Medium / Hero-Banner

Bild oder Video oberhalb des PDF-Viewers auf der öffentlichen Landingpage. Pro Tab aufgelöst, dreistufiger Fallback Shop → Artikel → Printess-Return.

QR-Code-Listenfeld

Printess-Listenfeld-Typ, das am Artikel zugeordnet wird. Im Editor automatisch mit allen Landing- und Tab-URLs des Shop-Benutzers gefüllt. Unabhängig von der DPP-Personalisierungs-Pipeline — reine Druck-Auswahl.