• 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 Block Patterns API

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

Leveraging Google Analytics v4 REST API with WordPress Block Patterns for Enhanced E-commerce Insights

Integrating granular, real-time e-commerce data from Google Analytics v4 (GA4) directly into a WordPress dashboard offers significant advantages for technical managers and e-commerce founders. This allows for immediate performance monitoring without context switching. This guide details a robust, secure method for achieving this integration using GA4’s REST API within a custom WordPress plugin, specifically leveraging the Block Patterns API for flexible data visualization.

Prerequisites and Setup

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

  • A Google Cloud Project with the Google Analytics Data API enabled.
  • A service account with appropriate permissions (e.g., “Analytics Viewer” role) for your GA4 property. Download the JSON key file for this service account.
  • Composer installed for managing PHP dependencies.
  • A WordPress development environment.

Securing GA4 API Credentials

Storing service account credentials directly in your plugin’s code is a critical security vulnerability. Instead, we’ll use WordPress’s secure options. The most robust approach involves storing the service account JSON key securely on the server and referencing its path. For enhanced security, consider using environment variables or a dedicated secrets management system if your hosting environment supports it. For this example, we’ll assume the JSON key is placed in a secure, non-web-accessible directory within your WordPress installation or on the server.

Let’s define a constant in your plugin’s main file to point to the credential file. Ensure this file is outside the web root.

Plugin Setup and Service Account Configuration

Create a new WordPress plugin. For example, a plugin named `ga4-analytics-dashboard`. Inside your plugin directory, create a `ga4-analytics-dashboard.php` file.

Plugin Main File (`ga4-analytics-dashboard.php`)

Add the plugin header and define the path to your service account credentials. Replace /path/to/your/service-account-key.json with the actual, secure path.

<?php
/**
 * Plugin Name: GA4 Analytics Dashboard
 * Description: Integrates GA4 data into WordPress using REST API and Block Patterns.
 * Version: 1.0.0
 * Author: Your Name
 * Author URI: https://yourwebsite.com
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// Define the path to your Google Cloud service account key file.
// IMPORTANT: Ensure this file is NOT publicly accessible via the web.
// Consider using environment variables or a more secure method for production.
define( 'GA4_SERVICE_ACCOUNT_KEY_PATH', '/path/to/your/service-account-key.json' );
define( 'GA4_PROPERTY_ID', 'YOUR_GA4_PROPERTY_ID' ); // e.g., '123456789'

// Include necessary files and hooks.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-ga4-api-client.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-ga4-dashboard-block.php';

// Initialize the plugin.
function ga4_dashboard_init() {
    GA4_Dashboard_Block::register();
}
add_action( 'init', 'ga4_dashboard_init' );

// Activation hook for initial setup if needed.
register_activation_hook( __FILE__, 'ga4_dashboard_activate' );
function ga4_dashboard_activate() {
    // Optional: Add any initial setup tasks here.
}

// Deactivation hook for cleanup.
register_deactivation_hook( __FILE__, 'ga4_dashboard_deactivate' );
function ga4_dashboard_deactivate() {
    // Optional: Add any cleanup tasks here.
}
?>

Implementing the GA4 API Client

We’ll create a class to handle the authentication and requests to the GA4 Data API. This class will use the Google Cloud PHP client library. First, install it via Composer:

cd /path/to/your/wordpress/wp-content/plugins/ga4-analytics-dashboard
composer require google/analytics-data

Now, create the `includes/class-ga4-api-client.php` file.

`includes/class-ga4-api-client.php`

<?php
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

use Google\Analytics\Data\V1beta\BetaClient;
use Google\Analytics\Data\V1beta\DateRange;
use Google\Analytics\Data\V1beta\Dimension;
use Google\Analytics\Data\V1beta\Metric;
use Google\Analytics\Data\V1beta\RunReportRequest;
use Google\Analytics\Data\V1beta\OrderBy;
use Google\Analytics\Data\V1beta\OrderBy\DimensionOrderBy;
use Google\Analytics\Data\V1beta\OrderBy\MetricOrderBy;
use Google\ApiCore\ApiException;

class GA4_API_Client {

    private $client;
    private $property_id;

    public function __construct() {
        if ( ! defined( 'GA4_SERVICE_ACCOUNT_KEY_PATH' ) || ! defined( 'GA4_PROPERTY_ID' ) ) {
            error_log( 'GA4 API Client: Service account path or Property ID not defined.' );
            return;
        }

        try {
            $this->client = new BetaClient( [
                'credentials' => GA4_SERVICE_ACCOUNT_KEY_PATH,
            ] );
            $this->property_id = 'properties/' . GA4_PROPERTY_ID;
        } catch ( \Exception $e ) {
            error_log( 'GA4 API Client Error: ' . $e->getMessage() );
            $this->client = null;
        }
    }

    /**
     * Checks if the client is initialized and ready.
     * @return bool
     */
    public function is_ready() {
        return $this->client !== null;
    }

    /**
     * Fetches a report from the GA4 Data API.
     *
     * @param array $dimensions Array of dimension names (e.g., ['date', 'country']).
     * @param array $metrics Array of metric names (e.g., ['activeUsers', 'sessions']).
     * @param array $date_ranges Array of date range configurations (e.g., [['startDate' => '2023-01-01', 'endDate' => 'today']]).
     * @param array $order_bys Array of order by configurations.
     * @param int $limit Number of rows to return.
     * @return array|null Report data or null on error.
     */
    public function get_report( $dimensions = [], $metrics = [], $date_ranges = [], $order_bys = [], $limit = 100 ) {
        if ( ! $this->is_ready() ) {
            return null;
        }

        try {
            $request = new RunReportRequest( [
                'property' => $this->property_id,
                'dimensions' => $this->build_dimensions( $dimensions ),
                'metrics' => $this->build_metrics( $metrics ),
                'date_ranges' => $this->build_date_ranges( $date_ranges ),
                'order_bys' => $this->build_order_bys( $order_bys ),
                'limit' => $limit,
            ] );

            $response = $this->client->runReport( $request );

            return $this->parse_report_response( $response );

        } catch ( ApiException $e ) {
            error_log( 'GA4 API Error: ' . $e->getMessage() );
            return null;
        } catch ( \Exception $e ) {
            error_log( 'GA4 API General Error: ' . $e->getMessage() );
            return null;
        }
    }

    /**
     * Builds Dimension objects from an array of names.
     * @param array $dimension_names
     * @return array
     */
    private function build_dimensions( $dimension_names ) {
        $dimensions = [];
        foreach ( $dimension_names as $name ) {
            $dimensions[] = new Dimension( [ 'name' => $name ] );
        }
        return $dimensions;
    }

    /**
     * Builds Metric objects from an array of names.
     * @param array $metric_names
     * @return array
     */
    private function build_metrics( $metric_names ) {
        $metrics = [];
        foreach ( $metric_names as $name ) {
            $metrics[] = new Metric( [ 'name' => $name ] );
        }
        return $metrics;
    }

    /**
     * Builds DateRange objects from an array of configurations.
     * @param array $date_range_configs
     * @return array
     */
    private function build_date_ranges( $date_range_configs ) {
        $date_ranges = [];
        foreach ( $date_range_configs as $config ) {
            $date_ranges[] = new DateRange( [
                'start_date' => $config['startDate'],
                'end_date' => $config['endDate'],
            ] );
        }
        return $date_ranges;
    }

    /**
     * Builds OrderBy objects from an array of configurations.
     * @param array $order_by_configs
     * @return array
     */
    private function build_order_bys( $order_by_configs ) {
        $order_bys = [];
        foreach ( $order_by_configs as $config ) {
            if ( isset( $config['dimension'] ) ) {
                $order_bys[] = new OrderBy( [
                    'dimension' => new DimensionOrderBy( [ 'dimension_name' => $config['dimension']['name'] ] ),
                    'desc' => $config['dimension']['desc'] ?? false,
                ] );
            } elseif ( isset( $config['metric'] ) ) {
                $order_bys[] = new OrderBy( [
                    'metric' => new MetricOrderBy( [ 'metric_name' => $config['metric']['name'] ] ),
                    'desc' => $config['metric']['desc'] ?? false,
                ] );
            }
        }
        return $order_bys;
    }

    /**
     * Parses the GA4 API response into a more usable array format.
     * @param \Google\Analytics\Data\V1beta\RunReportResponse $response
     * @return array
     */
    private function parse_report_response( $response ) {
        $parsed_data = [
            'rows' => [],
            'dimension_headers' => [],
            'metric_headers' => [],
            'totals' => [],
            'maximums' => [],
            'metadata' => $response->getMetadata()->toArray(),
        ];

        // Parse dimension headers
        foreach ( $response->getDimensionHeaders() as $header ) {
            $parsed_data['dimension_headers'][] = $header->getName();
        }

        // Parse metric headers
        foreach ( $response->getMetricHeaders() as $header ) {
            $parsed_data['metric_headers'][] = $header->getName();
        }

        // Parse rows
        foreach ( $response->getRows() as $row ) {
            $row_data = [];
            $dimensions = $row->getDimensionValues();
            $metrics = $row->getMetricValues();

            foreach ( $dimensions as $index => $dimension_value ) {
                $row_data[ $parsed_data['dimension_headers'][$index] ] = $dimension_value->getValue();
            }
            foreach ( $metrics as $index => $metric_value ) {
                $row_data[ $parsed_data['metric_headers'][$index] ] = $metric_value->getValue();
            }
            $parsed_data['rows'][] = $row_data;
        }

        // Parse totals
        foreach ( $response->getTotals() as $row ) {
            $row_data = [];
            $metrics = $row->getMetricValues();
            foreach ( $metrics as $index => $metric_value ) {
                $row_data[ $parsed_data['metric_headers'][$index] ] = $metric_value->getValue();
            }
            $parsed_data['totals'][] = $row_data;
        }

        // Parse maximums
        foreach ( $response->getMaximums() as $row ) {
            $row_data = [];
            $metrics = $row->getMetricValues();
            foreach ( $metrics as $index => $metric_value ) {
                $row_data[ $parsed_data['metric_headers'][$index] ] = $metric_value->getValue();
            }
            $parsed_data['maximums'][] = $row_data;
        }

        return $parsed_data;
    }
}
?>

Creating the WordPress Block for GA4 Data Display

We’ll use the Block Patterns API to create a reusable block that fetches and displays GA4 data. This involves registering a server-side rendered block. Create `includes/class-ga4-dashboard-block.php`.

`includes/class-ga4-dashboard-block.php`

<?php
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class GA4_Dashboard_Block {

    /**
     * Registers the server-side rendered block.
     */
    public static function register() {
        register_block_type( 'ga4-dashboard/analytics-overview', [
            'attributes' => [
                'title' => [
                    'type' => 'string',
                    'default' => 'GA4 Analytics Overview',
                ],
                'dimensions' => [
                    'type' => 'array',
                    'default' => ['date'],
                ],
                'metrics' => [
                    'type' => 'array',
                    'default' => ['activeUsers', 'sessions', 'totalRevenue'],
                ],
                'dateRange' => [
                    'type' => 'object',
                    'default' => ['startDate' => '7daysAgo', 'endDate' => 'today'],
                ],
                'limit' => [
                    'type' => 'integer',
                    'default' => 10,
                ],
            ],
            'render_callback' => [ self::class, 'render_block' ],
        ] );
    }

    /**
     * Renders the block content on the server.
     *
     * @param array $attributes Block attributes.
     * @return string HTML output.
     */
    public static function render_block( $attributes ) {
        $ga4_client = new GA4_API_Client();

        if ( ! $ga4_client->is_ready() ) {
            return '<p>Google Analytics API client is not configured correctly. Please check plugin settings and credentials.</p>';
        }

        $dimensions = $attributes['dimensions'] ?? ['date'];
        $metrics = $attributes['metrics'] ?? ['activeUsers', 'sessions'];
        $date_range_config = $attributes['dateRange'] ?? ['startDate' => '7daysAgo', 'endDate' => 'today'];
        $limit = $attributes['limit'] ?? 10;
        $title = $attributes['title'] ?? 'GA4 Analytics Overview';

        // Prepare date range for API
        $date_ranges = [
            [
                'startDate' => $date_range_config['startDate'],
                'endDate' => $date_range_config['endDate'],
            ],
        ];

        // Prepare order by for API (example: order by date descending)
        $order_bys = [];
        if ( in_array('date', $dimensions) ) {
            $order_bys[] = [
                'dimension' => ['name' => 'date', 'desc' => true],
            ];
        } elseif ( !empty($metrics) ) {
            // Default to ordering by the first metric descending if date is not a dimension
            $order_bys[] = [
                'metric' => ['name' => $metrics[0], 'desc' => true],
            ];
        }

        $report_data = $ga4_client->get_report(
            $dimensions,
            $metrics,
            $date_ranges,
            $order_bys,
            $limit
        );

        if ( ! $report_data || empty( $report_data['rows'] ) ) {
            return '<p>No GA4 data available for the selected criteria.</p>';
        }

        // Start output buffering
        ob_start();
        ?>
        <div class="ga4-analytics-block">
            <h3><?php echo esc_html( $title ); ?></h3>
            <table class="wp-block-table">
                <thead>
                    <tr>
                        <?php foreach ( $report_data['dimension_headers'] as $header ) : ?>
                            <th><?php echo esc_html( ucwords( str_replace('_', ' ', $header) ) ); ?></th>
                        <?php endforeach; ?>
                        <?php foreach ( $report_data['metric_headers'] as $header ) : ?>
                            <th><?php echo esc_html( ucwords( str_replace('_', ' ', $header) ) ); ?></th>
                        <?php endforeach; ?>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ( $report_data['rows'] as $row ) : ?>
                        <tr>
                            <?php foreach ( $report_data['dimension_headers'] as $header_key ) : ?>
                                <td><?php echo esc_html( $row[ $header_key ] ?? '' ); ?></td>
                            <?php endforeach; ?>
                            <?php foreach ( $report_data['metric_headers'] as $header_key ) : ?>
                                <td><?php echo esc_html( number_format( (float)($row[ $header_key ] ?? 0) ) ); ?></td>
                            <?php endforeach; ?>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
            <!-- Optional: Display totals or other metadata -->
            <?php if ( ! empty( $report_data['totals'] ) ) : ?>
                <p><strong>Totals:</strong>
                <?php
                $total_strings = [];
                foreach ( $report_data['totals'][0] as $metric_name => $value ) {
                    $total_strings[] = esc_html( ucwords( str_replace('_', ' ', $metric_name) ) ) . ': ' . esc_html( number_format( (float)$value ) );
                }
                echo implode( ', ', $total_strings );
                ?>
                </p>
            <?php endif; ?>
        </div>
        <?php
        // Return buffered content
        return ob_get_clean();
    }
}
?>

Registering the Block and Using it in the Editor

The `GA4_Dashboard_Block::register()` method is called on the `init` hook in the main plugin file. This makes the block available in the WordPress editor.

To use the block:

  • Go to a WordPress page or post.
  • Click the ‘+’ icon to add a new block.
  • Search for “GA4 Analytics Overview” (or the block name you registered).
  • Select the block.
  • In the block settings sidebar (Inspector), you can customize the Title, Dimensions, Metrics, Date Range, and Row Limit.

Example Configuration in Block Editor:

Title: Top Performing Products

Dimensions: `itemName`

Metrics: `itemRevenue`, `itemsViewed`, `itemsAddedToCart`

Date Range: Start Date: `2023-10-01`, End Date: `today`

Limit: `5`

Advanced Considerations and Security Best Practices

Error Handling and Logging

The provided code includes basic error logging using error_log(). For production environments, implement a more sophisticated logging mechanism. This could involve:

  • Using a dedicated logging library.
  • Sending errors to a centralized logging service (e.g., ELK stack, Sentry).
  • Implementing user-facing error messages that are informative but don’t expose sensitive details.

Credential Management

The `GA4_SERVICE_ACCOUNT_KEY_PATH` constant is a basic approach. For enhanced security:

  • Environment Variables: Store the path to the credentials or the credentials themselves in environment variables on your server. Access them using getenv('GA4_CREDENTIALS_PATH').
  • Secrets Management Systems: Utilize services like AWS Secrets Manager, Google Secret Manager, or HashiCorp Vault.
  • File Permissions: Ensure the service account key file has strict read permissions (e.g., `chmod 400`).

Rate Limiting and Quotas

The Google Analytics Data API has quotas. If you anticipate high traffic or frequent API calls, consider:

  • Caching: Implement caching for API responses. WordPress transients API is suitable for this. Cache responses based on the query parameters and a reasonable expiration time (e.g., 1 hour).
  • Background Processing: For non-critical data, use WP-Cron or a dedicated job queue to fetch data asynchronously.
  • API Usage Monitoring: Monitor your API usage in the Google Cloud Console.

Data Validation and Sanitization

Always validate and sanitize data received from external APIs before displaying or processing it. The example uses esc_html() and number_format() for output, which is a good start. Ensure that any user-configurable inputs (like dimensions and metrics) are validated against a known list of valid GA4 API parameters to prevent injection attacks or malformed requests.

User Roles and Permissions

Consider which user roles should have access to view this data. You can add checks within the `render_callback` or use WordPress capabilities to restrict access to the block itself.

Conclusion

By integrating the GA4 Data API with WordPress Block Patterns, you create a dynamic, secure, and flexible solution for surfacing critical e-commerce analytics directly within your content management system. This approach empowers technical teams to make data-driven decisions faster and more efficiently, ultimately contributing to better business outcomes.

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

  • How to design secure Algolia Search API webhook listeners using signature validation and payload queues
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with Shortcode API
  • Debugging and Resolving deep-seated hook priority conflicts in third-party Stripe Payment webhook connectors
  • Designing audit logs for enterprise WordPress setups tracking internal user modifications to user transaction ledgers
  • How to securely integrate OpenAI Completion API endpoints into WordPress custom plugins using WP HTTP API

Categories

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

Recent Posts

  • How to design secure Algolia Search API webhook listeners using signature validation and payload queues
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with Shortcode API
  • Debugging and Resolving deep-seated hook priority conflicts in third-party Stripe Payment webhook connectors

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • 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