Rechtstexte und B2C-Vertragsabläufe absichern

This commit is contained in:
2026-08-22 14:31:56 +02:00
parent d320a4fd7a
commit a31a235422
58 changed files with 2502 additions and 316 deletions
+154
View File
@@ -0,0 +1,154 @@
<?php
require_once __DIR__ . '/app/database.php';
require_once __DIR__ . '/app/legal-requests.php';
$pdo = app_db_pdo();
app_start_session();
$h = static fn(mixed $value): string => htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
$stage = (string)($_POST['stage'] ?? 'form');
$postedRequestedEnd = (string)($_POST['requested_end'] ?? '');
if ($postedRequestedEnd === '') {
$postedRequestedEnd = (string)($_POST['end_choice'] ?? 'earliest') === 'specific'
? (string)($_POST['requested_end_date'] ?? '')
: 'earliest';
}
$values = [
'requester_name' => trim((string)($_POST['requester_name'] ?? '')),
'requester_email' => trim((string)($_POST['requester_email'] ?? '')),
'contract_reference' => strtolower(trim((string)($_POST['contract_reference'] ?? ''))),
'cancellation_kind' => (string)($_POST['cancellation_kind'] ?? 'ordinary'),
'request_reason' => trim((string)($_POST['request_reason'] ?? '')),
'requested_end' => $postedRequestedEnd,
];
$errors = [];
$stored = null;
$processing = null;
$mailStatus = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
app_require_csrf();
if ($stage === 'form') {
// Die Werte aus der Prüfseite werden wieder im Formular angezeigt.
} else {
$errors = app_validate_legal_request($values, 'cancellation');
}
if ($stage !== 'form' && $errors !== [] ) {
$stage = 'form';
} elseif ($stage === 'submit') {
if (!app_rate_limit_check($pdo, 'legal_request_ip:' . app_client_ip(), 5, 3600)) {
$errors[] = 'Zu viele Erklärungen von diesem Anschluss. Bitte nutze alternativ E-Mail oder das Ticketsystem.';
$stage = 'form';
} else {
$input = $values + [
'request_type' => 'cancellation',
'metadata' => ['cancellation_kind' => $values['cancellation_kind']],
];
$stored = app_store_legal_request($pdo, $input);
$processing = app_process_cancellation_request($pdo, $stored, $input);
$mailStatus = app_send_legal_request_confirmations($input, $stored, $processing);
$stage = 'complete';
}
} elseif ($stage !== 'form') {
$stage = 'review';
}
}
?>
<!DOCTYPE HTML>
<html lang="de">
<head>
<title>Vertrag kündigen Kaffeeliste</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
<link rel="stylesheet" href="assets/css/public.css">
</head>
<body class="is-preload public-page">
<div class="public-shell">
<section class="public-section public-legal">
<nav class="public-nav" aria-label="Hauptnavigation"><strong><a href="landing.php">Kaffeeliste</a></strong></nav>
<header class="major"><h1>Vertrag kündigen</h1></header>
<?php if ($stage === 'complete' && $stored !== null && $processing !== null): ?>
<div class="hint-box success">
<h2>Kündigung eingegangen</h2>
<p>Referenz: <b><?php echo $h($stored['reference_code']); ?></b><br>
Eingang: <?php echo $h($stored['received_at']); ?> Uhr (Europe/Berlin)<br>
Vertragsende: <?php echo $h($processing['effective_end']); ?></p>
<p><?php echo $h($processing['processing_note']); ?></p>
</div>
<?php if (($mailStatus['customer_ok'] ?? false) === true): ?>
<p>Wir haben die Eingangsbestätigung sofort an <?php echo $h($values['requester_email']); ?> gesendet.</p>
<?php else: ?>
<div class="hint-box error">
<p>Die Kündigung wurde gespeichert, aber die E-Mail-Bestätigung konnte nicht versendet werden. Bitte speichere die Bestätigung jetzt über den folgenden Link und kontaktiere uns bei Bedarf über das <a href="<?php echo $h(app_legal_ticket_url()); ?>">Ticketsystem</a>.</p>
</div>
<?php endif; ?>
<ul class="actions">
<li><a class="button primary" href="rechtserklaerung-bestaetigung.php?token=<?php echo $h($stored['access_token']); ?>&amp;download=1">Bestätigung als Textdatei speichern</a></li>
<li><a class="button" href="landing.php">Zur Startseite</a></li>
</ul>
<?php elseif ($stage === 'review'): ?>
<h2>Angaben prüfen</h2>
<p>
Name: <?php echo $h($values['requester_name']); ?><br>
E-Mail: <?php echo $h($values['requester_email']); ?><br>
Kundenkürzel: <?php echo $h($values['contract_reference']); ?><br>
Art: <?php echo $values['cancellation_kind'] === 'extraordinary' ? 'außerordentlich' : 'ordentlich'; ?><br>
Ende: <?php echo $values['requested_end'] === 'earliest' ? 'zum frühestmöglichen Zeitpunkt' : $h($values['requested_end']); ?>
</p>
<?php if ($values['request_reason'] !== ''): ?><p>Grund: <?php echo nl2br($h($values['request_reason'])); ?></p><?php endif; ?>
<form method="post" action="kuendigen.php">
<?php echo app_csrf_field(); ?>
<input type="hidden" name="stage" value="submit">
<?php foreach ($values as $name => $value): ?>
<input type="hidden" name="<?php echo $h($name); ?>" value="<?php echo $h($value); ?>">
<?php endforeach; ?>
<ul class="actions">
<li><button type="submit" class="primary">jetzt kündigen</button></li>
<li><button type="submit" name="stage" value="form" class="button">Angaben ändern</button></li>
</ul>
</form>
<?php else: ?>
<p>Hier kannst du einen kostenlosen oder kostenpflichtigen Kaffeeliste-Vertrag ordentlich oder außerordentlich kündigen. Eine Anmeldung ist nicht erforderlich.</p>
<?php if ($errors !== []): ?><div class="hint-box error"><?php foreach ($errors as $error): ?><p><?php echo $h($error); ?></p><?php endforeach; ?></div><?php endif; ?>
<form method="post" action="kuendigen.php">
<?php echo app_csrf_field(); ?>
<input type="hidden" name="stage" value="review">
<label for="requester_name">Vollständiger Name</label>
<input type="text" name="requester_name" id="requester_name" maxlength="255" value="<?php echo $h($values['requester_name']); ?>" required>
<label for="requester_email">E-Mail für die sofortige Bestätigung</label>
<input type="email" name="requester_email" id="requester_email" maxlength="255" value="<?php echo $h($values['requester_email']); ?>" required>
<label for="contract_reference">Kundenkürzel / Vertragsreferenz</label>
<input type="text" name="contract_reference" id="contract_reference" maxlength="100" value="<?php echo $h($values['contract_reference']); ?>" required>
<fieldset>
<legend>Art der Kündigung</legend>
<input type="radio" name="cancellation_kind" id="ordinary" value="ordinary"<?php echo $values['cancellation_kind'] === 'ordinary' ? ' checked' : ''; ?>>
<label for="ordinary">Ordentliche Kündigung</label>
<input type="radio" name="cancellation_kind" id="extraordinary" value="extraordinary"<?php echo $values['cancellation_kind'] === 'extraordinary' ? ' checked' : ''; ?>>
<label for="extraordinary">Außerordentliche Kündigung</label>
</fieldset>
<label for="request_reason">Kündigungsgrund (nur bei außerordentlicher Kündigung erforderlich)</label>
<textarea name="request_reason" id="request_reason" maxlength="5000" rows="4"><?php echo $h($values['request_reason']); ?></textarea>
<fieldset>
<legend>Zeitpunkt der Beendigung</legend>
<input type="radio" name="end_choice" id="end_earliest" value="earliest"<?php echo $values['requested_end'] === 'earliest' ? ' checked' : ''; ?>>
<label for="end_earliest">Zum frühestmöglichen Zeitpunkt</label>
<input type="radio" name="end_choice" id="end_specific" value="specific"<?php echo $values['requested_end'] !== 'earliest' ? ' checked' : ''; ?>>
<label for="end_specific">Zu einem bestimmten Datum</label>
<label for="requested_end_date">Gewünschtes Datum</label>
<input type="date" name="requested_end_date" id="requested_end_date" value="<?php echo $values['requested_end'] !== 'earliest' ? $h($values['requested_end']) : ''; ?>">
</fieldset>
<button type="submit" class="primary">Kündigung prüfen</button>
</form>
<?php endif; ?>
</section>
</div>
<?php echo app_public_legal_footer(); ?>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/browser.min.js"></script>
<script src="assets/js/breakpoints.min.js"></script>
<script src="assets/js/util.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>