NovoVerificação de identidade facial

Como consumir API de CPF em PrestaShop com módulos customizados

Guia completo para consumir a API de CPF da CPFHub em PrestaShop criando módulos customizados com validação no checkout.

Lucas Vieira
Lucas Vieira
··8 min de leitura
Como consumir API de CPF em PrestaShop com módulos customizados

Para consumir a API de CPF da CPFHub em PrestaShop, crie um módulo customizado que registra hooks no checkout, faz chamadas cURL para https://api.cpfhub.io/cpf/{CPF} com o header x-api-key e retorna o resultado via AJAX para o JavaScript do storefront. O PrestaShop não oferece validação de CPF nativamente, então essa abordagem por módulo é a maneira mais organizada de adicionar essa funcionalidade sem modificar o core da plataforma. A latência média da API é de ~150 ms — exibir um estado de carregamento durante a chamada melhora a experiência do usuário. Consulte a documentação oficial de módulos do PrestaShop para entender o ciclo de vida de hooks e controllers antes de começar.


Estrutura do módulo PrestaShop

Um módulo PrestaShop segue uma estrutura de diretórios bem definida. Crie a seguinte estrutura:

modules/
    cpfhubvalidator/
    cpfhubvalidator.php
    controllers/
    front/
    validation.php
    views/
    templates/
    hook/
    cpf_field.tpl
    js/
    cpfhub-validator.js
    css/
    cpfhub-validator.css

Arquivo principal do módulo

O arquivo principal registra hooks, instala configurações e define o comportamento do módulo:

<?php
// modules/cpfhubvalidator/cpfhubvalidator.php

if (!defined('_PS_VERSION_')) {
    exit;
}

class CpfHubValidator extends Module
{
    public function __construct()
    {
    $this->name = 'cpfhubvalidator';
    $this->tab = 'front_office_features';
    $this->version = '1.0.0';
    $this->author = 'Sua Empresa';
    $this->need_instance = 0;

    parent::__construct();

    $this->displayName = $this->l('CPFHub Validator');
    $this->description = $this->l('Validação de CPF em tempo real via API CPFHub.io');
    }

    public function install()
    {
    return parent::install()
    && $this->registerHook('displayCustomerAccountForm')
    && $this->registerHook('displayHeader')
    && $this->registerHook('actionValidateCustomerAddressForm')
    && Configuration::updateValue('CPFHUB_API_KEY', '')
    && Configuration::updateValue('CPFHUB_ENABLED', 1);
    }

    public function uninstall()
    {
    return parent::uninstall()
    && Configuration::deleteByName('CPFHUB_API_KEY')
    && Configuration::deleteByName('CPFHUB_ENABLED');
    }

    public function getContent()
    {
    $output = '';

    if (Tools::isSubmit('submitCpfHubSettings')) {
    $apiKey = Tools::getValue('CPFHUB_API_KEY');
    $enabled = (int) Tools::getValue('CPFHUB_ENABLED');

    Configuration::updateValue('CPFHUB_API_KEY', $apiKey);
    Configuration::updateValue('CPFHUB_ENABLED', $enabled);

    $output .= $this->displayConfirmation($this->l('Configurações salvas.'));
    }

    return $output . $this->renderForm();
    }

    private function renderForm()
    {
    $fields = [
    'form' => [
    'legend' => [
    'title' => $this->l('Configurações CPFHub'),
    ],
    'input' => [
    [
    'type' => 'text',
    'label' => $this->l('Chave de API'),
    'name' => 'CPFHUB_API_KEY',
    'required' => true,
    'desc' => $this->l('Obtenha em cpfhub.io'),
    ],
    [
    'type' => 'switch',
    'label' => $this->l('Ativar validação'),
    'name' => 'CPFHUB_ENABLED',
    'values' => [
    ['id' => 'active_on', 'value' => 1, 'label' => $this->l('Sim')],
    ['id' => 'active_off', 'value' => 0, 'label' => $this->l('Não')],
    ],
    ],
    ],
    'submit' => [
    'title' => $this->l('Salvar'),
    ],
    ],
    ];

    $helper = new HelperForm();
    $helper->submit_action = 'submitCpfHubSettings';
    $helper->fields_value = [
    'CPFHUB_API_KEY' => Configuration::get('CPFHUB_API_KEY'),
    'CPFHUB_ENABLED' => Configuration::get('CPFHUB_ENABLED'),
    ];

    return $helper->generateForm([$fields]);
    }

    public function hookDisplayHeader()
    {
    if (!Configuration::get('CPFHUB_ENABLED')) {
    return;
    }

    Media::addJsDef([
    'cpfhubValidationUrl' => $this->context->link->getModuleLink(
    'cpfhubvalidator',
    'validation'
    ),
    ]);

    $this->context->controller->addJS(
    $this->_path . 'views/js/cpfhub-validator.js'
    );
    $this->context->controller->addCSS(
    $this->_path . 'views/css/cpfhub-validator.css'
    );
    }

    public function hookDisplayCustomerAccountForm($params)
    {
    if (!Configuration::get('CPFHUB_ENABLED')) {
    return '';
    }

    return $this->display(__FILE__, 'views/templates/hook/cpf_field.tpl');
    }

    public function hookActionValidateCustomerAddressForm($params)
    {
    if (!Configuration::get('CPFHUB_ENABLED')) {
    return;
    }

    $cpf = preg_replace('/\D/', '', Tools::getValue('cpf'));

    if (empty($cpf) || strlen($cpf) !== 11) {
    $params['form']->getField('cpf')->addError(
    $this->l('Por favor, informe um CPF válido.')
    );
    }
    }
}

Controller de validação AJAX

Crie o front controller que recebe as requisições AJAX:

<?php
// modules/cpfhubvalidator/controllers/front/validation.php

class CpfHubValidatorValidationModuleFrontController extends ModuleFrontController
{
    public function initContent()
    {
    parent::initContent();

    if (!$this->isTokenValid()) {
    $this->ajaxResponse(false, 'Token inválido.');
    return;
    }

    $cpf = preg_replace('/\D/', '', Tools::getValue('cpf'));

    if (strlen($cpf) !== 11) {
    $this->ajaxResponse(false, 'CPF deve conter 11 dígitos.');
    return;
    }

    $result = $this->validateCpf($cpf);
    $this->ajaxResponse($result['success'], $result['message'], $result['data'] ?? []);
    }

    private function validateCpf($cpf)
    {
    $apiKey = Configuration::get('CPFHUB_API_KEY');
    $url = "https://api.cpfhub.io/cpf/{$cpf}";

    $ch = curl_init();
    curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
    "x-api-key: {$apiKey}",
    "Accept: application/json",
    ],
    CURLOPT_TIMEOUT => 10,
    CURLOPT_CONNECTTIMEOUT => 5,
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    curl_close($ch);

    if ($error) {
    return [
    'success' => false,
    'message' => 'Erro na comunicação com a API.',
    ];
    }

    $data = json_decode($response, true);

    if ($httpCode === 200 && !empty($data['success'])) {
    return [
    'success' => true,
    'message' => 'CPF válido.',
    'data' => $data['data'],
    ];
    }

    return [
    'success' => false,
    'message' => 'CPF não encontrado na base de dados.',
    ];
    }

    private function ajaxResponse($success, $message, $data = [])
    {
    header('Content-Type: application/json');
    die(json_encode([
    'success' => $success,
    'message' => $message,
    'data' => $data,
    ]));
    }
}

Template Smarty para o campo de CPF

{* modules/cpfhubvalidator/views/templates/hook/cpf_field.tpl *}
<div class="form-group cpfhub-field-wrapper">
    <label for="cpf" class="col-md-3 form-control-label required">
    CPF
    </label>
    <div class="col-md-6">
    <input
    type="text"
    id="cpf"
    name="cpf"
    class="form-control"
    placeholder="000.000.000-00"
    inputmode="numeric"
    maxlength="14"
    required
    />
    <div id="cpfhub-feedback" class="cpfhub-feedback"></div>
    </div>
</div>

JavaScript para validação frontend

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

    var debounceTimer = null;

    document.addEventListener("DOMContentLoaded", function () {
    var cpfField = document.getElementById("cpf");
    if (!cpfField) return;

    var feedback = document.getElementById("cpfhub-feedback");

    cpfField.addEventListener("input", function () {
    formatCpf(cpfField);

    if (debounceTimer) clearTimeout(debounceTimer);
    debounceTimer = setTimeout(function () {
    var cpf = cpfField.value.replace(/\D/g, "");
    if (cpf.length === 11) {
    validateCpf(cpf, feedback);
    } else {
    feedback.textContent = "";
    feedback.className = "cpfhub-feedback";
    }
    }, 600);
    });
    });

    function formatCpf(field) {
    var value = field.value.replace(/\D/g, "").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 validateCpf(cpf, feedback) {
    feedback.textContent = "Validando...";
    feedback.className = "cpfhub-feedback cpfhub-loading";

    var controller = new AbortController();
    var timeoutId = setTimeout(function () {
    controller.abort();
    }, 12000);

    var formData = new FormData();
    formData.append("cpf", cpf);
    formData.append("ajax", "1");
    formData.append("token", prestashop.static_token);

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

Estilos CSS do módulo

/* modules/cpfhubvalidator/views/css/cpfhub-validator.css */
.cpfhub-field-wrapper {
    margin-bottom: 1rem;
}

.cpfhub-feedback {
    font-size: 0.85rem;
    margin-top: 4px;
    min-height: 1.2em;
    transition: color 0.2s ease;
}

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

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

.cpfhub-error {
    color: #dc2626;
}

Testando o módulo

Após instalar o módulo pelo backoffice do PrestaShop em Modules > Module Manager, configure a chave de API e acesse a página de cadastro de clientes. O campo de CPF deve aparecer com validação automática ao digitar.

Para testar via linha de comando, use:

# Verificar se o módulo está instalado
php bin/console prestashop:module list | grep cpfhub

# Limpar cache após alterações
php bin/console cache:clear

Perguntas frequentes

Por que criar um módulo customizado em vez de editar o tema diretamente?

Modificar o tema do PrestaShop diretamente torna o código frágil: qualquer atualização de tema apaga as alterações. Um módulo customizado encapsula toda a lógica de validação, registra os hooks apropriados e pode ser instalado, desinstalado e atualizado de forma independente. Essa separação segue as boas práticas da documentação oficial do PrestaShop para desenvolvimento sustentável.

A API CPFHub.io retorna erro 429 quando o limite de consultas é atingido?

O teto por minuto responde HTTP 429 com Retry-After e não se confunde com a cota mensal. No Grátis, acabar os 50 créditos pausa as consultas com HTTP 403 ("Limite de créditos excedido"). Se o plano pago tiver excedente, o valor por crédito está na página de preços. O plano gratuito inclui 50 créditos por mês; o plano Pro oferece 1.000 créditos mensais por R$149.

Como proteger a chave de API no módulo PrestaShop?

Armazene a chave usando Configuration::updateValue('CPFHUB_API_KEY', $apiKey) e recupere com Configuration::get('CPFHUB_API_KEY') — isso usa a tabela de configuração do banco de dados, nunca exposta ao frontend. A chave é usada apenas no controller PHP server-side; o JavaScript do storefront recebe apenas a URL do endpoint interno do módulo, sem acesso direto à API externa. A ANPD orienta que credenciais de acesso a dados pessoais devem ser tratadas com controles de acesso adequados.

Qual o tempo de resposta esperado e como ajustar os timeouts do cURL?

A latência média da API CPFHub.io é de ~150 ms. O controller deste módulo define CURLOPT_TIMEOUT => 10 e CURLOPT_CONNECTTIMEOUT => 5, valores adequados para absorver variações de rede sem bloquear o processo PHP por tempo excessivo. No JavaScript, o AbortController com 12 segundos de timeout garante que o usuário receba uma mensagem de erro clara caso a conexão falhe.


Conclusão

Criar um módulo PrestaShop para validação de CPF com a API do CPFHub.io, você consegue consultar nome, gênero e data de nascimento a partir do CPF informado no checkout, com uma requisição GET simples autenticada pelo header x-api-key. O plano Grátis inclui 50 créditos por mês, sem cartão, o que basta para testar o módulo antes de levá-lo à loja em produção.

A API da CPFHub oferece tempo de resposta de ~150 ms, uptime de 99,9% e conformidade com a LGPD -- características essenciais para operações de e-commerce no Brasil.

Cadastre-se em cpfhub.io

CPFHub.io

Pronto para integrar a API?

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

WhatsApp