Para consumir a API de CPF da CPFHub.io em lojas Tray Commerce, crie um proxy server em Node.js que recebe a requisição do tema da loja e consulta GET https://api.cpfhub.io/cpf/{CPF} com o header x-api-key. A Tray não permite chamadas server-side diretas a APIs externas dentro do tema, por isso o proxy protege a chave de API e serve como intermediário. A resposta chega em aproximadamente 900ms e pode ser usada para preencher campos do cadastro automaticamente.
Entendendo a arquitetura da Tray Commerce
A Tray Commerce opera com um sistema de temas baseados em HTML, CSS e JavaScript, com suporte a Liquid (template engine). A plataforma oferece:
- Temas customizáveis: acesso ao HTML/CSS/JS do frontend.
- API REST: endpoints para gerenciar pedidos, clientes, produtos e mais.
- Webhooks: notificações de eventos como criação de pedidos.
- Scripts customizados: possibilidade de inserir JavaScript nas páginas.
Para a validação de CPF, a abordagem mais direta é inserir JavaScript customizado no tema que consulta a API da CPFHub através de um proxy server.
Estratégia 1 -- Validação no frontend com proxy
Por que usar um proxy
A Tray Commerce não permite chamadas server-side diretas a APIs externas dentro do tema. Por isso, precisamos de um servidor intermediário (proxy) que recebe a requisição do frontend, consulta a API da CPFHub e retorna o resultado. Isso também protege a chave de API.
Criando o proxy com Node.js
// proxy-server/index.js
const express = require("express");
const cors = require("cors");
const app = express();
const API_KEY = process.env.CPFHUB_API_KEY;
const ALLOWED_ORIGINS = [
"https://www.sualoja.com.br",
"https://sualoja.com.br",
];
app.use(
cors({
origin: function (origin, callback) {
if (!origin || ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Origem não permitida"));
}
},
})
);
app.use(express.json());
// Rate limiting simples
const requestCounts = new Map();
function rateLimit(req, res, next) {
const ip = req.ip;
const now = Date.now();
const windowMs = 60000;
const maxRequests = 15;
if (!requestCounts.has(ip)) {
requestCounts.set(ip, []);
}
const requests = requestCounts
.get(ip)
.filter((time) => now - time < windowMs);
requests.push(now);
requestCounts.set(ip, requests);
if (requests.length > maxRequests) {
return res.status(429).json({
success: false,
message: "Muitas requisições. Aguarde um momento.",
});
}
next();
}
app.get("/api/validate-cpf/:cpf", rateLimit, async (req, res) => {
const cpf = req.params.cpf.replace(/\D/g, "");
if (cpf.length !== 11) {
return res.status(400).json({
success: false,
message: "CPF deve conter 11 dígitos.",
});
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
method: "GET",
headers: {
"x-api-key": API_KEY,
Accept: "application/json",
},
signal: controller.signal,
});
clearTimeout(timeoutId);
const data = await response.json();
res.json(data);
} catch (error) {
clearTimeout(timeoutId);
if (error.name === "AbortError") {
res.status(504).json({
success: false,
message: "Tempo de resposta excedido.",
});
} else {
res.status(500).json({
success: false,
message: "Erro ao consultar CPF.",
});
}
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Proxy CPFHub rodando na porta ${PORT}`);
});
Implementando o JavaScript no tema Tray
Inserindo o script
Na Tray Commerce, acesse Aparencia > Editor de Temas > Configurações > Scripts Customizados e adicione o seguinte código na seção de footer scripts:
<script>
(function() {
"use strict";
var PROXY_URL = "https://seu-proxy.herokuapp.com/api/validate-cpf";
var debounceTimer = null;
function init() {
var cpfField = document.querySelector(
'input[name="cpf"], input[name="CustomerCPF"], #customer_cpf'
);
if (!cpfField) return;
var feedbackEl = document.createElement("div");
feedbackEl.id = "cpfhub-feedback";
feedbackEl.style.cssText =
"font-size:13px;margin-top:4px;min-height:18px;transition:color 0.2s";
cpfField.parentNode.appendChild(feedbackEl);
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, feedbackEl);
} else {
feedbackEl.textContent = "";
feedbackEl.style.color = "";
}
}, 600);
});
}
function formatCpf(field) {
var v = field.value.replace(/\D/g, "").slice(0, 11);
if (v.length > 9)
v = v.replace(/(\d{3})(\d{3})(\d{3})(\d{1,2})/, "$1.$2.$3-$4");
else if (v.length > 6)
v = v.replace(/(\d{3})(\d{3})(\d{1,3})/, "$1.$2.$3");
else if (v.length > 3)
v = v.replace(/(\d{3})(\d{1,3})/, "$1.$2");
field.value = v;
}
function validateCpf(cpf, feedbackEl) {
feedbackEl.textContent = "Validando CPF...";
feedbackEl.style.color = "#6b7280";
var controller = new AbortController();
var timeoutId = setTimeout(function() { controller.abort(); }, 12000);
fetch(PROXY_URL + "/" + cpf, {
method: "GET",
signal: controller.signal
})
.then(function(res) {
clearTimeout(timeoutId);
return res.json();
})
.then(function(data) {
if (data.success && data.data) {
feedbackEl.textContent = "CPF valido - " + data.data.name;
feedbackEl.style.color = "#059669";
fillFields(data.data);
} else {
feedbackEl.textContent = data.message || "CPF nao encontrado.";
feedbackEl.style.color = "#dc2626";
}
})
.catch(function(err) {
clearTimeout(timeoutId);
feedbackEl.textContent = err.name === "AbortError"
? "Tempo excedido."
: "Erro na validacao.";
feedbackEl.style.color = "#dc2626";
});
}
function fillFields(data) {
var nameField = document.querySelector(
'input[name="name"], input[name="CustomerName"], #customer_name'
);
if (nameField && data.name) {
nameField.value = data.name;
nameField.dispatchEvent(new Event("change", { bubbles: true }));
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
</script>
Estratégia 2 -- Integração server-side com webhooks
Para validação no backend -- por exemplo, antes de confirmar um pedido --, use os webhooks da Tray combinados com um servidor externo. Consulte a documentação de webhooks da Tray para ver todos os eventos disponíveis e os campos retornados no payload.
// webhook-handler/index.js
const express = require("express");
const app = express();
app.use(express.json());
const API_KEY = process.env.CPFHUB_API_KEY;
const TRAY_API_URL = process.env.TRAY_API_URL;
const TRAY_ACCESS_TOKEN = process.env.TRAY_ACCESS_TOKEN;
app.post("/webhooks/tray/order-created", async (req, res) => {
const { order_id } = req.body;
try {
// Buscar dados do pedido na API Tray
const orderResponse = await fetch(
`${TRAY_API_URL}/orders/${order_id}?access_token=${TRAY_ACCESS_TOKEN}`,
{ signal: AbortSignal.timeout(10000) }
);
const orderData = await orderResponse.json();
const cpf = orderData.Order.Customer.cpf.replace(/\D/g, "");
// Validar CPF na API CPFHub
const cpfController = new AbortController();
const cpfTimeout = setTimeout(() => cpfController.abort(), 10000);
const cpfResponse = await fetch(
`https://api.cpfhub.io/cpf/${cpf}`,
{
headers: {
"x-api-key": API_KEY,
Accept: "application/json",
},
signal: cpfController.signal,
}
);
clearTimeout(cpfTimeout);
const cpfData = await cpfResponse.json();
if (!cpfData.success) {
// Marcar pedido para revisão manual
await fetch(
`${TRAY_API_URL}/orders/${order_id}?access_token=${TRAY_ACCESS_TOKEN}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
Order: {
status: "REVIEW",
observation: "CPF nao validado pela API CPFHub",
},
}),
signal: AbortSignal.timeout(10000),
}
);
}
res.status(200).json({ received: true });
} catch (error) {
console.error("Erro no webhook:", error.message);
res.status(500).json({ error: "Erro ao processar webhook" });
}
});
app.listen(3001);
Configurando webhooks na Tray
Registre o webhook via API da Tray:
curl -X POST "https://api.traycorp.com.br/webhooks" \
-H "Content-Type: application/json" \
-d '{
"Webhook": {
"url": "https://seu-servidor.com/webhooks/tray/order-created",
"event": "order_created",
"access_token": "SEU_TOKEN_TRAY"
}
}'
Deploy do proxy
Para colocar o proxy em produção, você pode usar serviços como Railway, Render ou Fly.io:
# Exemplo com Railway
npm init -y
npm install express cors
# Criar Procfile
echo "web: node index.js" > Procfile
# Deploy
railway up
Defina a variável de ambiente CPFHUB_API_KEY no painel do serviço de hospedagem.
Testando a integração
Após configurar o proxy e inserir o script no tema:
- Acesse a página de cadastro da sua loja Tray.
- Digite um CPF no campo correspondente.
- Aguarde o feedback visual de validação.
- Verifique se os campos de nome são preenchidos automaticamente.
Perguntas frequentes
Por que preciso de um proxy para validar CPF em uma loja Tray Commerce?
A Tray Commerce não permite que o tema faça chamadas server-side diretas a APIs externas, e expor a chave de API no JavaScript do frontend seria um risco de segurança. O proxy recebe a requisição do browser da loja, adiciona o header x-api-key no servidor e repassa a consulta à CPFHub.io. Isso mantém a chave protegida e o fluxo funcional.
Qual é a latência esperada ao validar CPF via proxy na Tray?
A API CPFHub.io responde em aproximadamente 900ms. Somado ao tempo do proxy, o ciclo completo fica em torno de 1-1,5 segundo. Configure o debounce no script do tema (o exemplo usa 600ms) para que a consulta só seja disparada quando o usuário terminar de digitar, evitando requisições desnecessárias.
O que acontece se o limite de consultas do plano gratuito for atingido?
O plano gratuito cobre 50 consultas por mês. Ao atingir esse limite, a API não bloqueia: cada consulta adicional é cobrada a R$0,15. Para lojas com volume maior, o plano Pro inclui 1.000 consultas mensais por R$149, com o mesmo custo por excedente.
Como lidar com CPFs inválidos ou pedidos suspeitos na integração com webhooks?
Quando o webhook recebe um pedido e a consulta à CPFHub.io retorna success: false, a recomendação é marcar o pedido com status de revisão manual em vez de cancelá-lo automaticamente. Isso preserva pedidos legítimos com CPFs temporariamente indisponíveis na base de dados e cria um fluxo de análise controlado pela equipe de operações.
Conclusão
A integração de validação de CPF em lojas Tray Commerce com a API do CPFHub.io
A API da CPFHub entrega respostas em cerca de 900ms, com uptime de 99,9% e conformidade total com a LGPD -- essencial para lojas que operam no mercado brasileiro.
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.



