From 2a2303d1dccc120dd7aa5f6b6ade0f89e0064850 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 25 Nov 2025 07:42:23 +0000
Subject: [PATCH] =Feed block mostly good! Referrals look good to go. Ready for Madi and Heidi to approve

---
 inc/integrations/Integrations.php |   51 +++++++++++++++++++++++++++++++++++----------------
 1 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index e3ef859..71392bb 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -62,7 +62,7 @@
 	 */
 	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
@@ -258,10 +258,6 @@
 		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;
 			}
 		}
@@ -924,8 +920,11 @@
 		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);
@@ -944,7 +943,6 @@
 
 		return $result;
 	}
-
 	/**
 	 * Check if response contains an error
 	 * Override in child classes for service-specific error detection
@@ -1393,17 +1391,29 @@
 			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
 					}
 				}
 			}
@@ -1427,6 +1437,7 @@
 		// Switch context
 		$this->userID = $user_id;
 		$this->credentials = [];
+		$this->resetTokenRefreshFlag();  // ADD THIS LINE
 
 		$this->ensureInitialized();
 	}
@@ -1933,7 +1944,7 @@
 		$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;
@@ -1943,7 +1954,7 @@
 			$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'];
 			}
@@ -3073,7 +3084,7 @@
 					switch ($action) {
 						case 'save_credentials':
 							$title = $label;
-							$label = jvbIcon('save');
+							$label = jvbIcon('floppy-disk');
 							break;
 						case 'clear_credentials':
 							$title = $label;
@@ -3527,4 +3538,12 @@
 			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;
+	}
 }

--
Gitblit v1.10.0