How to securely integrate HubSpot Contacts endpoints into WordPress custom plugins using Shortcode API
Prerequisites and Setup
Before diving into the integration, ensure you have a working WordPress environment and a HubSpot account. You’ll need to generate a HubSpot API key. Navigate to your HubSpot account settings, then to “Integrations” > “Private Apps”. Create a new private app and grant it “Contacts” read and write permissions. Copy the generated API key; this will be your authentication credential.
For this integration, we’ll create a custom WordPress plugin. The plugin will leverage the WordPress Shortcode API to expose a shortcode that fetches and displays HubSpot contacts. This approach offers flexibility and allows content creators to easily embed contact lists within posts and pages without direct code manipulation.
Plugin Structure and Initialization
Create a new directory for your plugin, e.g., hubspot-contacts-shortcode, within your WordPress installation’s wp-content/plugins/ directory. Inside this directory, create a main plugin file, e.g., hubspot-contacts-shortcode.php.
<?php
/**
* Plugin Name: HubSpot Contacts Shortcode
* Description: Integrates HubSpot Contacts API with WordPress using a shortcode.
* Version: 1.0
* Author: Antigravity
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Define constants for API key and portal ID.
// It's highly recommended to store sensitive information like API keys securely,
// for example, in wp-config.php or via WordPress options, not directly in the plugin file.
// For demonstration purposes, we'll use constants here.
define( 'HUBSPOT_API_KEY', 'YOUR_HUBSPOT_PRIVATE_APP_API_KEY' ); // Replace with your actual API key
define( 'HUBSPOT_PORTAL_ID', 'YOUR_HUBSPOT_PORTAL_ID' ); // Replace with your actual Portal ID
// Include the main class file.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-hubspot-contacts-api.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-hubspot-contacts-shortcode.php';
// Initialize the shortcode class.
function initialize_hubspot_contacts_shortcode() {
new HubSpot_Contacts_Shortcode();
}
add_action( 'plugins_loaded', 'initialize_hubspot_contacts_shortcode' );
// Function to check if HubSpot API key and Portal ID are set.
function check_hubspot_settings() {
if ( ( HUBSPOT_API_KEY === 'YOUR_HUBSPOT_PRIVATE_APP_API_KEY' || empty( HUBSPOT_API_KEY ) ) ||
( HUBSPOT_PORTAL_ID === 'YOUR_HUBSPOT_PORTAL_ID' || empty( HUBSPOT_PORTAL_ID ) ) ) {
add_action( 'admin_notices', 'display_hubspot_settings_warning' );
}
}
add_action( 'admin_init', 'check_hubspot_settings' );
function display_hubspot_settings_warning() {
<<<HTML
<div class="notice notice-warning is-dismissible">
<p><strong>HubSpot Contacts Shortcode:</strong> Please configure your HubSpot API Key and Portal ID in the plugin file (or via secure methods) for the shortcode to function correctly.</p>
</div>
HTML;
}
Create an includes directory within your plugin folder. Inside includes, create two files: class-hubspot-contacts-api.php and class-hubspot-contacts-shortcode.php.
HubSpot API Interaction Class
This class will handle all direct communication with the HubSpot Contacts API. It will manage authentication, making HTTP requests, and parsing responses. We’ll use WordPress’s built-in HTTP API for making requests, which provides a robust and secure way to interact with external services.
<?php
// includes/class-hubspot-contacts-api.php
class HubSpot_Contacts_API {
private $api_key;
private $portal_id;
private $base_url = 'https://api.hubapi.com';
public function __construct() {
$this->api_key = HUBSPOT_API_KEY;
$this->portal_id = HUBSPOT_PORTAL_ID;
}
/**
* Fetches contacts from HubSpot.
*
* @param array $args Optional arguments for filtering or pagination.
* @return array|WP_Error An array of contacts or a WP_Error object on failure.
*/
public function get_contacts( $args = array() ) {
if ( empty( $this->api_key ) || empty( $this->portal_id ) ) {
return new WP_Error( 'hubspot_api_error', __( 'HubSpot API key or Portal ID is not configured.', 'hubspot-contacts-shortcode' ) );
}
$endpoint = "{$this->base_url}/crm/v3/objects/contacts";
$query_args = array(
'limit' => isset( $args['limit'] ) ? intval( $args['limit'] ) : 10,
'properties' => isset( $args['properties'] ) ? implode( ',', $args['properties'] ) : 'firstname,lastname,email',
// Add more query parameters as needed, e.g., 'filterGroups', 'sorts'
);
// Add filters if provided
if ( ! empty( $args['filters'] ) ) {
$query_args['filterGroups'] = json_encode( $args['filters'] );
}
$url = add_query_arg( $query_args, $endpoint );
$headers = array(
'Authorization' => "Bearer {$this->api_key}",
'Content-Type' => 'application/json',
);
$response = wp_remote_get( $url, array(
'headers' => $headers,
'timeout' => 30, // Adjust timeout as needed
) );
if ( is_wp_error( $response ) ) {
return $response;
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code >= 200 && $status_code < 300 ) {
return $data['results'] ?? array(); // Return contacts or empty array if no results
} else {
// Log the error for debugging purposes
error_log( "HubSpot API Error: Status Code {$status_code}, Response: " . print_r( $data, true ) );
return new WP_Error( 'hubspot_api_error', __( 'Failed to retrieve contacts from HubSpot.', 'hubspot-contacts-shortcode' ), $data );
}
}
/**
* Creates a new contact in HubSpot.
*
* @param array $properties Contact properties.
* @return array|WP_Error The created contact data or a WP_Error object on failure.
*/
public function create_contact( $properties ) {
if ( empty( $this->api_key ) || empty( $this->portal_id ) ) {
return new WP_Error( 'hubspot_api_error', __( 'HubSpot API key or Portal ID is not configured.', 'hubspot-contacts-shortcode' ) );
}
$endpoint = "{$this->base_url}/crm/v3/objects/contacts";
$headers = array(
'Authorization' => "Bearer {$this->api_key}",
'Content-Type' => 'application/json',
);
$body = json_encode( array(
'properties' => $properties,
) );
$response = wp_remote_post( $endpoint, array(
'headers' => $headers,
'body' => $body,
'timeout' => 30,
) );
if ( is_wp_error( $response ) ) {
return $response;
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code >= 200 && $status_code < 300 ) {
return $data;
} else {
error_log( "HubSpot API Error (Create Contact): Status Code {$status_code}, Response: " . print_r( $data, true ) );
return new WP_Error( 'hubspot_api_error', __( 'Failed to create contact in HubSpot.', 'hubspot-contacts-shortcode' ), $data );
}
}
// Add more methods for other HubSpot API operations (update, delete, etc.) as needed.
}
Shortcode API Integration Class
This class will register the shortcode and define its behavior. It will instantiate the HubSpot_Contacts_API class to fetch data and then format it for display within the WordPress content. We’ll also include basic error handling and a fallback message if data cannot be retrieved.
<?php
// includes/class-hubspot-contacts-shortcode.php
class HubSpot_Contacts_Shortcode {
private $api;
public function __construct() {
$this->api = new HubSpot_Contacts_API();
add_shortcode( 'hubspot_contacts', array( $this, 'render_shortcode' ) );
}
/**
* Renders the shortcode output.
*
* @param array $atts Shortcode attributes.
* @return string HTML output for the shortcode.
*/
public function render_shortcode( $atts ) {
// Set default attributes and merge with user-provided ones.
$atts = shortcode_atts( array(
'limit' => 10,
'properties' => 'firstname,lastname,email', // Comma-separated list of HubSpot contact properties
'filter_prop' => '', // Property to filter by
'filter_val' => '', // Value to filter by
), $atts, 'hubspot_contacts' );
// Prepare filter arguments for the API call.
$api_args = array(
'limit' => intval( $atts['limit'] ),
'properties' => array_map( 'trim', explode( ',', $atts['properties'] ) ),
);
if ( ! empty( $atts['filter_prop'] ) && ! empty( $atts['filter_val'] ) ) {
$api_args['filters'] = array(
array(
'propertyName' => sanitize_text_field( $atts['filter_prop'] ),
'operator' => 'EQ', // Exact match. Consider other operators like 'CONTAINS', 'GT', etc.
'value' => sanitize_text_field( $atts['filter_val'] ),
),
);
}
// Fetch contacts from HubSpot.
$contacts = $this->api->get_contacts( $api_args );
// Start output buffering.
ob_start();
if ( is_wp_error( $contacts ) ) {
// Display error message if API call failed.
echo '<p class="hubspot-error">' . esc_html( $contacts->get_error_message() ) . '</p>';
} elseif ( empty( $contacts ) ) {
// Display message if no contacts were found.
echo '<p class="hubspot-no-contacts">' . esc_html__( 'No contacts found.', 'hubspot-contacts-shortcode' ) . '</p>';
} else {
// Display the contacts in a table or list.
echo '<table class="hubspot-contacts-table">';
echo '<thead><tr>';
// Dynamically generate table headers based on requested properties.
$requested_properties = $api_args['properties'];
foreach ( $requested_properties as $prop ) {
echo '<th>' . esc_html( ucwords( str_replace( '_', ' ', $prop ) ) ) . '</th>';
}
echo '</tr></thead>';
echo '<tbody>';
foreach ( $contacts as $contact ) {
echo '<tr>';
foreach ( $requested_properties as $prop ) {
$value = $contact['properties'][ $prop ] ?? ' '; // Use for empty cells
echo '<td>' . esc_html( $value ) . '</td>';
}
echo '</tr>';
}
echo '</tbody></table>';
}
// Return the buffered output.
return ob_get_clean();
}
}
Usage and Security Considerations
To use the shortcode, simply add [hubspot_contacts] to your WordPress posts or pages. You can customize the output using attributes:
limit: Number of contacts to display (e.g.,[hubspot_contacts limit="5"]).properties: Comma-separated list of HubSpot contact properties to fetch and display (e.g.,[hubspot_contacts properties="firstname,lastname,phone,company"]). Ensure these properties exist in your HubSpot portal.filter_propandfilter_val: To filter contacts based on a specific property and value (e.g.,[hubspot_contacts filter_prop="lead_source" filter_val="Website"]).
Security Best Practices:
- API Key Management: Never hardcode your HubSpot API key directly in the plugin file for production environments. Instead, use WordPress’s secure options API or define it in
wp-config.php. For example, you could adddefine( 'HUBSPOT_API_KEY', getenv('HUBSPOT_API_KEY') );towp-config.phpand manage the environment variable. - Input Sanitization: Always sanitize user inputs, especially when they are used in API requests (e.g., filter values). The example uses
sanitize_text_fieldfor filter values. - Error Handling and Logging: Implement robust error handling. Log API errors to the PHP error log for debugging. Avoid exposing sensitive error details to end-users.
- Permissions: Ensure your HubSpot Private App has only the necessary permissions (e.g., read/write for contacts).
- Rate Limiting: Be mindful of HubSpot API rate limits. For high-traffic sites, consider caching API responses to reduce the number of requests.
- HTTPS: Always use HTTPS for your WordPress site and ensure all API calls are made over HTTPS.
Advanced Considerations: Caching and Performance
For performance optimization, especially on high-traffic websites, caching HubSpot API responses is crucial. You can implement a custom caching mechanism using WordPress Transients API. This will reduce the number of direct API calls and improve page load times.
<?php
// In class-hubspot-contacts-api.php, modify get_contacts method:
public function get_contacts( $args = array() ) {
// ... (previous checks) ...
// Generate a unique cache key based on arguments.
$cache_key = 'hubspot_contacts_' . md5( json_encode( $args ) );
$cached_data = get_transient( $cache_key );
if ( false !== $cached_data ) {
return $cached_data; // Return cached data if available.
}
// ... (API call logic as before) ...
if ( $status_code >= 200 && $status_code < 300 ) {
$results = $data['results'] ?? array();
// Cache the results for a specific duration (e.g., 1 hour).
set_transient( $cache_key, $results, HOUR_IN_SECONDS );
return $results;
} else {
// ... (error handling) ...
}
}
// In class-hubspot-contacts-shortcode.php, you might want to add an option to clear cache.
// For example, via an admin page or a specific shortcode attribute.
// For simplicity, this example doesn't include cache clearing UI.
Consider adding a shortcode attribute to control the cache duration or to force a cache refresh. For more complex scenarios, you might integrate with a dedicated caching plugin or a CDN.
Extending Functionality: Creating Contacts
You can extend this plugin to allow creating contacts via a form. This would involve adding a new shortcode, a form handler, and using the create_contact method from the HubSpot_Contacts_API class. Remember to implement thorough validation and security checks for any form submissions.
<?php
// Add this to class-hubspot-contacts-shortcode.php
// In the constructor:
add_shortcode( 'hubspot_create_contact', array( $this, 'render_create_contact_form' ) );
// New method to render the form:
public function render_create_contact_form( $atts ) {
// Process form submission if data is present
if ( isset( $_POST['hubspot_form_submit'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'hubspot_create_contact_nonce' ) ) {
$properties = array();
// Sanitize and collect properties from POST data
// Example: assuming 'firstname', 'lastname', 'email' are submitted
if ( isset( $_POST['firstname'] ) ) {
$properties['firstname'] = sanitize_text_field( $_POST['firstname'] );
}
if ( isset( $_POST['lastname'] ) ) {
$properties['lastname'] = sanitize_text_field( $_POST['lastname'] );
}
if ( isset( $_POST['email'] ) && is_email( $_POST['email'] ) ) {
$properties['email'] = sanitize_email( $_POST['email'] );
}
// Add more properties as needed
if ( ! empty( $properties ) ) {
$result = $this->api->create_contact( $properties );
if ( is_wp_error( $result ) ) {
// Display error message
echo '<p class="hubspot-error">' . esc_html( $result->get_error_message() ) . '</p>';
} else {
// Display success message
echo '<p class="hubspot-success">' . esc_html__( 'Contact created successfully!', 'hubspot-contacts-shortcode' ) . '</p>';
// Optionally clear cache if you are displaying contacts elsewhere
}
} else {
echo '<p class="hubspot-error">' . esc_html__( 'No valid contact data provided.', 'hubspot-contacts-shortcode' ) . '</p>';
}
}
// Start output buffering for the form
ob_start();
?>
<form method="post" action="" class="hubspot-create-contact-form">
<p>
<label for="firstname">:</label><br>
<input type="text" id="firstname" name="firstname" required>
</p>
<p>
<label for="lastname">:</label><>
<input type="text" id="lastname" name="lastname" required>
</p>
<p>
<label for="email">:</label><br>
<input type="email" id="email" name="email" required>
</p>
<?php wp_nonce_field( 'hubspot_create_contact_nonce' ); ?>
<input type="hidden" name="hubspot_form_submit" value="1">
<p>
<button type="submit"></button>
</p>
</form>
<?php
return ob_get_clean();
}
This provides a foundational structure for securely integrating HubSpot Contacts into your WordPress site using custom plugins and the Shortcode API, with considerations for performance and extensibility.