Jake Vanderwerf
2025-11-04 42fa8304ddb811b0f725f245130f70c0f5e86a6c
inc/integrations/Integrations.php
@@ -167,7 +167,7 @@
   {
      $this->cacheName = $this->cacheName ?: $this->service_name;
      $this->userID = $userID;
      $this->cache = new CacheManager('integrations_' . $this->cacheName, $this->ttl);
      $this->cache = CacheManager::for('integrations_' . $this->cacheName, $this->ttl);
      // Load error stats from cache
      $this->loadErrorStats();
@@ -846,14 +846,14 @@
      $this->logDebug("$method request to: $url: ".print_r($args, true));
      // Standard WordPress HTTP API
      // Use appropriate WordPress HTTP function
      // Make the request
      $response = match($method) {
         'GET' => wp_remote_get($url, $args),
         'POST' => wp_remote_post($url, $args),
         'PUT', 'PATCH', 'DELETE' => wp_remote_request($url, array_merge($args, ['method' => $method])),
         default => null
      };
      if (!$response) {
         $this->logError("Unsupported HTTP method $method for $this->service_name");
         return null;
@@ -867,9 +867,42 @@
      $response_code = wp_remote_retrieve_response_code($response);
      $body = wp_remote_retrieve_body($response);
      // Handle 401 - try to refresh token and retry once
      if ($response_code === 401 && $this->isOAuthService && !empty($this->credentials['refresh_token'])) {
         // Avoid infinite retry loop - only retry once
         static $retry_count = 0;
         if ($retry_count === 0) {
            $retry_count++;
            $this->logDebug('Got 401, attempting token refresh...');
            if ($this->refreshOAuthToken()) {
               $this->logDebug('Token refreshed successfully, retrying request...');
               // Rebuild request args with new token
               $args = $this->buildRequestArgs($method, $data, $options);
               // Retry the request
               $response = match($method) {
                  'GET' => wp_remote_get($url, $args),
                  'POST' => wp_remote_post($url, $args),
                  'PUT', 'PATCH', 'DELETE' => wp_remote_request($url, array_merge($args, ['method' => $method])),
                  default => null
               };
               if ($response && !is_wp_error($response)) {
                  $response_code = wp_remote_retrieve_response_code($response);
                  $body = wp_remote_retrieve_body($response);
               }
            }
            $retry_count = 0; // Reset for next request
         }
      }
      if ($response_code >= 400) {
         $this->handleApiError($response_code, $body, $endpoint);
      }
      $decoded = json_decode($body, true);
      if (json_last_error() !== JSON_ERROR_NONE) {
         return ['raw_response' => $body];
@@ -904,7 +937,8 @@
      $result = $this->makeRequest('GET', $endpoint, $params, $baseKey);
      if ($result && $ttl > 0) {
      // Only cache successful responses (not WP_Error and not error objects)
      if ($result && !is_wp_error($result) && !$this->isErrorResponse($result) && $ttl > 0) {
         $this->cache->set($cacheKey, $result, $ttl);
      }
@@ -912,6 +946,18 @@
   }
   /**
    * Check if response contains an error
    * Override in child classes for service-specific error detection
    */
   protected function isErrorResponse(array $response): bool
   {
      // Common error patterns across APIs
      return isset($response['error'])
         || isset($response['errors'])
         || isset($response['error_description']);
   }
   /**
    * POST request
    */
   protected function postRequest(string $endpoint, array $data = [], ?string $baseKey = null): ?array
@@ -1758,12 +1804,7 @@
         'redirect_uri' => $this->getRedirectUri()
      ];
      // Use a custom endpoint key for OAuth (not part of regular API)
      // We need to handle this specially since OAuth endpoints are different
      $oauth_endpoint = $this->oauth['token'];
      // Make the request using the centralized method
      // This automatically includes rate limiting and error handling
      $response = $this->makeOAuthRequest('POST', $oauth_endpoint, $request_data);
      if (is_wp_error($response)) {
@@ -1776,10 +1817,13 @@
      // Parse response
      if (isset($response['access_token'])) {
         $expires_in = $response['expires_in'] ?? 2592000; // 30 days default
         return [
            'access_token' => $response['access_token'],
            'refresh_token' => $response['refresh_token'] ?? '',
            'expires_in' => $response['expires_in'] ?? 2592000, // 30 days default
            'expires_in' => $expires_in,
            'expires_at' => time() + $expires_in, // Calculate expiry timestamp
            'token_type' => $response['token_type'] ?? 'Bearer',
            'merchant_id' => $response['merchant_id'] ?? '',
            'scope' => $response['scope'] ?? ''
@@ -3064,7 +3108,7 @@
      }
      $credentials = $this->getCredentials();
      $hasCredentials = $this->hasOAuthCredentials();
      $returnURL = (is_admin()) ? :get_the_permalink();
      $returnURL = is_admin() ? admin_url('admin.php?page=jvb-integrations') : (get_the_permalink() ?: home_url());
      ?>
      <details <?= $hasCredentials?' open':''?>>