client->validateConfiguration(); } foreach (['daily_budget_usd', 'monthly_budget_usd', 'low_balance_usd', 'critical_balance_usd'] as $key) { if (Money::micros((string) config('serp.'.$key)) <= 0) { throw new \InvalidArgumentException('Invalid '.$key); } } return ['provider' => 'dataforseo', 'host' => 'api.dataforseo.com', 'tls' => true, 'credentials_configured' => $configured, 'operation_mode' => app(ActivationManager::class)->state()->value]; } public function remote(bool $fresh = false): ProviderHealth { return Cache::remember('serp:dataforseo:health', $fresh ? 0 : (int) config('serp.health_ttl_seconds'), function () { $data = $this->client->userData(); $status = $data['status_code'] ?? null; if (! is_int($status)) { throw new UnexpectedProviderResponseException('Health status missing.'); }if ($status !== 20000) { throw $this->errors->api($status); }$task = $data['tasks'][0] ?? null; if (! is_array($task) || ($task['status_code'] ?? null) !== 20000) { throw new UnexpectedProviderResponseException('Health task malformed.'); }$balance = $task['result'][0]['money']['balance'] ?? null; if (! is_int($balance) && ! is_float($balance) && ! is_string($balance)) { throw new UnexpectedProviderResponseException('Balance missing.'); }$health = new ProviderHealth('dataforseo', Money::micros($balance), 20000, now('UTC')); SerpOperationState::query()->updateOrCreate(['id' => 1], ['last_health_at' => $health->checkedAt, 'last_balance_micros' => $health->balanceMicros, 'last_provider_status_code' => 20000, 'last_provider_success_at' => $health->checkedAt, 'last_error_category' => null]); if ($health->balanceMicros < Money::micros((string) config('serp.critical_balance_usd'))) { $this->kill->activate('critical_low_balance'); } elseif ($health->balanceMicros < Money::micros((string) config('serp.low_balance_usd'))) { $this->audit->record('low_balance', ['cost_micros' => $health->balanceMicros]); } return $health; }); } }