Files
ladill-events/app/Services/Events/EventMailer.php
T
isaaccladandCursor 0c5a1ddb23
Deploy Ladill Events / deploy (push) Successful in 31s
Send Events mail from platform SMTP like Invoice, not owner Bird domain.
Use EventMailer with MAIL_FROM and organiser Reply-To instead of the Bird API, which rewrote the From address onto the account's verified domain (e.g. climp.me). Redesign the speaker invitation to use the shared Events email layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 17:09:05 +00:00

99 lines
2.8 KiB
PHP

<?php
namespace App\Services\Events;
use App\Services\Billing\BillingClient;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
/**
* Sends Ladill Events transactional email from the platform mailer (like Invoice),
* not the event owner's Bird SMTP domain. Reply-To is set to the organiser.
*/
class EventMailer
{
public const EMAIL_PRICE_MINOR = 1;
private ?string $lastError = null;
public function __construct(private readonly BillingClient $billing) {}
public function lastError(): ?string
{
return $this->lastError;
}
public function isConfigured(): bool
{
return filter_var((string) config('mail.from.address'), FILTER_VALIDATE_EMAIL) !== false;
}
public function send(
string $ownerPublicId,
string $to,
string $subject,
string $html,
?string $text = null,
?string $fromDisplayName = null,
?string $replyToEmail = null,
?string $replyToName = null,
): bool {
$this->lastError = null;
if (! $this->isConfigured()) {
$this->lastError = 'Outbound email is not configured on this Events instance.';
return false;
}
if (! $this->billing->canAfford($ownerPublicId, self::EMAIL_PRICE_MINOR)) {
$this->lastError = 'Insufficient Ladill wallet balance. Add funds in Billing and try again.';
return false;
}
$reference = 'events_email:'.Str::uuid();
try {
Mail::html($html, function ($message) use ($to, $subject, $text, $fromDisplayName, $replyToEmail, $replyToName): void {
$message->to($to)
->subject($subject)
->from(
(string) config('mail.from.address'),
$fromDisplayName ?: (string) config('mail.from.name'),
);
if ($replyToEmail) {
$message->replyTo($replyToEmail, $replyToName ?: null);
}
if ($text) {
$message->text($text);
}
});
} catch (\Throwable $e) {
Log::warning('Events mail send failed', ['error' => $e->getMessage(), 'to' => $to]);
$this->lastError = 'The email could not be sent. Please try again.';
return false;
}
if (! $this->billing->debit(
$ownerPublicId,
self::EMAIL_PRICE_MINOR,
'smtp',
'events_email',
$reference,
null,
'Events email to '.$to,
)) {
$this->lastError = 'Insufficient Ladill wallet balance. Add funds in Billing and try again.';
return false;
}
return true;
}
}