config = $config ?? config('DigitMotor'); $this->tokenModel = new MotorTokenModel(); helper('api'); } /** * Return a valid Bearer access token, refreshing from Digit when needed. * * @throws DigitApiException */ public function getToken(bool $forceRefresh = false): string { if (!$forceRefresh) { $cached = $this->tokenModel->getByEnvironment($this->config->environment); if ($cached && !empty($cached['access_token']) && !empty($cached['expires_at'])) { $expiresAt = strtotime($cached['expires_at']); if ($expiresAt !== false && ($expiresAt - $this->config->tokenLeewaySec) > time()) { return $cached['access_token']; } } } return $this->fetchAndStoreToken(); } /** * Force refresh (e.g. after 401). * * @throws DigitApiException */ public function refreshToken(): string { return $this->getToken(true); } /** * @throws DigitApiException */ protected function fetchAndStoreToken(): string { if ($this->config->username === '' || $this->config->password === '') { throw new DigitApiException( 'Digit Motor credentials are not configured. Set DIGIT_MOTOR_USERNAME and DIGIT_MOTOR_PASSWORD in .env.', 'CONFIG', 0 ); } $url = $this->config->baseUrl . $this->config->authPath; $headers = ['Content-Type: application/json']; $body = [ 'username' => $this->config->username, 'password' => $this->config->password, ]; $response = call_third_party_api($url, 'POST', $headers, $body); $httpCode = (int) ($response['code'] ?? 0); $data = $response['data'] ?? []; if (is_string($data)) { $decoded = json_decode($data, true); $data = is_array($decoded) ? $decoded : []; } $accessToken = $data['access_token'] ?? $data['accessToken'] ?? $data['token'] ?? null; if (empty($accessToken) || empty($response['status'])) { $msg = null; if (is_array($data)) { $msg = $data['message'] ?? $data['error'] ?? $data['error_description'] ?? null; if (is_array($msg)) { $msg = json_encode($msg); } } if (!$msg && is_string($response['data'] ?? null) && stripos((string) $response['data'], '502') !== false) { $msg = 'Digit auth gateway is down (HTTP 502). Retry in a few minutes.'; } if (!$msg && $httpCode >= 500) { $msg = 'Digit auth service unavailable (HTTP ' . $httpCode . ').'; } if (!$msg) { $msg = 'Digit token generation failed.'; } throw new DigitApiException( $msg, $data['code'] ?? (string) $httpCode, $httpCode, is_array($data) ? $data : ['raw' => $response['data'] ?? null], null, $url, [ 'username' => $this->config->username, 'password' => '***REDACTED***', ] ); } $expiresIn = (int) ($data['expires_in'] ?? $data['expiresIn'] ?? 900); $refreshToken = $data['refresh_token'] ?? $data['refreshToken'] ?? null; $expiresAt = date('Y-m-d H:i:s', time() + max(60, $expiresIn)); $this->tokenModel->upsertToken( $this->config->environment, $accessToken, $refreshToken, $expiresAt ); return $accessToken; } }