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
+11 -1
View File
@@ -6,6 +6,7 @@ require_once __DIR__ . '/app/database.php';
require_once __DIR__ . '/app/billing.php';
require_once __DIR__ . '/app/audit.php';
require_once __DIR__ . '/app/dolibarr.php';
require_once __DIR__ . '/app/saas-mail.php';
// Kein app_require_csrf()/saas_require_login(): Stripe ruft diesen
// Endpunkt unauthentifiziert von aussen auf. Die Echtheit wird
@@ -55,6 +56,12 @@ switch ($event['type']) {
'stripe_subscription_id' => (string)$object['subscription'],
]);
app_audit_log($pdo, $tenantId, null, 'billing.subscription_started', 'tenant', $tenantId, ['plan_code' => $planCode]);
$confirmation = saas_send_paid_contract_confirmation($pdo, $tenantId, $planCode);
if (!$confirmation['ok']) {
app_audit_log($pdo, $tenantId, null, 'billing.contract_confirmation_failed', 'tenant', $tenantId, [
'error' => $confirmation['error'] ?? 'unknown',
]);
}
}
break;
@@ -63,6 +70,9 @@ switch ($event['type']) {
$tenantId ??= billing_find_tenant_id_by_stripe_subscription($pdo, (string)($object['id'] ?? ''));
if ($tenantId !== null) {
$status = (string)($object['status'] ?? 'active');
if (!empty($object['cancel_at_period_end']) && in_array($status, ['active', 'trialing', 'past_due'], true)) {
$status = 'canceling';
}
$periodEnd = isset($object['current_period_end'])
? date('Y-m-d H:i:s', (int)$object['current_period_end'])
: null;
@@ -77,7 +87,7 @@ switch ($event['type']) {
// eindeutiger Zuordnung, sonst bleibt der bisherige Plan bestehen.
$lookupKey = (string)($object['items']['data'][0]['price']['lookup_key'] ?? '');
$mappedPlan = billing_plan_code_by_lookup_key($lookupKey);
if ($mappedPlan !== null && in_array($status, ['active', 'trialing', 'past_due'], true)) {
if ($mappedPlan !== null && in_array($status, ['active', 'trialing', 'past_due', 'canceling'], true)) {
$updateFields['plan_code'] = $mappedPlan;
}