Jake Vanderwerf
2025-11-25 2a2303d1dccc120dd7aa5f6b6ade0f89e0064850
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;
         }
      }
@@ -1395,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
               }
            }
         }
@@ -1429,6 +1437,7 @@
      // Switch context
      $this->userID = $user_id;
      $this->credentials = [];
      $this->resetTokenRefreshFlag();  // ADD THIS LINE
      $this->ensureInitialized();
   }
@@ -1935,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;
@@ -1945,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'];
         }
@@ -3529,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;
   }
}