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 ~900ms — 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?
Não. Quando o plano atinge o limite mensal, a API continua respondendo normalmente e cobra R$0,15 por consulta adicional — sem bloquear o serviço nem retornar código de erro 429. 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.
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 aproximadamente 900ms. 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
A API da CPFHub oferece tempo de resposta de aproximadamente 900ms, 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 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
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.



