Como consumir API de CPF em Tray Commerce via API de integração

Saiba como consumir a API de CPF da CPFHub em lojas Tray Commerce usando a API de integração para validação no checkout.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como consumir API de CPF em Tray Commerce via API de integração

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:

  1. Acesse a página de cadastro da sua loja Tray.
  2. Digite um CPF no campo correspondente.
  3. Aguarde o feedback visual de validação.
  4. 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.

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