Withdrawal function (§ 356a BGB, mandatory since 19 June 2026): - /vertrag-widerrufen: two-step flow "Vertrag widerrufen" -> "Widerruf bestätigen", no login, only name, order number and e-mail required. Prominent footer button on every page, prefilled link per order in the customer account. - /api/widerruf stores every declaration (matched to the order only if order number and e-mail fit), mails a receipt with the declaration and the exact time of receipt, notifies ADMIN_EMAIL. Same response for matching and non-matching orders (no enumeration), origin check and rate limit instead of a CAPTCHA that could block a statutory right. User input is HTML-escaped and stripped of line breaks in headers. - /admin/widerrufe lists declarations, open ones first, with an open-count badge and "mark as done". - Withdrawal notice gains the statutory sentence on the online function. Other fixes: - Newsletter stores confirmedAt as proof of consent (Art. 7(1) GDPR). - "Recently viewed" is only persisted with consent (§ 25(1) TDDDG); choosing "essential only" or withdrawing consent deletes it. The banner no longer promises audience-measurement cookies that do not exist. - Admin rejects an invalid company e-mail; an obfuscated stored value falls back to the configured address so the legal notice stays valid. - Privacy policy: Microsoft 365 as mail processor, log retention of at most 14 days, withdrawal-function processing, consent timestamps. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
28 lines
1.0 KiB
SQL
28 lines
1.0 KiB
SQL
-- Elektronische Widerrufsfunktion (§ 356a BGB, Pflicht seit 19.06.2026)
|
|
-- und Nachweis der Newsletter-Einwilligung (Art. 7 Abs. 1 DSGVO).
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "withdrawal_declarations" (
|
|
"id" TEXT NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"orderNumber" TEXT NOT NULL,
|
|
"email" TEXT NOT NULL,
|
|
"message" TEXT,
|
|
"locale" TEXT NOT NULL DEFAULT 'de',
|
|
"orderId" TEXT,
|
|
"receivedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"confirmationSentAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "withdrawal_declarations_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "withdrawal_declarations_receivedAt_idx" ON "withdrawal_declarations"("receivedAt");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "withdrawal_declarations" ADD CONSTRAINT "withdrawal_declarations_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "orders"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AlterTable
|
|
-- Nullable: fuer bestehende Abonnements ist der Zeitpunkt nicht bekannt.
|
|
ALTER TABLE "newsletter_subscribers" ADD COLUMN "confirmedAt" TIMESTAMP(3);
|