fix: Befunde aus Sicherheits- und Korrektheitsreview der Bienenpatenschaft
Docker Image bauen und veroeffentlichen / typecheck (push) Successful in 2m57s
Docker Image bauen und veroeffentlichen / lint (push) Successful in 3m7s
Docker Image bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Docker Image bauen und veroeffentlichen / test (push) Canceled after 1m22s
Docker Image bauen und veroeffentlichen / typecheck (push) Successful in 2m57s
Docker Image bauen und veroeffentlichen / lint (push) Successful in 3m7s
Docker Image bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Docker Image bauen und veroeffentlichen / test (push) Canceled after 1m22s
- Oeffentliche Kuendigung: exakte Identifikation, Limits je Vertrag und Adresse, Bestaetigung nur an die Kontoadresse mit Ruecknahme-Link (HMAC) - Vorschau: Body-Grenze beim Lesen, Parallelitaets- und Minutenlimit, Cache - Rabatte gelten nicht fuer Patenschaftspositionen - Abo aktiv erst nach bestaetigter Zahlung, offene Abschluesse zaehlen zum Kontingent und werden nach 24 h aufgeraeumt, Sitzung 58 Minuten - Konto-Loeschung nur ohne laufendes Abo, Urkunden-Dateien werden entfernt - Abo-Lieferungen mit Zahlungszeile (DHL-Marke moeglich) - Shop zeigt freien Bestand nach Abo-Reservierung - Storno/volle Erstattung widerruft die Patenschaft (Status WIDERRUFEN) - Umsatzsteuer der Patenschaft per VAT_RATE_PERCENT_PATENSCHAFT (19 %) - Digitale Bestellungen archivieren sich, Kalendertage in Europe/Berlin, Monatsenden begrenzt, Datenauskunft um Patenschaften ergaenzt Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f227aeb8f9
commit
515fca1c16
97 files changed
+2979
-254
No files matched your search
@@ -0,0 +1,53 @@
|
||||
"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<void> {
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user