Como integrar validação de CPF em WordPress com Elementor e AJAX

Tutorial completo para integrar validação de CPF em formulários WordPress com Elementor usando AJAX e a API da CPFHub.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como integrar validação de CPF em WordPress com Elementor e AJAX

Para integrar validação de CPF em WordPress com Elementor e AJAX, crie um plugin que registra um handler no admin-ajax.php, faz a chamada server-side para https://api.cpfhub.io/cpf/{CPF} com o header x-api-key e retorna o resultado ao JavaScript do formulário. Essa arquitetura mantém a chave de API protegida no servidor e entrega feedback visual ao usuário sem recarregar a página. A latência média da API é de ~900ms, então um indicador de carregamento melhora a percepção de velocidade. A documentação do WordPress para requisições HTTP explica como usar wp_remote_get de forma correta e segura.


Arquitetura da solução

A integração segue o padrão WordPress para requisições AJAX:

  1. O usuário digita o CPF no campo do formulário Elementor.
  2. Um script JavaScript captura o evento e envia uma requisição AJAX para o admin-ajax.php.
  3. O PHP no servidor recebe a requisição, consulta a API da CPFHub e retorna o resultado.
  4. O JavaScript atualiza o formulário com os dados recebidos.

Essa abordagem mantém a chave de API protegida no servidor, seguindo as boas práticas de segurança do WordPress.


Criando o plugin de validação

Crie um plugin WordPress dedicado para manter o código organizado e facilitar atualizações:

<?php
/**
    * Plugin Name: CPFHub Validator
    * Description: Validação de CPF em tempo real com a API CPFHub.io
    * Version: 1.0.0
    * Author: Sua Empresa
    */

defined('ABSPATH') || exit;

class CPFHub_Validator {

    private $api_key;

    public function __construct() {
    $this->api_key = get_option('cpfhub_api_key', '');

    add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
    add_action('wp_ajax_cpfhub_validate', [$this, 'ajax_validate']);
    add_action('wp_ajax_nopriv_cpfhub_validate', [$this, 'ajax_validate']);
    add_action('admin_menu', [$this, 'add_settings_page']);
    add_action('admin_init', [$this, 'register_settings']);
    }

    public function enqueue_scripts() {
    if ($this->should_load_scripts()) {
    wp_enqueue_script(
    'cpfhub-validator',
    plugin_dir_url(__FILE__) . 'js/cpfhub-validator.js',
    [],
    '1.0.0',
    true
    );

    wp_localize_script('cpfhub-validator', 'cpfhubConfig', [
    'ajaxUrl' => admin_url('admin-ajax.php'),
    'nonce' => wp_create_nonce('cpfhub_validate'),
    ]);

    wp_enqueue_style(
    'cpfhub-validator',
    plugin_dir_url(__FILE__) . 'css/cpfhub-validator.css',
    [],
    '1.0.0'
    );
    }
    }

    public function ajax_validate() {
    check_ajax_referer('cpfhub_validate', 'nonce');

    $cpf = preg_replace('/\D/', '', sanitize_text_field($_POST['cpf'] ?? ''));

    if (strlen($cpf) !== 11) {
    wp_send_json_error(['message' => 'CPF deve conter 11 dígitos.']);
    }

    $response = wp_remote_get(
    "https://api.cpfhub.io/cpf/{$cpf}",
    [
    'headers' => [
    'x-api-key' => $this->api_key,
    'Accept' => 'application/json',
    ],
    'timeout' => 10,
    ]
    );

    if (is_wp_error($response)) {
    wp_send_json_error([
    'message' => 'Erro na comunicação com a API. Tente novamente.'
    ]);
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    if (!empty($body['success']) && $body['success'] === true) {
    wp_send_json_success($body['data']);
    } else {
    wp_send_json_error([
    'message' => 'CPF não encontrado na base de dados.'
    ]);
    }
    }

    private function should_load_scripts() {
    return !empty($this->api_key);
    }

    public function add_settings_page() {
    add_options_page(
    'CPFHub Validator',
    'CPFHub Validator',
    'manage_options',
    'cpfhub-validator',
    [$this, 'render_settings_page']
    );
    }

    public function register_settings() {
    register_setting('cpfhub_settings', 'cpfhub_api_key');
    }

    public function render_settings_page() {
    ?>
    <div class="wrap">
    <h1>CPFHub Validator</h1>
    <form method="post" action="options.php">
    <?php settings_fields('cpfhub_settings'); ?>
    <table class="form-table">
    <tr>
    <th>Chave de API</th>
    <td>
    <input type="text"
    name="cpfhub_api_key"
    value="<?php echo esc_attr(get_option('cpfhub_api_key')); ?>"
    class="regular-text" />
    <p class="description">
    Obtenha sua chave em
    <a href="https://www.cpfhub.io/" target="_blank">cpfhub.io</a>
    </p>
    </td>
    </tr>
    </table>
    <?php submit_button(); ?>
    </form>
    </div>
    <?php
    }
}

new CPFHub_Validator();

Criando o JavaScript para AJAX

Crie o arquivo js/cpfhub-validator.js dentro do diretório do plugin:

// js/cpfhub-validator.js
(function () {
    "use strict";

    let debounceTimer = null;

    function init() {
    const cpfFields = document.querySelectorAll(
    'input[name="form_fields[cpf]"], input[data-cpfhub="true"], .cpfhub-field input'
    );

    cpfFields.forEach(function (field) {
    field.setAttribute("inputmode", "numeric");
    field.setAttribute("maxlength", "14");

    const feedbackEl = document.createElement("div");
    feedbackEl.className = "cpfhub-feedback";
    field.parentNode.appendChild(feedbackEl);

    field.addEventListener("input", function () {
    formatCpf(field);
    debouncedValidate(field, feedbackEl);
    });
    });
    }

    function formatCpf(field) {
    let value = field.value.replace(/\D/g, "");
    if (value.length > 11) value = value.slice(0, 11);

    if (value.length > 9) {
    value = value.replace(/(\d{3})(\d{3})(\d{3})(\d{1,2})/, "$1.$2.$3-$4");
    } else if (value.length > 6) {
    value = value.replace(/(\d{3})(\d{3})(\d{1,3})/, "$1.$2.$3");
    } else if (value.length > 3) {
    value = value.replace(/(\d{3})(\d{1,3})/, "$1.$2");
    }

    field.value = value;
    }

    function debouncedValidate(field, feedbackEl) {
    if (debounceTimer) clearTimeout(debounceTimer);

    debounceTimer = setTimeout(function () {
    const cpf = field.value.replace(/\D/g, "");

    if (cpf.length !== 11) {
    feedbackEl.textContent = "";
    feedbackEl.className = "cpfhub-feedback";
    return;
    }

    validateCpf(cpf, field, feedbackEl);
    }, 600);
    }

    function validateCpf(cpf, field, feedbackEl) {
    feedbackEl.textContent = "Validando CPF...";
    feedbackEl.className = "cpfhub-feedback cpfhub-loading";

    const formData = new FormData();
    formData.append("action", "cpfhub_validate");
    formData.append("nonce", cpfhubConfig.nonce);
    formData.append("cpf", cpf);

    const controller = new AbortController();
    const timeoutId = setTimeout(function () {
    controller.abort();
    }, 15000);

    fetch(cpfhubConfig.ajaxUrl, {
    method: "POST",
    body: formData,
    signal: controller.signal,
    })
    .then(function (response) {
    clearTimeout(timeoutId);
    return response.json();
    })
    .then(function (result) {
    if (result.success) {
    feedbackEl.textContent = "CPF válido - " + result.data.name;
    feedbackEl.className = "cpfhub-feedback cpfhub-success";
    fillFormFields(result.data);
    } else {
    feedbackEl.textContent =
    result.data?.message || "CPF não encontrado.";
    feedbackEl.className = "cpfhub-feedback cpfhub-error";
    }
    })
    .catch(function (error) {
    clearTimeout(timeoutId);
    if (error.name === "AbortError") {
    feedbackEl.textContent = "Tempo excedido. Tente novamente.";
    } else {
    feedbackEl.textContent = "Erro na validação. Tente novamente.";
    }
    feedbackEl.className = "cpfhub-feedback cpfhub-error";
    });
    }

    function fillFormFields(data) {
    const nameFields = document.querySelectorAll(
    'input[name="form_fields[name]"], input[data-cpfhub-fill="name"]'
    );
    nameFields.forEach(function (f) {
    f.value = data.name || "";
    });

    const birthFields = document.querySelectorAll(
    'input[name="form_fields[birth_date]"], input[data-cpfhub-fill="birthDate"]'
    );
    birthFields.forEach(function (f) {
    f.value = data.birthDate || "";
    });
    }

    if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
    } else {
    init();
    }
})();

Configurando o formulário no Elementor

Com Elementor Pro (Form Widget)

  1. Adicione um widget Form na página.
  2. Adicione um campo de texto com ID cpf.
  3. Adicione campos para name e birth_date que serão preenchidos automaticamente.
  4. No campo de CPF, adicione a classe CSS cpfhub-field em Advanced > CSS Classes.

Com Elementor Free (HTML Widget)

Se você usa a versão gratuita, insira o formulário via widget HTML:

<form id="cpfhub-registration" class="cpfhub-form">
    <div class="cpfhub-form-group">
    <label for="cpf-field">CPF</label>
    <input
    type="text"
    id="cpf-field"
    data-cpfhub="true"
    placeholder="000.000.000-00"
    inputmode="numeric"
    required
    />
    </div>

    <div class="cpfhub-form-group">
    <label for="name-field">Nome Completo</label>
    <input
    type="text"
    id="name-field"
    data-cpfhub-fill="name"
    readonly
    />
    </div>

    <div class="cpfhub-form-group">
    <label for="birth-field">Data de Nascimento</label>
    <input
    type="text"
    id="birth-field"
    data-cpfhub-fill="birthDate"
    readonly
    />
    </div>

    <button type="submit" class="cpfhub-submit">Cadastrar</button>
</form>

Estilos CSS para feedback visual

Crie o arquivo css/cpfhub-validator.css:

/* css/cpfhub-validator.css */
.cpfhub-feedback {
    margin-top: 4px;
    font-size: 13px;
    min-height: 20px;
    transition: all 0.3s ease;
}

.cpfhub-loading {
    color: #6b7280;
}

.cpfhub-loading::before {
    content: "";
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid #6b7280;
    border-top-color: transparent;
    border-radius: 50%;
    animation: cpfhub-spin 0.6s linear infinite;
    margin-right: 6px;
    vertical-align: middle;
}

.cpfhub-success {
    color: #059669;
    font-weight: 500;
}

.cpfhub-error {
    color: #dc2626;
}

@keyframes cpfhub-spin {
    to {
    transform: rotate(360deg);
    }
}

.cpfhub-form-group {
    margin-bottom: 16px;
}

.cpfhub-form-group label {
    display: block;
    margin-bottom: 4px;
    font-weight: 600;
}

.cpfhub-form-group input {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 16px;
}

.cpfhub-form-group input:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.cpfhub-form-group input[readonly] {
    background-color: #f9fafb;
    cursor: not-allowed;
}

Segurança e boas práticas

Proteção com nonce

O WordPress usa nonces para proteger requisições AJAX contra CSRF. Nosso plugin já implementa isso com wp_create_nonce e check_ajax_referer.

Rate limiting no servidor

Adicione proteção contra abusos:

public function ajax_validate() {
    check_ajax_referer('cpfhub_validate', 'nonce');

    $ip = $_SERVER['REMOTE_ADDR'];
    $transient_key = 'cpfhub_rate_' . md5($ip);
    $count = (int) get_transient($transient_key);

    if ($count >= 10) {
    wp_send_json_error([
    'message' => 'Muitas tentativas. Aguarde um minuto.'
    ]);
    }

    set_transient($transient_key, $count + 1, 60);

    // ... restante da validação
}

Perguntas frequentes

Como a chave de API da CPFHub.io é protegida nessa integração com WordPress?

A chave de API fica armazenada na tabela de opções do WordPress, acessada via get_option('cpfhub_api_key'), e nunca é exposta no frontend. Todo o tráfego com a API passa pelo admin-ajax.php no servidor, de modo que o JavaScript do navegador jamais vê a chave diretamente. Isso segue o padrão recomendado pela documentação de segurança do WordPress.

A API CPFHub.io bloqueia requisições quando o limite do plano é atingido?

Não. Quando o limite mensal de consultas é ultrapassado, a API continua respondendo normalmente e cobra R$0,15 por consulta adicional — sem retornar erro 429 nem interromper o serviço. O plano gratuito inclui 50 consultas por mês; o plano Pro oferece 1.000 consultas mensais por R$149, com o mesmo modelo de excedente.

O plugin funciona com o Elementor Free ou apenas com o Pro?

O plugin funciona com ambas as versões. Com o Elementor Pro, basta adicionar a classe cpfhub-field no campo de CPF do Form Widget. Com o Elementor Free, use o HTML Widget para inserir o formulário diretamente com os atributos data-cpfhub="true" nos campos adequados — o JavaScript do plugin detecta e inicializa automaticamente.

Qual o tempo de resposta esperado da API e como configurar o timeout corretamente?

A latência média da API CPFHub.io é de aproximadamente 900ms. Por isso, o plugin configura 'timeout' => 10 na chamada via wp_remote_get e 15 segundos de timeout no AbortController do JavaScript — margem suficiente para a grande maioria das conexões. O indicador de carregamento exibido durante a validação evita que o usuário submeta o formulário antes da resposta chegar.


Conclusão

Integrar validação de CPF em WordPress com Elementor e AJAX é uma solução prática e segura que beneficia sites de e-commerce, landing pages e portais de cadastro. O plugin que construímos mantém a chave de API protegida no servidor, usa nonces para segurança e oferece feedback visual instantâneo ao usuário.

A API do CPFHub.io

Cadastre-se em cpfhub.io

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

Sobre a redação

Redação CPFHub.io

Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.

WhatsAppFale conosco via WhatsApp