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
+67
View File
@@ -3,6 +3,7 @@
declare(strict_types=1);
require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/legal.php';
function saas_mail_transport(): string
{
@@ -172,6 +173,7 @@ function saas_log_outbound_email(
?string $error,
?int $createdByUserId
): int {
$pdo->exec('DELETE FROM outbound_emails WHERE created_at < DATE_SUB(NOW(), INTERVAL 180 DAY)');
$stmt = $pdo->prepare(
'INSERT INTO outbound_emails
(tenant_id, participant_id, template, subject, status, sent_at, error, created_by_user_id)
@@ -267,6 +269,71 @@ function saas_send_email_verification_mail(string $to, string $token): array
return saas_send_mail($to, 'Kaffeeliste E-Mail bestätigen', $body);
}
function saas_send_registration_contract_mail(string $to, string $tenantName, string $tenantSlug, string $customerType): array
{
$body = implode("\n", [
'Vertragsbestätigung Kaffeeliste',
'',
'Kunde: ' . $tenantName,
'Kundenkürzel: ' . $tenantSlug,
'Kundentyp: ' . ($customerType === 'consumer' ? 'Verbraucher' : 'Unternehmer'),
'Tarif: Kostenlos, 0,00 € pro Monat, unbefristet, jederzeit kündbar',
'Vertragsschluss: ' . date('d.m.Y H:i') . ' Uhr (Europe/Berlin)',
'',
'Nachfolgend erhältst du die bei Vertragsschluss geltenden Texte auf einem dauerhaften Datenträger.',
'',
'================ AGB ================',
app_legal_document_text('terms'),
'',
'================ AVV ================',
app_legal_document_text('dpa'),
'',
'========== WIDERRUFSBELEHRUNG ==========',
app_legal_document_text('withdrawal_information'),
]);
return saas_send_mail($to, 'Kaffeeliste Vertragsbestätigung kostenloser Tarif', $body);
}
function saas_send_paid_contract_confirmation(PDO $pdo, int $tenantId, string $planCode): array
{
require_once __DIR__ . '/billing.php';
$stmt = $pdo->prepare(
"SELECT t.name, t.slug, t.customer_type, u.email
FROM tenants t
JOIN tenant_memberships tm ON tm.tenant_id = t.id AND tm.role = 'owner' AND tm.status = 'active'
JOIN users u ON u.id = tm.user_id AND u.status = 'active'
WHERE t.id = ? ORDER BY tm.id LIMIT 1"
);
$stmt->execute([$tenantId]);
$contract = $stmt->fetch();
$plan = billing_plans()[$planCode] ?? null;
if ($contract === false || $plan === null) {
return ['ok' => false, 'transport' => saas_mail_transport(), 'error' => 'contract confirmation data missing'];
}
$body = implode("\n", [
'Vertragsbestätigung Kaffeeliste kostenpflichtiger Tarif',
'',
'Kunde: ' . $contract['name'],
'Kundenkürzel: ' . $contract['slug'],
'Kundentyp: ' . ($contract['customer_type'] === 'consumer' ? 'Verbraucher' : 'Unternehmer'),
'Tarif: ' . $plan['label'],
'Gesamtpreis: ' . number_format(((int)$plan['price_cents']) / 100, 2, ',', '') . ' € pro Monat',
'Laufzeit: unbefristet; monatliche Abrechnung; keine längere Mindestlaufzeit',
'Kündigung: jederzeit zum Ende der laufenden Abrechnungsperiode',
'Bestätigung: ' . date('d.m.Y H:i') . ' Uhr (Europe/Berlin)',
'',
'================ AGB ================',
app_legal_document_text('terms'),
'',
'========== WIDERRUFSBELEHRUNG ==========',
app_legal_document_text('withdrawal_information'),
]);
return saas_send_mail((string)$contract['email'], 'Kaffeeliste Vertragsbestätigung ' . $plan['label'], $body);
}
/**
* Textkoerper der automatischen Zahlungserinnerung an ein Mitglied mit
* offenem Betrag oberhalb der Warnschwelle des Mandanten.