userPublicPayload($user)]); case 'request_email_verification': $email = strtolower(trim((string)($_POST['email'] ?? ''))); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) jsonResponse(['error' => 'Enter a valid email'], 400); $stmt = getDB()->prepare("SELECT id FROM users WHERE email=? AND id!=?"); $stmt->execute([$email, $user['id']]); if ($stmt->fetchColumn()) jsonResponse(['error' => 'That email is already in use'], 409); try { sendVerificationEmail($user, $email); } catch (Throwable $e) { jsonResponse(['error' => 'Verification email could not be sent: ' . $e->getMessage()], 503); } jsonResponse(['success' => true, 'message' => 'Verification email sent']); case 'verify_email': $token = trim((string)($_POST['token'] ?? $_GET['token'] ?? '')); $code = trim((string)($_POST['code'] ?? '')); if (!consumeEmailVerification($user, $token, $code)) { jsonResponse(['error' => 'Invalid or expired verification code'], 400); } $fresh = userById((int)$user['id']); if (!empty($fresh['stripe_customer_id'])) { try { require_once ROOT_DIR . '/lib/stripe.php'; stripeRequest('POST', 'customers/' . rawurlencode($fresh['stripe_customer_id']), ['email' => $fresh['email']]); } catch (Throwable $e) { error_log('Stripe email update failed: ' . $e->getMessage()); } } jsonResponse(['success' => true, 'user' => userPublicPayload($fresh)]); case 'set_color': if (!featureValue($user, 'username_color', false)) jsonResponse(['error' => 'Your plan does not include custom colors'], 403); $color = trim((string)($_POST['color'] ?? '')); if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) jsonResponse(['error' => 'Invalid color'], 400); getDB()->beginTransaction(); getDB()->prepare("UPDATE users SET color=? WHERE id=?")->execute([$color, $user['id']]); getDB()->prepare("UPDATE messages SET color=? WHERE user_id=?")->execute([$color, $user['id']]); getDB()->commit(); jsonResponse(['success' => true, 'user' => userPublicPayload(userById((int)$user['id']))]); case 'set_2fa': if (!featureValue($user, 'email_2fa', false)) jsonResponse(['error' => 'Your plan does not include email 2FA'], 403); if (empty($user['email_verified_at'])) jsonResponse(['error' => 'Verify an email first'], 400); $enabled = filter_var($_POST['enabled'] ?? false, FILTER_VALIDATE_BOOLEAN) ? 1 : 0; getDB()->prepare("UPDATE users SET two_factor_enabled=? WHERE id=?")->execute([$enabled, $user['id']]); jsonResponse(['success' => true, 'user' => userPublicPayload(userById((int)$user['id']))]); default: jsonResponse(['error' => 'Unknown action'], 400); }