import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { withdrawalSchema } from "@/lib/validation"; import { rateLimit, getClientIp } from "@/lib/rate-limit"; import { isTrustedOrigin } from "@/lib/csrf"; import { sendMail } from "@/lib/mail"; import { withdrawalReceiptEmail } from "@/lib/email-templates"; import { formatReceivedAt, withdrawalDeclarationLines } from "@/lib/withdrawal"; import { escapeHtml } from "@/lib/escape-html"; import { getLocale } from "@/i18n/locale"; const WITHDRAWALS_PER_WINDOW = 10; const WINDOW_MS = 60 * 60 * 1000; /** * Elektronische Widerrufsfunktion (§ 356a BGB), zweite Stufe "Widerruf * bestaetigen". Bewusst OHNE Turnstile: eine fehlschlagende Pruefung duerfte * die Ausuebung eines gesetzlichen Rechts nicht verhindern. Missbrauch * begrenzen Herkunftspruefung und Rate-Limit. * * Die Antwort ist fuer passende und unpassende Bestellnummern identisch — * sonst liessen sich Bestellnummern samt E-Mail-Adresse durchprobieren. Die * Erklaerung wird in jedem Fall gespeichert: sie ist mit dem Zugang wirksam * abgegeben, die Zuordnung zur Bestellung ist unsere Aufgabe (Admin). */ export async function POST(request: Request) { if (!isTrustedOrigin(request)) { return NextResponse.json({ error: "Ungültige Anfrage." }, { status: 403 }); } const ip = await getClientIp(); if (!rateLimit(`withdrawal:${ip}`, WITHDRAWALS_PER_WINDOW, WINDOW_MS)) { return NextResponse.json( { error: "Zu viele Anfragen. Bitte versuche es später erneut oder schreib uns eine E-Mail." }, { status: 429 } ); } const body: unknown = await request.json().catch(() => null); const parsed = withdrawalSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: "Bitte überprüfe deine Angaben.", issues: parsed.error.flatten() }, { status: 400 } ); } const input = parsed.data; const locale = await getLocale(); const order = await prisma.order.findUnique({ where: { orderNumber: input.orderNumber }, select: { id: true, email: true }, }); const orderId = order && order.email.trim().toLowerCase() === input.email ? order.id : null; const declaration = await prisma.withdrawalDeclaration.create({ data: { ...input, locale, orderId }, }); const receipt = await withdrawalReceiptEmail(input, declaration.receivedAt, locale); const confirmationSent = await sendMail({ to: input.email, ...receipt }); if (confirmationSent) { await prisma.withdrawalDeclaration.update({ where: { id: declaration.id }, data: { confirmationSentAt: new Date() }, }); } // Interne Benachrichtigung: ein Widerruf hat eine 14-Tage-Frist fuer die // Erstattung und darf nicht bis zum naechsten Blick ins Admin liegen bleiben. const adminEmail = process.env.ADMIN_EMAIL; if (adminEmail) { const noticeLines = [ ...withdrawalDeclarationLines(input, "de"), "", `Eingegangen am: ${formatReceivedAt(declaration.receivedAt, "de")}`, orderId ? "Automatisch der Bestellung zugeordnet." : "Keine passende Bestellung gefunden — bitte manuell prüfen.", `Eingangsbestätigung an Kundschaft: ${confirmationSent ? "versendet" : "NICHT versendet"}`, ]; await sendMail({ to: adminEmail, subject: `Neuer Widerruf zu ${input.orderNumber.replace(/[\r\n]+/g, " ")}`, text: noticeLines.join("\n"), html: `

${noticeLines.map(escapeHtml).join("
")}

`, }); } return NextResponse.json( { receivedAt: formatReceivedAt(declaration.receivedAt, locale), lines: withdrawalDeclarationLines(input, locale), confirmationSent, }, { headers: { "Cache-Control": "no-store" } } ); }