Webshop A1/A2: Konfiguration, Schalter, Katalog shopSetup, Schema und Setup-Skript

config.shop (SHOP_ENABLED, SHOP_PILOT_EMAILS, SHOP_UPLOAD_DIR, STRIPE_CONNECT_WEBHOOK_SECRET), config.shopSetup (299 € einmalig),
Collection-IDs shopConfig/shopProducts/shopOrders/shopWiderrufe, services/shop/schalter.js, Katalogschluessel shopSetup in
stripeBilling und stripe-products.mjs, services/shop/schema.js, scripts/shop-setup.mjs (--check), schemaKeys fuer die Test-Mocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 08:30:21 +00:00
1 parent b168b10485
commit e9ab15bdb5
11 files changed
+759 -6

No files matched your search

+47
View File
@@ -30,6 +30,7 @@ function nonNegativeNumber(raw, fallback) {
}
const termineEnabled = (process.env.TERMINE_ENABLED || '').trim().toLowerCase()
const shopEnabled = (process.env.SHOP_ENABLED || '').trim().toLowerCase()
// Abo-Produkte (Spec 2026-09-27 "Verträge, Hosting-Abo und E-Mail-Zusatzspeicher"). Preise in Cent,
// Endpreise gemäß § 19 UStG. Texte, die Kunden sehen, stimmen mit den Vertragsbedingungen überein.
@@ -116,6 +117,16 @@ const termineSetup = {
priceNickname: 'Terminplaner-Einrichtung 149 €',
}
// Webshop: einmalige Einrichtung (Einmalpreis, kein Abo); der Shop selbst verkauft ueber das eigene Stripe-Konto des Kunden (Connect)
const shopSetup = {
key: 'shopSetup',
name: 'Webshop – Einrichtung',
amountCents: 29900,
lookupKey: 'webklar_shop_einrichtung_29900',
oneTime: true,
priceNickname: 'Webshop-Einrichtung 299 €',
}
// Ticketsystem (WOMS); eigene Konstante, weil auch die Enable-Banking-Rueckkehradresse darauf aufbaut
const womsBaseUrl = (process.env.WOMS_PUBLIC_URL || 'https://ticket.webklar.com').replace(/\/$/, '')
@@ -396,6 +407,11 @@ export const config = {
termineBookings: process.env.APPWRITE_COLLECTION_TERMINE_BOOKINGS || 'termineBookings',
termineClaims: process.env.APPWRITE_COLLECTION_TERMINE_CLAIMS || 'termineClaims',
termineOutbox: process.env.APPWRITE_COLLECTION_TERMINE_OUTBOX || 'termineOutbox',
// Webshop (Einstellungen, Artikel, Bestellungen, Widerrufe; scripts/shop-setup.mjs)
shopConfig: process.env.APPWRITE_COLLECTION_SHOP_CONFIG || 'shopConfig',
shopProducts: process.env.APPWRITE_COLLECTION_SHOP_PRODUCTS || 'shopProducts',
shopOrders: process.env.APPWRITE_COLLECTION_SHOP_ORDERS || 'shopOrders',
shopWiderrufe: process.env.APPWRITE_COLLECTION_SHOP_WIDERRUFE || 'shopWiderrufe',
},
publicBaseUrl: (process.env.PORTAL_PUBLIC_URL || 'https://project.webklar.com').replace(/\/$/, ''),
@@ -412,6 +428,7 @@ export const config = {
mailStorage,
newsSetup,
termineSetup,
shopSetup,
ustHinweis,
// Register der kaufbaren Funktionen (siehe oben)
features,
@@ -641,6 +658,36 @@ export const config = {
minAntwortMs: nonNegativeNumber(process.env.TERMINE_MIN_ANTWORT_MS, 300),
},
// Webshop (Funktionsschluessel 'shop'). Wird bei jedem Aufruf gelesen (Tests aendern die Werte zur Laufzeit);
// Schalter siehe server/services/shop/schalter.js
shop: {
// off (Vorgabe) | pilot (nur SHOP_PILOT_EMAILS) | on (alle Kunden)
mode: ['pilot', 'on'].includes(shopEnabled) ? shopEnabled : 'off',
// Kunden-E-Mails fuer den Pilotbetrieb; '*' = alle
pilotEmails: (process.env.SHOP_PILOT_EMAILS || '')
.split(',')
.map((s) => s.trim().toLowerCase())
.filter(Boolean),
// Fassung der Webshop-Bestellunterlagen (Leistungsbeschreibung, AVV, Bestelltexte): Berliner Kalendertag des
// Fassungs-Commits (JJJJ-MM-TT); eine ausgelieferte Fassung wird nie ueberschrieben
fassung: '2026-10-05',
// Artikelbilder der Shops: <uploadDir>/<shopId>/<datei>
uploadDir: process.env.SHOP_UPLOAD_DIR || path.join(process.cwd(), 'upload-data', 'shop'),
// Signing-Secret des Stripe-Connect-Webhooks (Ereignisse der verbundenen Konten); leer = Webhook antwortet 503
connectWebhookSecret: process.env.STRIPE_CONNECT_WEBHOOK_SECRET || '',
// Eine unbezahlte Bestellung haelt den Bestand (und die Stripe-Checkout-Sitzung) so lange
bestellAblaufMinuten: 30,
maxArtikel: 500,
maxBilder: 4,
maxVarianten: 30,
// Bestellungen werden so viele Jahre nach Abschluss anonymisiert (Aufbewahrungsfrist), siehe Block J
anonymisierenJahre: 3,
// Tage nach Abschalten der Funktion bis zur Loeschung der Shop-Daten
loeschTageNachAbschalten: 90,
// Unbezahlte (offene/stornierte) Bestellungen werden nach so vielen Tagen geloescht
unbezahltLoeschTage: 30,
},
invoiceNinja: {
url: (process.env.INVOICE_NINJA_URL || 'https://invoice.webklar.com').replace(/\/$/, ''),
apiToken: process.env.INVOICE_NINJA_API_TOKEN || '',