| | |
| | | */ |
| | | protected array $credentials = []; // Service credentials (API keys, tokens, etc.) |
| | | protected ?int $userID = null; // User context for user-specific integrations |
| | | |
| | | private bool $token_refresh_attempted = false; // Circuit breaker for token refresh |
| | | |
| | | protected array $fields = []; // The fields to generate that become credentials |
| | | protected array $advanced = []; // The fields that are optional settings |
| | |
| | | if (!empty($this->credentials['expires_at'])) { |
| | | $expires_at = intval($this->credentials['expires_at']); |
| | | if ($expires_at <= time()) { |
| | | // Token expired, try to refresh |
| | | if (!empty($this->credentials['refresh_token'])) { |
| | | return $this->refreshOAuthToken(); |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | |
| | | bool $force = false |
| | | ): ?array |
| | | { |
| | | $cacheKey = $this->buildCacheKey('GET', $endpoint, $params); |
| | | $ttl = $this->cacheStrategy[$cacheStrategy] ?? $this->ttl; |
| | | $cacheKey = $this->buildCacheKey('GET', $endpoint, $params, $baseKey); |
| | | |
| | | $ttl = is_int($cacheStrategy) |
| | | ? max(0, $cacheStrategy) |
| | | : ($this->cacheStrategy[$cacheStrategy] ?? $this->ttl); |
| | | |
| | | if (!$force && $ttl > 0) { |
| | | $cached = $this->cache->get($cacheKey); |
| | |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Check if response contains an error |
| | | * Override in child classes for service-specific error detection |
| | |
| | | if ($this->isOAuthService && $this->hasOAuthCredentials()) { |
| | | // Check if token is expired first |
| | | if (!$this->isOAuthValid()) { |
| | | $this->logDebug('OAuth token expired, attempting refresh'); |
| | | if (!$this->refreshOAuthToken()) { |
| | | $this->logError('Failed to refresh expired OAuth token'); |
| | | // Only attempt refresh once per request |
| | | if (!$this->token_refresh_attempted) { |
| | | $this->token_refresh_attempted = true; |
| | | $this->logDebug('OAuth token expired, attempting refresh'); |
| | | |
| | | if (!$this->refreshOAuthToken()) { |
| | | $this->logError('Failed to refresh expired OAuth token - stopping execution'); |
| | | // Token refresh failed - DO NOT continue making API requests |
| | | return; |
| | | } |
| | | } else { |
| | | // Already attempted refresh in this request |
| | | $this->logDebug('Token refresh already attempted, skipping'); |
| | | return; |
| | | } |
| | | } |
| | | // Check if we should proactively refresh (before expiry) |
| | | elseif ($this->shouldRefreshToken()) { |
| | | elseif ($this->shouldRefreshToken() && !$this->token_refresh_attempted) { |
| | | $this->token_refresh_attempted = true; |
| | | $this->logDebug('OAuth token should be refreshed proactively'); |
| | | if (!$this->refreshOAuthToken()) { |
| | | $this->logError('Failed to proactively refresh OAuth token'); |
| | | // Not critical - token is still valid |
| | | // Not critical - token is still valid, so continue |
| | | } |
| | | } |
| | | } |
| | |
| | | // Switch context |
| | | $this->userID = $user_id; |
| | | $this->credentials = []; |
| | | $this->resetTokenRefreshFlag(); // ADD THIS LINE |
| | | |
| | | $this->ensureInitialized(); |
| | | } |
| | |
| | | $response = $this->makeOAuthRequest('POST', $this->oauth['token'], $request_data); |
| | | |
| | | if (is_wp_error($response)) { |
| | | $this->logError('Failed to refresh Square token', [ |
| | | $this->logError('Failed to refresh OAuth token for '.$this->service_name, [ |
| | | 'error' => $response->get_error_message() |
| | | ]); |
| | | return false; |
| | |
| | | $this->credentials['access_token'] = $response['access_token']; |
| | | $this->credentials['expires_at'] = time() + ($response['expires_in'] ?? 2592000); // 30 days |
| | | |
| | | // Note: Square returns the SAME refresh token |
| | | // Note: Some services return the SAME refresh token |
| | | if (isset($response['refresh_token'])) { |
| | | $this->credentials['refresh_token'] = $response['refresh_token']; |
| | | } |
| | |
| | | throw new Exception('Failed to save JPEG image'); |
| | | } |
| | | } |
| | | /** |
| | | * Reset token refresh attempt flag |
| | | * Called automatically when switching users |
| | | */ |
| | | protected function resetTokenRefreshFlag(): void |
| | | { |
| | | $this->token_refresh_attempted = false; |
| | | } |
| | | } |