string, 'message' => string] (or `null` to skip). * * No-ops when ONESIGNAL_APP_ID/API_KEY are unset, when the notifiable user has * no `push_token`, or when toOneSignal() returns null. Each call is logged to * api_logs through ApiLogger. */ final class OneSignalChannel { public const string NAME = 'onesignal'; public function __construct( private readonly ApiLogger $apiLogger, ) {} public function send(mixed $notifiable, Notification $notification): void { $appId = config('services.onesignal.app_id'); $apiKey = config('services.onesignal.api_key'); if ($appId === null || $apiKey === null) { Log::info('OneSignalChannel: skipped — credentials not configured'); return; } $playerId = $notifiable->push_token ?? null; if ($playerId === null) { return; } $payload = method_exists($notification, 'toOneSignal') ? $notification->toOneSignal($notifiable) : null; if ($payload === null) { return; } $url = 'https://api.onesignal.com/notifications'; try { $this->apiLogger->send(self::NAME, 'POST', $url, fn () => Http::timeout(10) ->withToken($apiKey) ->acceptJson() ->post($url, [ 'app_id' => $appId, 'include_player_ids' => [$playerId], 'headings' => ['en' => $payload['heading'] ?? 'Fuel Alert'], 'contents' => ['en' => $payload['message'] ?? ''], ])); } catch (Throwable $e) { Log::error('OneSignalChannel: send failed', ['error' => $e->getMessage()]); } } }