*/ function billing_plans(): array { return [ 'free' => ['label' => 'Kostenlos', 'max_participants' => 10, 'price_cents' => 0], 'basic' => ['label' => 'Basic', 'max_participants' => 25, 'price_cents' => 399], 'plus' => ['label' => 'Plus', 'max_participants' => 50, 'price_cents' => 799], 'pro' => ['label' => 'Pro', 'max_participants' => 150, 'price_cents' => 1299], 'enterprise' => ['label' => 'Enterprise (auf Anfrage)', 'max_participants' => null, 'price_cents' => 0], ]; } /** * Returns the cheapest plan whose participant limit still covers * $activeParticipantCount. Used both to show customers which plan they * need and, once Stripe is wired up, to detect required upgrades. */ function billing_required_plan_code(int $activeParticipantCount): string { foreach (billing_plans() as $code => $plan) { if ($plan['max_participants'] === null || $activeParticipantCount <= $plan['max_participants']) { return $code; } } return 'enterprise'; } function billing_count_active_participants(PDO $pdo, int $tenantId): int { $stmt = $pdo->prepare('SELECT COUNT(*) FROM participants WHERE tenant_id = ? AND active = 1'); $stmt->execute([$tenantId]); return (int)$stmt->fetchColumn(); } /** * @return array{tenant_id: int, plan_code: string, subscription_status: string, stripe_customer_id: ?string, stripe_subscription_id: ?string, current_period_end: ?string} */ function billing_fetch_or_init(PDO $pdo, int $tenantId): array { $stmt = $pdo->prepare('SELECT * FROM tenant_billing WHERE tenant_id = ?'); $stmt->execute([$tenantId]); $row = $stmt->fetch(); if ($row === false) { $pdo->prepare("INSERT INTO tenant_billing (tenant_id, plan_code, subscription_status) VALUES (?, 'free', 'active')") ->execute([$tenantId]); return [ 'tenant_id' => $tenantId, 'plan_code' => 'free', 'subscription_status' => 'active', 'stripe_customer_id' => null, 'stripe_subscription_id' => null, 'current_period_end' => null, ]; } return $row; } /** * Compares the tenant's currently booked plan against what their active * participant count actually requires. Purely informational until the * Stripe integration lands — nothing here blocks usage or auto-upgrades. * * @return array{current_plan: string, required_plan: string, active_participants: int, needs_upgrade: bool} */ function billing_check_tenant(PDO $pdo, int $tenantId): array { $billing = billing_fetch_or_init($pdo, $tenantId); $activeCount = billing_count_active_participants($pdo, $tenantId); $requiredPlan = billing_required_plan_code($activeCount); $plans = array_keys(billing_plans()); $currentRank = array_search($billing['plan_code'], $plans, true); $requiredRank = array_search($requiredPlan, $plans, true); return [ 'current_plan' => $billing['plan_code'], 'required_plan' => $requiredPlan, 'active_participants' => $activeCount, 'needs_upgrade' => $requiredRank !== false && $currentRank !== false && $requiredRank > $currentRank, ]; }