diff --git a/app/Http/Controllers/Events/AttendeeController.php b/app/Http/Controllers/Events/AttendeeController.php index fe1d8c0..5dbed01 100644 --- a/app/Http/Controllers/Events/AttendeeController.php +++ b/app/Http/Controllers/Events/AttendeeController.php @@ -154,6 +154,9 @@ class AttendeeController extends Controller 'programme' => $programme, 'commsModes' => $commsModes, 'defaultCommsMode' => $commsModes[0] ?? EventCommsService::MODE_PROGRAMME, + 'messagingSettingsUrl' => function_exists('ladill_account_url') + ? ladill_account_url('/account/settings/messaging') + : (string) config('smtp.messaging_settings_url', 'https://account.ladill.com/account/settings/messaging'), ]); } diff --git a/app/Http/Controllers/Qr/AccountController.php b/app/Http/Controllers/Qr/AccountController.php index 88c4d32..65d949d 100644 --- a/app/Http/Controllers/Qr/AccountController.php +++ b/app/Http/Controllers/Qr/AccountController.php @@ -6,6 +6,8 @@ use App\Http\Controllers\Controller; use App\Models\PaymentGatewaySetting; use App\Models\QrSetting; use App\Services\Billing\BillingClient; +use App\Services\Billing\PlatformEmailClient; +use App\Services\Billing\PlatformSmsClient; use App\Services\Events\SubscriptionService; use App\Services\Payments\MerchantGatewayService; use App\Support\AccountBranding; @@ -86,6 +88,11 @@ class AccountController extends Controller 'frameStyles' => QrFrameStyleCatalog::visible(), 'gateway' => $this->gateway->settingFor($account), 'canUsePaymentGateway' => $this->subscriptions->canUsePaymentGateway($account), + 'messagingSettingsUrl' => function_exists('ladill_account_url') + ? ladill_account_url('/account/settings/messaging') + : (string) config('smtp.messaging_settings_url', 'https://account.ladill.com/account/settings/messaging'), + 'suiteMessagingReady' => app(PlatformEmailClient::class)->isConfigured() + || app(PlatformSmsClient::class)->isConfigured(), ]); } diff --git a/app/Http/Controllers/Qr/IntegrationsController.php b/app/Http/Controllers/Qr/IntegrationsController.php index 3159422..9cf8cbd 100644 --- a/app/Http/Controllers/Qr/IntegrationsController.php +++ b/app/Http/Controllers/Qr/IntegrationsController.php @@ -19,6 +19,9 @@ class IntegrationsController extends Controller return view('qr.account.integrations', [ 'account' => $account, 'credential' => $credentials->forOwner((string) $account->public_id), + 'messagingSettingsUrl' => function_exists('ladill_account_url') + ? ladill_account_url('/account/settings/messaging') + : (string) config('smtp.messaging_settings_url', 'https://account.ladill.com/account/settings/messaging'), ]); } diff --git a/app/Services/Billing/SmsService.php b/app/Services/Billing/SmsService.php index d7852fe..cde30e7 100644 --- a/app/Services/Billing/SmsService.php +++ b/app/Services/Billing/SmsService.php @@ -5,6 +5,10 @@ namespace App\Services\Billing; use App\Services\Messaging\CustomerSmsClient; use App\Services\Messaging\MessagingCredentialsService; +/** + * Attendee SMS: prefer platform suite messaging (zero-config), fall back to + * product SMS API keys when suite is unavailable. + */ class SmsService { private ?string $lastError = null; @@ -12,6 +16,7 @@ class SmsService public function __construct( private readonly CustomerSmsClient $customer, private readonly MessagingCredentialsService $credentials, + private readonly PlatformSmsClient $platformSms, ) {} public function lastError(): ?string @@ -19,6 +24,15 @@ class SmsService return $this->lastError; } + public function isConfiguredForOwner(string $ownerPublicId): bool + { + if ($this->platformSms->isConfigured()) { + return true; + } + + return $this->credentials->forOwner($ownerPublicId)->hasValidSms(); + } + public function send(string $ownerPublicId, string $to, string $message): bool { $this->lastError = null; @@ -37,15 +51,43 @@ class SmsService } $credential = $this->credentials->forOwner($ownerPublicId); + + if ($this->platformSms->isConfigured()) { + $sender = $credential->sms_sender_id ?: null; + $sent = $this->platformSms->send( + $ownerPublicId, + $phone, + $message, + $sender ? (string) $sender : null, + ); + if ($sent) { + return true; + } + + $suiteError = $this->platformSms->lastError(); + if ($this->tryCustomerSms($credential, $phone, $message)) { + return true; + } + + $this->lastError = $suiteError ?: $this->lastError; + + return false; + } + + return $this->tryCustomerSms($credential, $phone, $message); + } + + private function tryCustomerSms(object $credential, string $phone, string $message): bool + { if (! $credential->hasValidSms()) { - $this->lastError = 'Connect Ladill SMS in Integrations before sending attendee SMS.'; + $this->lastError = 'SMS is not available (platform SMS key missing and no product SMS credentials).'; return false; } $apiKey = $credential->smsApiKey(); if (! $apiKey) { - $this->lastError = 'SMS credentials could not be decrypted. Reconnect SMS in Integrations.'; + $this->lastError = 'SMS credentials could not be decrypted. Reconnect SMS under Advanced keys.'; return false; } diff --git a/app/Services/Events/EventCommsService.php b/app/Services/Events/EventCommsService.php index fbf8104..1e056a9 100644 --- a/app/Services/Events/EventCommsService.php +++ b/app/Services/Events/EventCommsService.php @@ -4,6 +4,8 @@ namespace App\Services\Events; use App\Models\QrCode; use App\Models\QrEventRegistration; +use App\Services\Billing\PlatformEmailClient; +use App\Services\Billing\PlatformSmsClient; use App\Services\Billing\SmsService; use App\Services\Messaging\MessagingCredentialsService; use Illuminate\Support\Collection; @@ -23,6 +25,8 @@ class EventCommsService private readonly SmsService $sms, private readonly MessagingCredentialsService $credentials, private readonly EventProgrammeService $programmes, + private readonly PlatformEmailClient $platformEmail, + private readonly PlatformSmsClient $platformSms, ) {} /** @return array{recipients: int, emails: int, sms: int, estimated_ghs: float, affordable: bool, integrations_error: ?string} */ @@ -188,12 +192,15 @@ class EventCommsService $joinUrl, ) !== ''; - if ($needsEmail && ! $credential->hasValidBird()) { - return 'Connect Ladill Bird in Integrations before sending attendee email.'; + $emailReady = $this->platformEmail->isConfigured() || $credential->hasValidBird(); + $smsReady = $this->platformSms->isConfigured() || $credential->hasValidSms(); + + if ($needsEmail && ! $emailReady) { + return 'Email is not available yet. Bind a mailbox under Account → Messaging once platform keys are configured, or connect Bird under Advanced keys.'; } - if ($needsSms && ! $credential->hasValidSms()) { - return 'Connect Ladill SMS in Integrations before sending attendee SMS.'; + if ($needsSms && ! $smsReady) { + return 'SMS is not available yet. Ensure platform SMS is configured on this Events instance, or connect a product SMS key under Advanced keys.'; } return null; diff --git a/app/Services/Events/EventSpeakerInviteService.php b/app/Services/Events/EventSpeakerInviteService.php index 717b8c3..1889c62 100644 --- a/app/Services/Events/EventSpeakerInviteService.php +++ b/app/Services/Events/EventSpeakerInviteService.php @@ -227,7 +227,7 @@ class EventSpeakerInviteService } if (! app(EventMailer::class)->isConfiguredForOwner($ownerRef)) { - return ['ok' => false, 'error' => 'Connect Ladill Bird in Integrations before sending speaker invitations.']; + return ['ok' => false, 'error' => 'Email is not available yet. Bind a mailbox under Account → Messaging once platform keys are configured, or connect Bird under Advanced keys.']; } $token = (string) ($speaker['invite_token'] ?? ''); diff --git a/resources/views/events/attendees.blade.php b/resources/views/events/attendees.blade.php index 0df6529..9dd256d 100644 --- a/resources/views/events/attendees.blade.php +++ b/resources/views/events/attendees.blade.php @@ -54,7 +54,12 @@

Message attendees

-

Send programme links, virtual join links, or both. Requires SMS and email credentials. Manage integrations

+

+ Send programme links, virtual join links, or both. Uses suite messaging by default (no API keys). + Account messaging settings + · + Advanced keys +