How to securely integrate ActiveCampaign automation API endpoints into WordPress custom plugins using WordPress Settings API
Leveraging the WordPress Settings API for Secure ActiveCampaign API Key Management
Integrating third-party services like ActiveCampaign into WordPress often requires storing sensitive API credentials. The WordPress Settings API provides a robust and secure mechanism for managing these settings directly within the WordPress admin area. This approach avoids hardcoding keys in your plugin files, which is a critical security best practice. We’ll walk through creating a custom settings page to store your ActiveCampaign API key and account ID.
Registering Settings and Fields
The core of the Settings API lies in its registration functions. We need to register a settings group, a settings section, and individual fields for our API key and account ID. This is typically done within your plugin’s main PHP file or an included settings administration file, hooked into the admin_init action.
Plugin Setup and Hooking
Assume you have a basic plugin structure. We’ll create a function that handles all the settings registration.
`my-activecampaign-plugin.php` (Main Plugin File Snippet)
<?php
/*
Plugin Name: My ActiveCampaign Integration
Description: Integrates with ActiveCampaign using API keys stored securely.
Version: 1.0
Author: Your Name
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Include the settings API handler
require_once plugin_dir_path( __FILE__ ) . 'includes/settings-api.php';
// Add settings page to the admin menu
function my_ac_add_admin_menu() {
add_options_page(
'ActiveCampaign Integration Settings',
'ActiveCampaign',
'manage_options',
'my-activecampaign-settings',
'my_ac_options_page_html'
);
}
add_action( 'admin_menu', 'my_ac_add_admin_menu' );
// Function to render the settings page HTML
function my_ac_options_page_html() {
// Check user capabilities
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1></h1>
<form action="options.php" method="post">
</form>
</div>
`includes/settings-api.php` - The Core Settings Logic
This file will contain the registration of our settings, sections, and fields. We'll use register_setting, add_settings_section, and add_settings_field.
<?php
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Register settings, sections, and fields
function my_ac_settings_init() {
// Register a setting group
register_setting( 'my_ac_settings_group', 'my_ac_options', 'my_ac_options_sanitize' );
// Add a settings section
add_settings_section(
'my_ac_main_section', // ID
'ActiveCampaign API Credentials', // Title
'my_ac_main_section_callback', // Callback for section description
'my-activecampaign-settings' // Page slug
);
// Add API Key field
add_settings_field(
'my_ac_api_key', // ID
'API Key', // Title
'my_ac_api_key_callback', // Callback to render the field
'my-activecampaign-settings', // Page slug
'my_ac_main_section', // Section ID
array( 'label_for' => 'my_ac_api_key' ) // Arguments for the callback
);
// Add Account ID field
add_settings_field(
'my_ac_account_id', // ID
'Account ID', // Title
'my_ac_account_id_callback', // Callback to render the field
'my-activecampaign-settings', // Page slug
'my_ac_main_section', // Section ID
array( 'label_for' => 'my_ac_account_id' ) // Arguments for the callback
);
}
add_action( 'admin_init', 'my_ac_settings_init' );
// Callback for the main section description
function my_ac_main_section_callback() {
echo '<p>Enter your ActiveCampaign API Key and Account ID below. You can find these in your ActiveCampaign account settings under "Developer".</p>';
}
// Callback to render the API Key input field
function my_ac_api_key_callback() {
$options = get_option( 'my_ac_options' );
$api_key = isset( $options['api_key'] ) ? $options['api_key'] : '';
?>
<input type="text" id="my_ac_api_key" name="my_ac_options[api_key]" value="" class="regular-text" />
<p class="description">Your ActiveCampaign API Key.</p>
<?php
}
// Callback to render the Account ID input field
function my_ac_account_id_callback() {
$options = get_option( 'my_ac_options' );
$account_id = isset( $options['account_id'] ) ? $options['account_id'] : '';
?>
<input type="text" id="my_ac_account_id" name="my_ac_options[account_id]" value="" class="regular-text" />
<p class="description">Your ActiveCampaign Account ID.</p>
<?php
}
// Sanitize and validate input data
function my_ac_options_sanitize( $input ) {
$sanitized_input = array();
if ( isset( $input['api_key'] ) ) {
$sanitized_input['api_key'] = sanitize_text_field( $input['api_key'] );
// Basic validation: Ensure it's not empty and has a typical format (e.g., alphanumeric)
if ( ! empty( $sanitized_input['api_key'] ) && ! preg_match( '/^[a-zA-Z0-9]+$/', $sanitized_input['api_key'] ) ) {
add_settings_error( 'my_ac_messages', 'my_ac_api_key_invalid', 'API Key appears to be invalid. Please check the format.', 'error' );
// Optionally, unset the invalid value or keep it for user to correct
// unset( $sanitized_input['api_key'] );
}
}
if ( isset( $input['account_id'] ) ) {
$sanitized_input['account_id'] = absint( $input['account_id'] ); // Ensure it's a positive integer
if ( $sanitized_input['account_id'] === 0 && isset( $input['account_id'] ) && $input['account_id'] !== '' ) {
add_settings_error( 'my_ac_messages', 'my_ac_account_id_invalid', 'Account ID must be a valid number.', 'error' );
// unset( $sanitized_input['account_id'] );
}
}
// Retrieve existing options to preserve other settings if any
$existing_options = get_option( 'my_ac_options' );
// Merge sanitized input with existing options, prioritizing sanitized values
return array_merge( $existing_options ?: array(), $sanitized_input );
}
?>
In this code:
register_setting()registers the option groupmy_ac_settings_groupand the option namemy_ac_options. It also specifies a sanitization callbackmy_ac_options_sanitize.add_settings_section()creates a visual grouping for our fields on the settings page.add_settings_field()defines each input field, linking it to a callback function that renders the HTML for the input.- The callback functions (e.g.,
my_ac_api_key_callback) retrieve existing options usingget_option()and display them in the input fields.esc_attr()is used for security. - The
my_ac_options_sanitize()function is crucial. It receives the raw input from the form, cleans it using WordPress sanitization functions (sanitize_text_field,absint), and performs basic validation. It also usesadd_settings_error()to display user-friendly error messages if validation fails.
Retrieving Stored API Credentials
Once the settings are saved, you can retrieve them anywhere in your plugin using get_option(). It's best practice to retrieve them once and store them in a constant or a class property to avoid repeated database queries.
<?php
// In your plugin's main file or a dedicated API handler file
// Retrieve ActiveCampaign options
$ac_options = get_option( 'my_ac_options' );
$activecampaign_api_key = isset( $ac_options['api_key'] ) ? $ac_options['api_key'] : '';
$activecampaign_account_id = isset( $ac_options['account_id'] ) ? $ac_options['account_id'] : '';
// Define constants for easy access and security (optional but recommended)
if ( ! defined( 'ACTIVECAMPAIGN_API_KEY' ) && ! empty( $activecampaign_api_key ) ) {
define( 'ACTIVECAMPAIGN_API_KEY', $activecampaign_api_key );
}
if ( ! defined( 'ACTIVECAMPAIGN_ACCOUNT_ID' ) && ! empty( $activecampaign_account_id ) ) {
define( 'ACTIVECAMPAIGN_ACCOUNT_ID', $activecampaign_account_id );
}
// Example usage in an API call function
function call_activecampaign_api( $endpoint, $method = 'GET', $data = array() ) {
if ( ! defined( 'ACTIVECAMPAIGN_API_KEY' ) || ! defined( 'ACTIVECAMPAIGN_ACCOUNT_ID' ) ) {
error_log( 'ActiveCampaign API credentials not configured.' );
return false; // Or throw an exception
}
$url = "https://{ACTIVECAMPAIGN_ACCOUNT_ID}.api-us1.com/api/3/{$endpoint}"; // Adjust base URL if needed
$headers = array(
'Api-Token' => ACTIVECAMPAIGN_API_KEY,
'Content-Type' => 'application/json',
);
$args = array(
'method' => $method,
'headers' => $headers,
'body' => json_encode( $data ),
);
// Handle GET requests differently for parameters
if ( $method === 'GET' && ! empty( $data ) ) {
$url = add_query_arg( $data, $url );
unset( $args['body'] ); // GET requests don't have a body
}
$response = wp_remote_request( $url, $args );
if ( is_wp_error( $response ) ) {
error_log( 'ActiveCampaign API Error: ' . $response->get_error_message() );
return false;
}
$body = wp_remote_retrieve_body( $response );
$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code >= 400 ) {
error_log( "ActiveCampaign API Error ({$status_code}): " . $body );
return false;
}
return json_decode( $body, true );
}
// Example of calling the function
// $contacts = call_activecampaign_api( 'contacts', 'GET', array( 'limit' => 10 ) );
// if ( $contacts ) {
// print_r( $contacts );
// }
?>
The call_activecampaign_api function demonstrates how to use the retrieved credentials. It constructs the API URL, sets the necessary headers (including the Api-Token), and uses wp_remote_request for making the HTTP call. Error handling and response processing are included.
Security Considerations and Best Practices
- Never hardcode API keys: The Settings API approach prevents this.
- Sanitize and Validate all input: The
my_ac_options_sanitizefunction is critical. Always clean user-provided data before saving it to the database. - Use
esc_attr()andesc_html(): When displaying data back to the user (in forms or elsewhere), always escape it to prevent XSS attacks. - Restrict Access: The
manage_optionscapability check inmy_ac_add_admin_menuandmy_ac_options_page_htmlensures only administrators can access and modify these settings. - Use HTTPS: Ensure your WordPress site uses HTTPS to protect data in transit.
- Environment Variables (Advanced): For even greater security, especially in development or staging environments, consider using environment variables to store API keys and loading them into WordPress configuration. This is beyond the scope of the basic Settings API but is a valuable next step for production systems.
- Database Encryption: For highly sensitive data, consider database-level encryption, though this adds significant complexity.
By following these steps, you can securely integrate ActiveCampaign API endpoints into your WordPress custom plugins, providing a user-friendly interface for managing credentials while adhering to security best practices.