Rechtstexte und B2C-Vertragsabläufe absichern
This commit is contained in:
+51
-8
@@ -4,6 +4,7 @@ require_once __DIR__ . '/functions.php';
|
||||
require_once __DIR__ . '/app/billing.php';
|
||||
require_once __DIR__ . '/app/audit.php';
|
||||
require_once __DIR__ . '/app/saas-mail.php';
|
||||
require_once __DIR__ . '/app/legal.php';
|
||||
|
||||
$pdo = app_db_pdo();
|
||||
$user = saas_require_login();
|
||||
@@ -34,12 +35,22 @@ if (!billing_plan_selectable_for($pdo, $tenantId, $planCode)) {
|
||||
exit('Dieser Tarif deckt eure aktuelle Teilnehmerzahl nicht ab. Bitte einen passenden Tarif wählen.');
|
||||
}
|
||||
|
||||
if ($planCode !== 'free' && !in_array((string)($user['customer_type'] ?? ''), ['business', 'consumer'], true)) {
|
||||
http_response_code(400);
|
||||
exit('Bitte wähle zuerst in den Mandant-Einstellungen, ob der Vertrag geschäftlich oder als Verbraucher geführt wird.');
|
||||
}
|
||||
|
||||
$billing = billing_fetch_or_init($pdo, $tenantId);
|
||||
$baseUrl = saas_app_url('mandant-einstellungen.php');
|
||||
|
||||
if ($planCode !== 'free' && !empty($user['contract_ends_at'])) {
|
||||
http_response_code(409);
|
||||
exit('Für diesen Vertrag liegt bereits eine Kündigung vor. Bitte nutze das Ticketsystem, wenn du sie zurücknehmen möchtest.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Fall 1: Wechsel auf "free" (kein Stripe-Preis) mit bestehender bezahlter
|
||||
// Subscription -> echtes Kuendigen der Subscription, kein Checkout noetig.
|
||||
// Wechsel auf "free": die bezahlte Subscription wird zum Ende der bereits
|
||||
// bezahlten Periode gekündigt. Bis dahin bleiben Tarif und Funktionen aktiv.
|
||||
if ($plans[$planCode]['stripe_lookup_key'] === null) {
|
||||
if ($planCode !== 'free') {
|
||||
// enterprise hat ebenfalls keinen Stripe-Preis, ist aber kein
|
||||
@@ -49,7 +60,7 @@ try {
|
||||
}
|
||||
|
||||
if ($billing['stripe_subscription_id'] !== null) {
|
||||
$cancelResult = stripe_cancel_subscription((string)$billing['stripe_subscription_id']);
|
||||
$cancelResult = stripe_schedule_subscription_cancellation((string)$billing['stripe_subscription_id']);
|
||||
if (!$cancelResult['ok']) {
|
||||
app_audit_log($pdo, $tenantId, (int)$user['user_id'], 'billing.downgrade_failed', 'tenant', $tenantId, ['plan_code' => $planCode, 'error' => $cancelResult['error']]);
|
||||
http_response_code(502);
|
||||
@@ -57,11 +68,20 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
billing_update($pdo, $tenantId, [
|
||||
'plan_code' => 'free',
|
||||
'subscription_status' => 'canceled',
|
||||
if ($billing['stripe_subscription_id'] === null) {
|
||||
billing_update($pdo, $tenantId, [
|
||||
'plan_code' => 'free',
|
||||
'subscription_status' => 'active',
|
||||
]);
|
||||
} else {
|
||||
billing_update($pdo, $tenantId, [
|
||||
'subscription_status' => 'canceling',
|
||||
'current_period_end' => $cancelResult['current_period_end'],
|
||||
]);
|
||||
}
|
||||
app_audit_log($pdo, $tenantId, (int)$user['user_id'], 'billing.cancellation_scheduled', 'tenant', $tenantId, [
|
||||
'current_period_end' => $cancelResult['current_period_end'] ?? null,
|
||||
]);
|
||||
app_audit_log($pdo, $tenantId, (int)$user['user_id'], 'billing.downgraded_to_free', 'tenant', $tenantId);
|
||||
|
||||
header('Location: ' . $baseUrl . '?upgrade=success');
|
||||
exit;
|
||||
@@ -73,10 +93,24 @@ try {
|
||||
exit('Der Tarif ist bei Stripe aktuell nicht verfügbar. Bitte später erneut versuchen.');
|
||||
}
|
||||
|
||||
if (empty($_POST['accept_terms']) || empty($_POST['acknowledge_withdrawal']) || empty($_POST['request_early_performance'])) {
|
||||
http_response_code(400);
|
||||
exit('Bitte bestätige AGB, Widerrufsbelehrung und den gewünschten sofortigen Leistungsbeginn auf der Bestellseite.');
|
||||
}
|
||||
|
||||
$orderMetadata = [
|
||||
'plan_code' => $planCode,
|
||||
'monthly_price_cents' => (int)$plans[$planCode]['price_cents'],
|
||||
'customer_type' => (string)($user['customer_type'] ?? 'business'),
|
||||
];
|
||||
app_record_legal_acceptance($pdo, $tenantId, (int)$user['user_id'], 'terms', 'paid_order', $orderMetadata);
|
||||
app_record_legal_acceptance($pdo, $tenantId, (int)$user['user_id'], 'withdrawal_information', 'paid_order', $orderMetadata);
|
||||
app_record_legal_acceptance($pdo, $tenantId, (int)$user['user_id'], 'early_performance', 'paid_order', $orderMetadata);
|
||||
|
||||
// Fall 2: Es gibt bereits eine aktive/bezahlte Subscription bei Stripe ->
|
||||
// Preis in-place wechseln (Up- oder Downgrade zwischen bezahlten Stufen),
|
||||
// statt eine weitere Checkout-Session zu erzeugen. Stripe rechnet die
|
||||
// Differenz automatisch anteilig ab (Proration).
|
||||
// neue Preis gilt ohne Zwischenbelastung ab der nächsten Verlängerung.
|
||||
if ($billing['stripe_subscription_id'] !== null) {
|
||||
$subscription = stripe_get_subscription((string)$billing['stripe_subscription_id']);
|
||||
|
||||
@@ -102,6 +136,15 @@ try {
|
||||
]);
|
||||
app_audit_log($pdo, $tenantId, (int)$user['user_id'], 'billing.plan_switched', 'tenant', $tenantId, ['plan_code' => $planCode]);
|
||||
|
||||
$confirmation = saas_send_paid_contract_confirmation($pdo, $tenantId, $planCode);
|
||||
if (empty($confirmation['ok'])) {
|
||||
app_audit_log($pdo, $tenantId, (int)$user['user_id'], 'billing.contract_confirmation_failed', 'tenant', $tenantId, [
|
||||
'error' => $confirmation['error'] ?? 'unknown',
|
||||
]);
|
||||
header('Location: ' . $baseUrl . '?upgrade=success&contract_mail_failed=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Location: ' . $baseUrl . '?upgrade=success');
|
||||
exit;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user