• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to securely integrate Google Analytics v4 REST endpoints into WordPress custom plugins using Filesystem API

How to securely integrate Google Analytics v4 REST endpoints into WordPress custom plugins using Filesystem API

Leveraging WordPress Filesystem API for Secure GA4 REST API Integration

Integrating Google Analytics 4 (GA4) data directly into a WordPress e-commerce platform offers significant advantages for real-time performance monitoring and personalized user experiences. While direct JavaScript tracking is standard, fetching aggregated or specific GA4 data via its REST API within a custom WordPress plugin requires careful handling of authentication and data storage. This document outlines a robust approach using WordPress’s native Filesystem API to securely store and manage API credentials and cached responses, minimizing direct database queries and enhancing plugin performance.

Prerequisites: GA4 API Access and WordPress Environment

Before diving into the code, ensure you have the following:

  • A Google Cloud Project with the Google Analytics Data API enabled.
  • A Service Account within your Google Cloud Project, with appropriate permissions (e.g., Viewer) granted to your GA4 property. Download the JSON key file for this service account.
  • A WordPress installation with a custom plugin structure.
  • Basic understanding of PHP, WordPress plugin development, and OAuth 2.0.

Secure Credential Management with WordPress Filesystem API

Storing sensitive API credentials (like the service account JSON key) directly in the database or as plain text within plugin files is a significant security risk. The WordPress Filesystem API provides a secure and standardized way to interact with the server’s file system, abstracting away the underlying filesystem method (e.g., direct, FTP, FTPS, SSH). We’ll use it to store the GA4 service account key in a protected location.

Step 1: Obtain WordPress Filesystem Access

Within your plugin’s main file or a dedicated utility class, you’ll need to instantiate the WP_Filesystem_Direct class or leverage the global $wp_filesystem object after it’s been initialized. For simplicity and direct server access, we’ll assume direct filesystem access is available. If not, WordPress will attempt to use other methods.

/**
 * Ensure WP_Filesystem is available.
 */
if ( ! function_exists( 'WP_Filesystem' ) ) {
    require_once ABSPATH . 'wp-admin/includes/file.php';
}

// Attempt to get filesystem credentials.
// For direct access, this might not be strictly necessary if WP_Filesystem() is called later.
// However, it's good practice to ensure it's available.
$creds = request_filesystem_credentials( admin_url() );

// If credentials are not available, we can't proceed.
if ( ! $creds ) {
    // Handle error: Cannot get filesystem credentials.
    // This might involve displaying an admin notice.
    return;
}

// If we have credentials, initialize the filesystem.
if ( ! WP_Filesystem( $creds ) ) {
    // Handle error: Filesystem initialization failed.
    return;
}

// Now $wp_filesystem is globally available and ready to use.
global $wp_filesystem;

Step 2: Define a Secure Storage Path

Choose a directory that is not publicly accessible via the web. A good practice is to place it within the WordPress core directories but in a non-web-root location, or a custom, secured directory. For this example, we’ll use a subdirectory within the WordPress uploads directory, but ensure it’s properly secured.

/**
 * Define the secure storage path for GA4 credentials.
 * We'll use a subdirectory within the uploads directory, which is generally
 * not web-accessible if configured correctly, but we'll add an index.php
 * for extra protection.
 */
$upload_dir = wp_upload_dir();
$ga4_credentials_dir = trailingslashit( $upload_dir['basedir'] ) . 'ga4-api-credentials/';

// Ensure the directory exists.
if ( ! $wp_filesystem->is_dir( $ga4_credentials_dir ) ) {
    // Use WP_Filesystem to create the directory.
    if ( ! $wp_filesystem->mkdir( $ga4_credentials_dir, true ) ) {
        // Handle error: Failed to create directory.
        return;
    }

    // Add an index.php file to prevent directory listing.
    $index_file_path = trailingslashit( $ga4_credentials_dir ) . 'index.php';
    if ( ! $wp_filesystem->exists( $index_file_path ) ) {
        $wp_filesystem->put_contents( $index_file_path, '<?php // Silence is golden. ?>' );
    }
}

Step 3: Store the Service Account Key

Copy the content of your downloaded service account JSON key file into a new file within the secure directory. It’s crucial to handle this file’s content securely during the process.

/**
 * Path to the service account JSON key file.
 */
$service_account_key_path = trailingslashit( $ga4_credentials_dir ) . 'service-account-key.json';

/**
 * The actual content of your service account JSON key.
 * In a real-world scenario, this would be securely obtained,
 * perhaps via an admin setting or a secure upload mechanism.
 * FOR DEMONSTRATION PURPOSES ONLY: Replace with your actual key content.
 */
$service_account_key_content = '{
  "type": "service_account",
  "project_id": "your-project-id",
  "private_key_id": "your-private-key-id",
  "private_key": "-----BEGIN PRIVATE KEY-----\\nYOUR_PRIVATE_KEY_HERE\\n-----END PRIVATE KEY-----\\n",
  "client_email": "your-service-account-email@your-project-id.iam.gserviceaccount.com",
  "client_id": "your-client-id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-service-account-email%40your-project-id.iam.gserviceaccount.com"
}';

// Write the key content to the file using WP_Filesystem.
if ( ! $wp_filesystem->put_contents( $service_account_key_path, $service_account_key_content ) ) {
    // Handle error: Failed to write service account key.
    return;
}

// Set appropriate file permissions if possible (e.g., 0600 for owner-only read/write).
// Note: This depends on server configuration and user permissions.
@chmod( $service_account_key_path, 0600 );

Interacting with the GA4 Data API

With credentials securely stored, we can now use a PHP library to authenticate with Google’s APIs and fetch data. The Google Client Library for PHP is the standard choice. We’ll need to install it via Composer.

Step 1: Install Google Client Library

Navigate to your plugin’s root directory in your terminal and run:

composer require google/apiclient:"^2.0"

This will create a vendor directory. Ensure your plugin’s autoloader is included.

// In your plugin's main file or an included file.
require_once plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';

use Google\Client;
use Google\Service\AnalyticsData;

/**
 * Authenticates with Google and returns the AnalyticsData service client.
 *
 * @return AnalyticsData|false The AnalyticsData service client or false on failure.
 */
function get_ga4_analytics_service() {
    global $wp_filesystem;

    // Ensure WP_Filesystem is initialized.
    if ( ! function_exists( 'WP_Filesystem' ) ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    $creds = request_filesystem_credentials( admin_url() );
    if ( ! $creds || ! WP_Filesystem( $creds ) ) {
        return false; // Filesystem not available.
    }
    global $wp_filesystem;

    $upload_dir = wp_upload_dir();
    $ga4_credentials_dir = trailingslashit( $upload_dir['basedir'] ) . 'ga4-api-credentials/';
    $service_account_key_path = trailingslashit( $ga4_credentials_dir ) . 'service-account-key.json';

    if ( ! $wp_filesystem->exists( $service_account_key_path ) ) {
        // Handle error: Service account key file not found.
        return false;
    }

    try {
        $client = new Client();
        $client->setApplicationName( 'WordPress GA4 Integration' );
        $client->setScopes( [ AnalyticsData::ANALYTICS_DATA_READONLY ] );
        $client->setAuthConfig( $service_account_key_path ); // Use the securely stored key.

        $analytics_data_service = new AnalyticsData( $client );

        return $analytics_data_service;

    } catch ( \Exception $e ) {
        // Log the error: Failed to authenticate with Google API.
        error_log( 'GA4 API Authentication Error: ' . $e->getMessage() );
        return false;
    }
}

Step 2: Fetch GA4 Data

Now, you can use the authenticated service to make API calls. For example, fetching daily active users.

/**
 * Fetches daily active users from GA4.
 *
 * @param string $property_id Your GA4 Property ID (e.g., "123456789").
 * @return array|false The response from the GA4 API or false on failure.
 */
function fetch_ga4_daily_active_users( $property_id ) {
    $analytics_service = get_ga4_analytics_service();

    if ( ! $analytics_service ) {
        return false; // Authentication failed.
    }

    try {
        $request = new \Google\Service\AnalyticsData\RunReportRequest();
        $request->setDateRanges( [
            'startDate' => '7daysAgo',
            'endDate'   => 'today',
        ] );
        $request->setMetrics( [
            [ 'name' => 'activeUsers' ],
        ] );
        // You can add dimensions here if needed, e.g., for daily breakdown.
        // $request->setDimensions([['name' => 'date']]);

        $response = $analytics_service->properties->runReport( $property_id, $request );

        // Process the response.
        $data = [];
        if ( $response->getRows() ) {
            foreach ( $response->getRows() as $row ) {
                $rowData = [];
                if ( $row->getDimensionValues() ) {
                    foreach ( $row->getDimensionValues() as $dimensionValue ) {
                        $rowData[] = $dimensionValue->getValue();
                    }
                }
                if ( $row->getMetricValues() ) {
                    foreach ( $row->getMetricValues() as $metricValue ) {
                        $rowData[] = $metricValue->getValue();
                    }
                }
                $data[] = $rowData;
            }
        }

        return $data;

    } catch ( \Exception $e ) {
        error_log( 'GA4 API Run Report Error: ' . $e->getMessage() );
        return false;
    }
}

// Example usage:
// $ga4_property_id = 'YOUR_GA4_PROPERTY_ID'; // Replace with your actual GA4 Property ID.
// $daily_users = fetch_ga4_daily_active_users( $ga4_property_id );
//
// if ( $daily_users ) {
//     // Process and display the data.
//     // print_r( $daily_users );
// } else {
//     // Handle error.
// }

Caching GA4 API Responses with Filesystem API

Frequent calls to the GA4 API can incur costs and hit rate limits. Caching API responses using the Filesystem API is an effective strategy. We can store the JSON response in a file and serve it directly if it’s still valid.

Step 1: Define Cache Directory and Expiration

/**
 * Defines the cache directory and expiration time.
 */
function get_ga4_cache_settings() {
    $upload_dir = wp_upload_dir();
    $cache_dir = trailingslashit( $upload_dir['basedir'] ) . 'ga4-api-cache/';

    // Ensure cache directory exists.
    global $wp_filesystem;
    if ( ! $wp_filesystem->is_dir( $cache_dir ) ) {
        if ( ! $wp_filesystem->mkdir( $cache_dir, true ) ) {
            return false; // Failed to create cache directory.
        }
        $index_file_path = trailingslashit( $cache_dir ) . 'index.php';
        if ( ! $wp_filesystem->exists( $index_file_path ) ) {
            $wp_filesystem->put_contents( $index_file_path, '<?php // Silence is golden. ?>' );
        }
    }

    return [
        'dir'  => $cache_dir,
        'ttl'  => HOUR_IN_SECONDS, // Cache expiration time (e.g., 1 hour).
    ];
}

Step 2: Implement Cache Read/Write Logic

/**
 * Reads data from the GA4 API cache.
 *
 * @param string $cache_key A unique key for the cache entry.
 * @return mixed|false The cached data or false if not found or expired.
 */
function read_ga4_cache( $cache_key ) {
    $settings = get_ga4_cache_settings();
    if ( ! $settings ) {
        return false;
    }

    $cache_file_path = trailingslashit( $settings['dir'] ) . sanitize_key( $cache_key ) . '.json';
    global $wp_filesystem;

    if ( $wp_filesystem->exists( $cache_file_path ) ) {
        $file_content = $wp_filesystem->get_contents( $cache_file_path );
        $cache_data = json_decode( $file_content, true );

        if ( $cache_data && isset( $cache_data['timestamp'] ) && isset( $cache_data['data'] ) ) {
            if ( time() - $cache_data['timestamp'] < $settings['ttl'] ) {
                return $cache_data['data']; // Cache is valid.
            } else {
                // Cache expired, delete it.
                $wp_filesystem->delete( $cache_file_path );
            }
        }
    }

    return false; // Cache not found or expired.
}

/**
 * Writes data to the GA4 API cache.
 *
 * @param string $cache_key A unique key for the cache entry.
 * @param mixed  $data      The data to cache.
 * @return bool True on success, false on failure.
 */
function write_ga4_cache( $cache_key, $data ) {
    $settings = get_ga4_cache_settings();
    if ( ! $settings ) {
        return false;
    }

    $cache_file_path = trailingslashit( $settings['dir'] ) . sanitize_key( $cache_key ) . '.json';

    $cache_entry = [
        'timestamp' => time(),
        'data'      => $data,
    ];

    global $wp_filesystem;
    return $wp_filesystem->put_contents( $cache_file_path, json_encode( $cache_entry ) );
}

Step 3: Integrate Caching into Data Fetching

/**
 * Fetches daily active users from GA4, using cache if available.
 *
 * @param string $property_id Your GA4 Property ID.
 * @return array|false The GA4 data or false on failure.
 */
function get_ga4_daily_active_users_cached( $property_id ) {
    // Generate a unique cache key based on property ID and date range.
    $cache_key = 'ga4_daily_active_users_' . $property_id . '_' . date('Y-m-d');

    // Try to read from cache first.
    $cached_data = read_ga4_cache( $cache_key );
    if ( $cached_data !== false ) {
        return $cached_data;
    }

    // If not in cache, fetch from API.
    $api_data = fetch_ga4_daily_active_users( $property_id ); // Using the function from previous section.

    if ( $api_data !== false ) {
        // Write to cache if API call was successful.
        write_ga4_cache( $cache_key, $api_data );
        return $api_data;
    }

    return false; // API call failed.
}

// Example usage:
// $ga4_property_id = 'YOUR_GA4_PROPERTY_ID';
// $daily_users_cached = get_ga4_daily_active_users_cached( $ga4_property_id );
//
// if ( $daily_users_cached ) {
//     // Process and display the data.
//     // echo '<pre>'; print_r( $daily_users_cached ); echo '</pre>';
// } else {
//     // Handle error.
// }

Error Handling and Security Considerations

Permissions: Ensure the web server user has write permissions to the upload directory where you intend to store credentials and cache files. Incorrect permissions are a common point of failure.

Error Logging: Implement robust error logging using error_log() for any failures in filesystem operations, authentication, or API calls. This is crucial for debugging in a production environment.

Credential Security: While storing the key in a non-web-accessible directory is a good step, consider further hardening. If your server environment allows, use file permissions (e.g., 0600) to restrict access to the service account key file to the owner only. Avoid committing the actual key content directly into version control; use environment variables or a secure configuration management system if possible.

Input Sanitization: Always sanitize any user-provided input (like property IDs) before using them in API calls or cache keys to prevent injection vulnerabilities.

Rate Limiting: Be mindful of Google Analytics Data API quotas. The caching mechanism significantly helps, but for high-traffic sites, consider implementing client-side checks or more aggressive caching strategies.

Conclusion

By integrating the WordPress Filesystem API for managing GA4 service account credentials and caching API responses, you can build secure, performant, and reliable custom plugins. This approach abstracts away direct database interactions for sensitive data and API results, leveraging WordPress’s built-in capabilities for robust file management. Always prioritize security best practices, especially when handling authentication credentials.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using Vanilla JS Web Components
  • Implementing automated compliance reporting for custom member profile directories ledgers using dompdf library
  • How to construct high-throughput import engines for large affiliate click tracking logs sets using custom XML/JSON parsers
  • Step-by-Step Guide to building a custom dynamic lead collector block for Gutenberg using REST API custom routes
  • Advanced Diagnostics: Locating slow Model-View-Controller (MVC) modular query bottlenecks in WooCommerce custom checkout pipelines

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (647)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (855)
  • PHP (5)
  • PHP Development (38)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (627)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (285)
  • WordPress Theme Development (357)

Recent Posts

  • Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using Vanilla JS Web Components
  • Implementing automated compliance reporting for custom member profile directories ledgers using dompdf library
  • How to construct high-throughput import engines for large affiliate click tracking logs sets using custom XML/JSON parsers

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (855)
  • Debugging & Troubleshooting (647)
  • Security & Compliance (627)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala