, "from_email":"...", "from_name":"..." } * Seed it with db/seed_email.php (reads BREVO_API_KEY from the environment). * * Send order: Brevo HTTP API (if configured) → SMTP via config.php EMAIL* constants * (if set) → false. Callers treat false as "not sent" and log a fallback (e.g. the * account verification link) so flows stay testable before email is wired up. */ class Mailer { /** @return bool true when the provider accepted the message. */ public static function send(string $toEmail, string $toName, string $subject, string $html, string $text = ''): bool { $cfg = json_decode((string) \Db::getValue( "SELECT metval FROM sp_settings WHERE keyval = 'email' AND status = 1 LIMIT 1" ), true) ?: []; if (strtolower((string) ($cfg['provider'] ?? '')) === 'brevo' && !empty($cfg['api_key'])) { return self::sendBrevo($cfg, $toEmail, $toName, $subject, $html, $text); } $host = defined('EMAILHOST') ? (string) EMAILHOST : ''; if ($host !== '' && !str_starts_with($host, 'ADD')) { return self::sendSmtp($toEmail, $toName, $subject, $html, $text); } return false; // unconfigured } private static function fromEmail(array $cfg): string { $from = trim((string) ($cfg['from_email'] ?? '')); return $from !== '' ? $from : 'noreply@' . (parse_url(URL, PHP_URL_HOST) ?: 'localhost'); } private static function fromName(array $cfg): string { $name = trim((string) ($cfg['from_name'] ?? '')); return $name !== '' ? $name : (defined('PROJECT_NAME') ? PROJECT_NAME : 'Website'); } private static function sendBrevo(array $cfg, string $toEmail, string $toName, string $subject, string $html, string $text): bool { try { $http = new \GuzzleHttp\Client(['timeout' => 12, 'http_errors' => false]); $res = $http->post('https://api.brevo.com/v3/smtp/email', [ 'headers' => [ 'api-key' => \Functions::Decrypt((string) $cfg['api_key']), 'accept' => 'application/json', 'content-type' => 'application/json', ], 'json' => [ 'sender' => ['name' => self::fromName($cfg), 'email' => self::fromEmail($cfg)], 'to' => [['email' => $toEmail, 'name' => $toName !== '' ? $toName : $toEmail]], 'subject' => $subject, 'htmlContent' => $html, 'textContent' => $text !== '' ? $text : trim(strip_tags($html)), ], ]); $code = $res->getStatusCode(); if ($code >= 200 && $code < 300) { return true; } error_log('[mailer] brevo HTTP ' . $code . ': ' . (string) $res->getBody()); return false; } catch (\Throwable $e) { error_log('[mailer] brevo exception: ' . $e->getMessage()); return false; } } private static function sendSmtp(string $toEmail, string $toName, string $subject, string $html, string $text): bool { try { $mail = new \PHPMailer\PHPMailer\PHPMailer(true); $mail->isSMTP(); $mail->Host = EMAILHOST; $mail->SMTPAuth = true; $mail->Username = defined('EMAILUSER') ? EMAILUSER : ''; $mail->Password = defined('EMAILPASSWORD') ? EMAILPASSWORD : ''; $mail->SMTPSecure = 'tls'; $mail->Port = 587; $mail->setFrom('noreply@' . (parse_url(URL, PHP_URL_HOST) ?: 'localhost'), defined('PROJECT_NAME') ? PROJECT_NAME : 'Website'); $mail->addAddress($toEmail, $toName !== '' ? $toName : $toEmail); $mail->isHTML(true); $mail->Subject = $subject; $mail->Body = $html; $mail->AltBody = $text !== '' ? $text : trim(strip_tags($html)); $mail->send(); return true; } catch (\Throwable $e) { error_log('[mailer] smtp send failed: ' . $e->getMessage()); return false; } } }