"use server"; import { redirect } from "next/navigation"; import { rateLimit, getClientIp } from "@/lib/rate-limit"; import { undoCancellation } from "@/lib/sponsorship-subscription/cancellation-undo"; import { parseUndoParams, resolveUndoAccess } from "./undo-access"; const UNDO_ATTEMPTS_PER_WINDOW = 10; const UNDO_WINDOW_MS = 60 * 60 * 1000; /** Ergebnis als Suchparameter — die Seite zeigt den passenden Text. */ export type UndoOutcome = "ok" | "ungueltig" | "nicht-gekuendigt" | "abgelaufen" | "fehler"; /** * "Kündigung zurücknehmen": prueft erneut Besitz oder signierten Link * (die Seite ist keine Sicherheitsgrenze) und nimmt die Kuendigung bei * Stripe zurueck. Server-Actions pruefen die Herkunft selbst (kein fremder * Cross-Site-POST). */ export async function undoCancellationAction(formData: FormData): Promise { const params = parseUndoParams({ abo: formData.get("abo"), bis: formData.get("bis"), sig: formData.get("sig") }); const back = (outcome: UndoOutcome): never => { const query = new URLSearchParams({ ergebnis: outcome }); if (params) { query.set("abo", params.subscriptionId); if (params.cancelAtSeconds !== null) query.set("bis", String(params.cancelAtSeconds)); if (params.signature) query.set("sig", params.signature); } redirect(`/vertraege-kuendigen/zuruecknehmen?${query.toString()}`); }; if (!params) return back("ungueltig"); if (!rateLimit(`cancellation-undo:${await getClientIp()}`, UNDO_ATTEMPTS_PER_WINDOW, UNDO_WINDOW_MS)) return back("fehler"); const now = new Date(); const access = await resolveUndoAccess(params, now); if (!access.ok) return back("ungueltig"); let outcome: UndoOutcome; try { const result = await undoCancellation({ subscriptionId: access.subscriptionId, expectedCancelAt: access.expectedCancelAt, ownerId: access.ownerId, now, }); outcome = result.ok ? "ok" : result.reason === "expired" ? "abgelaufen" : result.reason === "not_found" ? "ungueltig" : "nicht-gekuendigt"; } catch (error: unknown) { console.error(`[abo] Kuendigung von Abo ${access.subscriptionId} konnte nicht zurueckgenommen werden:`, error); outcome = "fehler"; } return back(outcome); }