config->get('mailgun.enabled', false)) { $this->writeToLog($to, $subject, $html); return true; } if (!function_exists('curl_init')) { throw new RuntimeException('The PHP cURL extension is required for Mailgun.'); } $domain = (string) $this->config->get('mailgun.domain'); $apiKey = (string) $this->config->get('mailgun.api_key'); if ($domain === '' || $apiKey === '') { throw new RuntimeException('Mailgun is enabled but its credentials are incomplete.'); } $from = sprintf( '%s <%s>', $this->config->get('mailgun.from_name', 'Fat Bottom Grille'), $this->config->get('mailgun.from_email') ); $curl = curl_init('https://api.mailgun.net/v3/' . rawurlencode($domain) . '/messages'); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_USERPWD => 'api:' . $apiKey, CURLOPT_POST => true, CURLOPT_POSTFIELDS => [ 'from' => $from, 'to' => $to, 'subject' => $subject, 'html' => $html, ], CURLOPT_TIMEOUT => 20, ]); $response = curl_exec($curl); $status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE); $error = curl_error($curl); curl_close($curl); if ($response === false || $status < 200 || $status >= 300) { throw new RuntimeException('Mailgun delivery failed: ' . ($error ?: 'HTTP ' . $status)); } return true; } private function writeToLog(string $to, string $subject, string $html): void { $directory = BASE_PATH . '/storage/logs'; if (!is_dir($directory)) { mkdir($directory, 0775, true); } $line = sprintf( "[%s] TO: %s | SUBJECT: %s\n%s\n\n", date('c'), $to, $subject, trim(strip_tags($html)) ); file_put_contents($directory . '/mail.log', $line, FILE_APPEND | LOCK_EX); } }