Add per-customer SMS and Bird Integrations for event messaging.
Deploy Ladill Events / deploy (push) Successful in 28s
Deploy Ladill Events / deploy (push) Successful in 28s
Attendee and speaker comms use encrypted tenant relay credentials and skip platform-key fallback and Bird wallet double-debit. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,31 +2,36 @@
|
||||
|
||||
namespace App\Services\Events;
|
||||
|
||||
use App\Services\Billing\BillingClient;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Services\Messaging\CustomerEmailClient;
|
||||
use App\Services\Messaging\MessagingCredentialsService;
|
||||
|
||||
/**
|
||||
* Sends Ladill Events transactional email via the platform mailer
|
||||
* (noreply@ladill.com — same as CRM and other Ladill apps).
|
||||
* Sends Ladill Events transactional email to attendees and speakers via the
|
||||
* customer's Ladill Bird relay when configured in Integrations.
|
||||
*/
|
||||
class EventMailer
|
||||
{
|
||||
public const EMAIL_PRICE_MINOR = 1;
|
||||
|
||||
private ?string $lastError = null;
|
||||
|
||||
public function __construct(private readonly BillingClient $billing) {}
|
||||
public function __construct(
|
||||
private readonly MessagingCredentialsService $credentials,
|
||||
private readonly CustomerEmailClient $customerEmail,
|
||||
) {}
|
||||
|
||||
public function lastError(): ?string
|
||||
{
|
||||
return $this->lastError;
|
||||
}
|
||||
|
||||
public function isConfiguredForOwner(string $ownerPublicId): bool
|
||||
{
|
||||
return $this->credentials->forOwner($ownerPublicId)->hasValidBird();
|
||||
}
|
||||
|
||||
/** @deprecated Use isConfiguredForOwner() — attendee email requires per-account Bird credentials. */
|
||||
public function isConfigured(): bool
|
||||
{
|
||||
return filter_var((string) config('mail.from.address'), FILTER_VALIDATE_EMAIL) !== false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function send(
|
||||
@@ -40,54 +45,32 @@ class EventMailer
|
||||
): bool {
|
||||
$this->lastError = null;
|
||||
|
||||
if (! $this->isConfigured()) {
|
||||
$this->lastError = 'Outbound email is not configured on this Events instance.';
|
||||
$credential = $this->credentials->forOwner($ownerPublicId);
|
||||
if (! $credential->hasValidBird()) {
|
||||
$this->lastError = 'Connect Ladill Bird in Integrations before sending attendee email.';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->billing->canAfford($ownerPublicId, self::EMAIL_PRICE_MINOR)) {
|
||||
$this->lastError = 'Insufficient Ladill wallet balance. Add funds in Billing and try again.';
|
||||
$apiKey = $credential->birdApiKey();
|
||||
if (! $apiKey) {
|
||||
$this->lastError = 'Bird credentials could not be decrypted. Reconnect Bird in Integrations.';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$reference = 'events_email:'.Str::uuid();
|
||||
$sent = $this->customerEmail->send(
|
||||
$apiKey,
|
||||
(string) $credential->bird_from_email,
|
||||
$credential->bird_from_name,
|
||||
$to,
|
||||
$subject,
|
||||
$html,
|
||||
$text,
|
||||
);
|
||||
|
||||
try {
|
||||
Mail::html($html, function ($message) use ($to, $subject, $text, $replyToEmail, $replyToName): void {
|
||||
$message->to($to)
|
||||
->subject($subject)
|
||||
->from(
|
||||
(string) config('mail.from.address'),
|
||||
(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.';
|
||||
if (! $sent) {
|
||||
$this->lastError = $this->customerEmail->lastError() ?: 'The email could not be sent.';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user