• 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 Shopify headless API endpoints into WordPress custom plugins using WP HTTP API

How to securely integrate Shopify headless API endpoints into WordPress custom plugins using WP HTTP API

Securing Shopify API Credentials in WordPress

Integrating external APIs, especially e-commerce platforms like Shopify, into WordPress requires careful handling of sensitive credentials. For Shopify’s headless API, this typically involves API keys and access tokens. Storing these directly within your custom plugin’s code is a significant security risk. A more robust approach involves leveraging WordPress’s built-in options API for secure storage and retrieval.

We’ll use the `add_option()` and `get_option()` functions to manage these credentials. It’s crucial to ensure these options are not directly accessible via the WordPress admin interface without proper authentication and authorization. For production environments, consider using environment variables or a dedicated secrets management system, but for plugin development and simpler setups, the WordPress options API provides a good starting point.

Registering Settings for Shopify API Credentials

To manage your Shopify API credentials within the WordPress admin, you need to register settings. This involves using the Settings API to create a settings page, fields, and sections. This allows users (or administrators) to input and save their Shopify API key and password.

/**
 * Register Shopify API settings.
 */
function my_shopify_plugin_register_settings() {
    // Register settings group
    register_setting( 'my_shopify_plugin_options_group', 'my_shopify_api_key' );
    register_setting( 'my_shopify_plugin_options_group', 'my_shopify_api_password' );
    register_setting( 'my_shopify_plugin_options_group', 'my_shopify_store_url' );

    // Add settings section
    add_settings_section(
        'my_shopify_api_section',
        __( 'Shopify API Configuration', 'my-shopify-plugin' ),
        'my_shopify_api_section_callback',
        'my-shopify-plugin'
    );

    // Add settings fields
    add_settings_field(
        'shopify_api_key',
        __( 'Shopify API Key', 'my-shopify-plugin' ),
        'my_shopify_api_key_callback',
        'my-shopify-plugin',
        'my_shopify_api_section'
    );

    add_settings_field(
        'shopify_api_password',
        __( 'Shopify API Password', 'my-shopify-plugin' ),
        'my_shopify_api_password_callback',
        'my-shopify-plugin',
        'my_shopify_api_section'
    );

    add_settings_field(
        'shopify_store_url',
        __( 'Shopify Store URL', 'my-shopify-plugin' ),
        'my_shopify_store_url_callback',
        'my-shopify-plugin',
        'my_shopify_api_section'
    );
}
add_action( 'admin_init', 'my_shopify_plugin_register_settings' );

/**
 * Settings section callback.
 */
function my_shopify_api_section_callback() {
    echo '

' . __( 'Enter your Shopify API credentials below. These are required to fetch data from your Shopify store.', 'my-shopify-plugin' ) . '

'; } /** * Shopify API Key field callback. */ function my_shopify_api_key_callback() { $api_key = get_option( 'my_shopify_api_key' ); ?>

Creating the Settings Page

Now, we need to add a menu item in the WordPress admin area and hook into the settings API to display the form.

/**
 * Add settings page to the admin menu.
 */
function my_shopify_plugin_add_admin_menu() {
    add_options_page(
        __( 'Shopify API Settings', 'my-shopify-plugin' ),
        __( 'Shopify API', 'my-shopify-plugin' ),
        'manage_options',
        'my-shopify-plugin',
        'my_shopify_plugin_options_page_html'
    );
}
add_action( 'admin_menu', 'my_shopify_plugin_add_admin_menu' );

/**
 * Render the settings page HTML.
 */
function my_shopify_plugin_options_page_html() {
    // Check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    

Fetching Data from Shopify API

With credentials stored and retrievable, we can now use the WordPress HTTP API (`wp_remote_get`, `wp_remote_post`) to interact with Shopify's GraphQL Admin API. It's highly recommended to use the GraphQL API for efficiency and flexibility.

First, retrieve your stored credentials. Ensure you have a mechanism to handle cases where these options are not yet set.

/**
 * Get Shopify API credentials.
 *
 * @return array|false An array of credentials or false if not set.
 */
function get_shopify_credentials() {
    $api_key      = get_option( 'my_shopify_api_key' );
    $api_password = get_option( 'my_shopify_api_password' );
    $store_url    = get_option( 'my_shopify_store_url' );

    if ( empty( $api_key ) || empty( $api_password ) || empty( $store_url ) ) {
        return false; // Credentials not set
    }

    // Ensure store URL has a trailing slash for the API endpoint
    if ( substr( $store_url, -1 ) !== '/' ) {
        $store_url .= '/';
    }

    return array(
        'api_key'      => $api_key,
        'api_password' => $api_password,
        'store_url'    => $store_url,
    );
}

/**
 * Fetch products from Shopify using GraphQL API.
 *
 * @return array|WP_Error An array of products or a WP_Error object on failure.
 */
function fetch_shopify_products() {
    $credentials = get_shopify_credentials();

    if ( ! $credentials ) {
        return new WP_Error( 'shopify_credentials_missing', __( 'Shopify API credentials are not configured.', 'my-shopify-plugin' ) );
    }

    $store_url = $credentials['store_url'];
    $api_key   = $credentials['api_key'];
    $password  = $credentials['api_password'];

    // Shopify GraphQL Admin API endpoint
    $api_endpoint = esc_url_raw( $store_url . 'admin/api/2023-10/graphql.json' ); // Use a specific API version

    // GraphQL query to fetch first 10 products
    $graphql_query = '
        query {
          products(first: 10) {
            edges {
              node {
                id
                title
                handle
                descriptionHtml
                images(first: 1) {
                  edges {
                    node {
                      url
                      altText
                    }
                  }
                }
                variants(first: 1) {
                  edges {
                    node {
                      price
                      sku
                    }
                  }
                }
              }
            }
          }
        }
    ';

    $headers = array(
        'Content-Type'  => 'application/json',
        'X-Shopify-Access-Token' => $password, // For private apps or custom apps with admin API scopes
        // For public apps or custom apps with specific scopes, you might use a different auth mechanism.
        // For basic auth with API key and password (less common for GraphQL):
        // 'Authorization' => 'Basic ' . base64_encode( $api_key . ':' . $password ),
    );

    $body = json_encode( array( 'query' => $graphql_query ) );

    $response = wp_remote_post( $api_endpoint, array(
        'method'    => 'POST',
        'headers'   => $headers,
        'body'      => $body,
        'timeout'   => 30, // Adjust timeout as needed
        'sslverify' => true, // Always verify SSL certificates in production
    ) );

    if ( is_wp_error( $response ) ) {
        return $response; // Return WP_Error object
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    $response_body = wp_remote_retrieve_body( $response );
    $data          = json_decode( $response_body, true );

    if ( $response_code >= 200 && $response_code < 300 ) {
        if ( isset( $data['data']['products']['edges'] ) ) {
            return $data['data']['products']['edges'];
        } else {
            // Handle cases where the query was successful but returned no data or an unexpected structure
            return new WP_Error( 'shopify_data_format_error', __( 'Unexpected data format received from Shopify.', 'my-shopify-plugin' ), $data );
        }
    } else {
        // Handle API errors
        $error_message = isset( $data['errors'] ) ? implode( ', ', array_column( $data['errors'], 'message' ) ) : __( 'Unknown API error.', 'my-shopify-plugin' );
        return new WP_Error( 'shopify_api_error', sprintf( __( 'Shopify API Error (%d): %s', 'my-shopify-plugin' ), $response_code, $error_message ), $data );
    }
}

Displaying Shopify Product Data in WordPress

Once you have successfully fetched the product data, you can display it within your WordPress site. This could be on a custom page template, a shortcode, or a widget. Here's a basic example using a shortcode.

/**
 * Shortcode to display Shopify products.
 *
 * Usage: [shopify_products]
 */
function my_shopify_products_shortcode() {
    $products = fetch_shopify_products();

    if ( is_wp_error( $products ) ) {
        return '

' . esc_html( $products->get_error_message() ) . '

'; } if ( empty( $products ) ) { return '

' . __( 'No products found on your Shopify store.', 'my-shopify-plugin' ) . '

'; } ob_start(); ?>

<?php echo esc_attr( $product['images']['edges'][0]['node']['altText'] ); ?>

Price:

View Product

Error Handling and Security Best Practices

When integrating with external APIs, robust error handling is paramount. The `wp_remote_post` function can return `WP_Error` objects, which should always be checked. Additionally, ensure that API responses are properly sanitized and escaped before being displayed to prevent cross-site scripting (XSS) vulnerabilities.

For the Shopify API, pay close attention to the authentication method. The example uses the `X-Shopify-Access-Token` header, which is common for custom apps and private apps. Always refer to the latest Shopify API documentation for the most secure and current authentication practices.

Furthermore, consider rate limiting. Shopify's API has strict rate limits. Implement caching mechanisms for API requests and handle potential rate limit errors gracefully. The `wp_remote_post` function's `timeout` parameter should be set appropriately, and in production, you might want to implement retry logic with exponential backoff for transient API errors.

Finally, never expose your API secrets directly in client-side JavaScript. All API interactions that require sensitive credentials should be handled server-side within your WordPress plugin.

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

  • Optimizing p99 database query response latency in multi-site Model-View-Controller (MVC) modular custom tables
  • Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using Vue micro-frontends
  • How to design secure Salesforce CRM webhook listeners using signature validation and payload queues
  • How to securely integrate Stripe Payment webhook endpoints into WordPress custom plugins using WP HTTP API
  • How to securely integrate Stripe Payment webhook endpoints into WordPress custom plugins using REST API Controllers

Categories

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

Recent Posts

  • Optimizing p99 database query response latency in multi-site Model-View-Controller (MVC) modular custom tables
  • Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using Vue micro-frontends
  • How to design secure Salesforce CRM webhook listeners using signature validation and payload queues

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (870)
  • Debugging & Troubleshooting (653)
  • Security & Compliance (638)
  • 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