Validação de CPF para plataformas de venda de passagens aéreas e rodoviárias

Saiba como integrar validação de CPF via API em plataformas de venda de passagens para cumprir exigências regulatórias e prevenir fraudes.

Redação CPFHub.io
Redação CPFHub.io
··9 min de leitura
Validação de CPF para plataformas de venda de passagens aéreas e rodoviárias

Plataformas de venda de passagens aéreas e rodoviárias são obrigadas a validar o CPF do passageiro para cumprir exigências da ANAC, ANTT e Receita Federal — sem isso, a emissão do bilhete e da nota fiscal fica comprometida. A CPFHub.io permite essa validação em ~900ms via API REST, retornando nome completo e data de nascimento para conferência automática dos dados, o que reduz erros no embarque e bloqueia fraudes com cartões roubados.

Introdução

A venda online de passagens aéreas e rodoviárias no Brasil movimenta bilhões de reais por ano. Diferentemente de outros segmentos do e-commerce, esse setor opera sob regulamentações específicas da ANAC e da ANTT que exigem a identificação do passageiro no momento da compra. O CPF é o principal identificador utilizado nesse processo -- e sua validação é obrigatória para garantir conformidade, prevenir fraudes e evitar problemas no embarque.


Exigências regulatórias

ANAC -- passagens aéreas

A Agência Nacional de Aviação Civil exige que o bilhete aéreo contenha o nome completo do passageiro conforme documento oficial. Embora o CPF não seja tecnicamente obrigatório no bilhete, ele é amplamente utilizado como identificador único para fins fiscais, programas de fidelidade e check-in online. Divergências entre o nome do passageiro e o nome no documento podem impedir o embarque.

ANTT -- passagens rodoviárias

A Agência Nacional de Transportes Terrestres, por meio da Resolução 4.770, exige que a venda de passagens rodoviárias interestaduais e internacionais inclua a identificação do passageiro. O CPF é o documento mais utilizado para esse fim, especialmente em vendas online onde o passageiro não apresenta documento físico no momento da compra.

Receita Federal

A emissão de nota fiscal na venda de passagens exige o CPF do comprador. Um CPF inválido ou inexistente impede a emissão fiscal correta, gerando problemas contábeis e regulatórios para a plataforma.


Cenários de fraude no setor

O setor de passagens enfrenta fraudes específicas que a validação de CPF ajuda a combater.

Compra com cartão roubado

O fraudador compra passagens com cartão de crédito roubado e as revende abaixo do preço de mercado. A validação de CPF permite verificar se o titular do cartão corresponde ao passageiro, adicionando uma camada de verificação.

Cambistas digitais

Intermediários compram passagens em promoções e as revendem com lucro. Embora nem sempre ilegal, essa prática pode ser controlada limitando o número de compras por CPF em determinado período.

Reservas fantasma

Bots criam reservas em massa para segurar assentos em voos promocionais, liberando-os apenas quando o prazo de pagamento está prestes a vencer. A exigência de CPF válido no momento da reserva dificulta essa prática.


Implementação em Python

O exemplo a seguir demonstra um sistema de venda de passagens com validação de CPF integrada.

import requests
import re
from datetime import datetime, timedelta
from typing import Optional
import json

CPFHUB_API_URL = "https://api.cpfhub.io/cpf"
CPFHUB_API_KEY = "SUA_CHAVE_DE_API"
REQUEST_TIMEOUT = 10 # segundos

def consultar_cpf(cpf: str) -> Optional[dict]:
    """Consulta CPF na API CPFHub.io."""
    cpf_limpo = re.sub(r"\D", "", cpf)

    try:
    response = requests.get(
    f"{CPFHUB_API_URL}/{cpf_limpo}",
    headers={
    "x-api-key": CPFHUB_API_KEY,
    "Accept": "application/json",
    },
    timeout=REQUEST_TIMEOUT,
    )
    response.raise_for_status()
    dados = response.json()

    if dados.get("success"):
    return dados["data"]
    return None

    except requests.exceptions.Timeout:
    raise Exception("Timeout na consulta de CPF")
    except requests.exceptions.HTTPError as e:
    status = e.response.status_code
    mensagens = {
    401: "Chave de API inválida",
    404: None, # CPF não encontrado
    }
    if status in mensagens:
    if mensagens[status] is None:
    return None
    raise Exception(mensagens[status])
    raise Exception(f"Erro HTTP {status}")
    except requests.exceptions.RequestException:
    raise Exception("Erro de conexão com a API")

def normalizar_nome(nome: str) -> str:
    """Remove acentos e normaliza o nome para comparação."""
    import unicodedata
    nome_normalizado = unicodedata.normalize("NFKD", nome)
    nome_sem_acento = nome_normalizado.encode("ASCII", "ignore").decode("ASCII")
    return nome_sem_acento.upper().strip()

def verificar_correspondencia_nome(
    nome_informado: str, nome_api: str, tolerancia: float = 0.8
) -> dict:
    """Verifica se o nome informado corresponde ao retornado pela API."""
    partes_informado = normalizar_nome(nome_informado).split()
    partes_api = normalizar_nome(nome_api).split()

    # Verifica primeiro e último nome
    primeiro_ok = partes_informado[0] == partes_api[0] if partes_informado and partes_api else False
    ultimo_ok = partes_informado[-1] == partes_api[-1] if partes_informado and partes_api else False

    # Calcula score de similaridade
    partes_em_comum = set(partes_informado) & set(partes_api)
    total_partes = max(len(partes_informado), len(partes_api))
    score = len(partes_em_comum) / total_partes if total_partes > 0 else 0

    return {
    "corresponde": primeiro_ok and ultimo_ok and score >= tolerancia,
    "score": round(score, 2),
    "primeiro_nome_ok": primeiro_ok,
    "ultimo_nome_ok": ultimo_ok,
    "nome_sugerido": nome_api,
    }

def validar_passageiro(
    cpf: str,
    nome_passageiro: str,
    data_viagem: str,
) -> dict:
    """Valida CPF e dados do passageiro para emissão de bilhete."""

    cpf_limpo = re.sub(r"\D", "", cpf)

    # Validação estrutural do CPF
    if len(cpf_limpo) != 11:
    return {"valido": False, "erro": "CPF deve ter 11 dígitos"}

    # Consulta à API
    try:
    dados = consultar_cpf(cpf_limpo)
    except Exception as e:
    return {"valido": False, "erro": str(e)}

    if not dados:
    return {"valido": False, "erro": "CPF não encontrado na base"}

    # Verificação de correspondência de nome
    verificacao_nome = verificar_correspondencia_nome(
    nome_passageiro, dados.get("name", "")
    )

    if not verificacao_nome["corresponde"]:
    return {
    "valido": False,
    "erro": "Nome do passageiro não corresponde ao CPF",
    "nome_no_cpf": dados.get("name"),
    "score_similaridade": verificacao_nome["score"],
    "sugestao": f"Verifique se o nome correto é: {dados.get('name')}",
    }

    # Verificação de idade (menores podem precisar de autorização)
    ano_nascimento = dados.get("year", 0)
    idade = datetime.now().year - ano_nascimento
    menor_de_idade = idade < 18

    return {
    "valido": True,
    "passageiro": {
    "cpf": cpf_limpo,
    "nome": dados.get("name"),
    "nomeUpper": dados.get("nameUpper"),
    "dataNascimento": dados.get("birthDate"),
    "genero": dados.get("gender"),
    "menorDeIdade": menor_de_idade,
    },
    "alertas": (
    ["Passageiro menor de idade -- autorização pode ser necessária"]
    if menor_de_idade
    else []
    ),
    }

def emitir_bilhete(
    cpf: str,
    nome_passageiro: str,
    origem: str,
    destino: str,
    data_viagem: str,
    tipo: str = "aereo",
) -> dict:
    """Emite bilhete após validação completa."""

    # Valida passageiro
    validacao = validar_passageiro(cpf, nome_passageiro, data_viagem)

    if not validacao["valido"]:
    return {"emitido": False, "erro": validacao["erro"]}

    passageiro = validacao["passageiro"]

    # Gera localizador
    import hashlib
    hash_base = f"{cpf}-{origem}-{destino}-{data_viagem}-{datetime.now().isoformat()}"
    localizador = hashlib.md5(hash_base.encode()).hexdigest()[:6].upper()

    bilhete = {
    "emitido": True,
    "localizador": localizador,
    "tipo": tipo,
    "passageiro": passageiro["nomeUpper"],
    "cpf": passageiro["cpf"][:3] + ".***.***-" + passageiro["cpf"][-2:],
    "origem": origem,
    "destino": destino,
    "dataViagem": data_viagem,
    "emitidoEm": datetime.now().strftime("%d/%m/%Y %H:%M"),
    "alertas": validacao.get("alertas", []),
    }

    return bilhete

# Exemplo de uso
if __name__ == "__main__":
    # Emitir passagem aérea
    resultado = emitir_bilhete(
    cpf="123.456.789-09",
    nome_passageiro="João da Silva",
    origem="GRU",
    destino="GIG",
    data_viagem="15/10/2025",
    tipo="aereo",
    )
    print(json.dumps(resultado, indent=2, ensure_ascii=False))

    # Emitir passagem rodoviária
    resultado_rod = emitir_bilhete(
    cpf="987.654.321-00",
    nome_passageiro="Maria Oliveira",
    origem="São Paulo - Tietê",
    destino="Rio de Janeiro - Novo Rio",
    data_viagem="20/10/2025",
    tipo="rodoviario",
    )
    print(json.dumps(resultado_rod, indent=2, ensure_ascii=False))

Controle de compras por CPF

Para combater cambistas e reservas fantasma, implemente limites de compra por CPF.

from collections import defaultdict
from datetime import datetime, timedelta

# Registro de compras por CPF
compras_por_cpf = defaultdict(list)

# Limites configuráveis
LIMITE_PASSAGENS_DIA = 5
LIMITE_PASSAGENS_MES = 20
LIMITE_MESMO_TRECHO_DIA = 2

def verificar_limites(cpf: str, origem: str, destino: str) -> dict:
    """Verifica se o CPF atingiu algum limite de compra."""
    cpf_limpo = re.sub(r"\D", "", cpf)
    agora = datetime.now()

    compras = compras_por_cpf.get(cpf_limpo, [])

    # Compras nas últimas 24 horas
    compras_24h = [c for c in compras if agora - c["data"] < timedelta(hours=24)]

    # Compras no mês atual
    compras_mes = [c for c in compras if c["data"].month == agora.month]

    # Mesmo trecho nas últimas 24 horas
    mesmo_trecho_24h = [
    c for c in compras_24h
    if c["origem"] == origem and c["destino"] == destino
    ]

    alertas = []

    if len(compras_24h) >= LIMITE_PASSAGENS_DIA:
    return {
    "permitido": False,
    "erro": f"Limite de {LIMITE_PASSAGENS_DIA} passagens por dia atingido",
    }

    if len(compras_mes) >= LIMITE_PASSAGENS_MES:
    return {
    "permitido": False,
    "erro": f"Limite de {LIMITE_PASSAGENS_MES} passagens por mês atingido",
    }

    if len(mesmo_trecho_24h) >= LIMITE_MESMO_TRECHO_DIA:
    return {
    "permitido": False,
    "erro": f"Limite de {LIMITE_MESMO_TRECHO_DIA} passagens para o mesmo trecho por dia",
    }

    if len(compras_24h) >= LIMITE_PASSAGENS_DIA - 1:
    alertas.append("Próximo do limite diário de compras")

    return {"permitido": True, "alertas": alertas}

Integração com companhias

As companhias aéreas e rodoviárias exigem que os dados do passageiro sejam consistentes em todas as etapas -- reserva, emissão, check-in e embarque. A API da CPFHub.io retorna exatamente os campos exigidos pelas companhias nos arquivos de manifesto de passageiros, garantindo consistência end-to-end sem retrabalho manual.

Os dados retornados pela API -- nome completo, data de nascimento e gênero -- são precisamente os campos exigidos pelas companhias nos arquivos de manifesto de passageiros.


Considerações de performance

No setor de passagens, a velocidade do checkout é crítica. Promoções de passagens aéreas esgotam em minutos, e qualquer latência adicional pode significar a perda da venda.

A API da CPFHub.io opera com latência média de ~900ms, o que é suficiente para validar o passageiro em segundo plano enquanto o usuário preenche os demais campos do formulário, sem impactar a experiência de compra.


Perguntas frequentes

O que é necessário para implementar validação de CPF neste contexto?

A validação de CPF exige uma chamada à API com o número do documento e a chave de autenticação. A CPFHub.io retorna o status do CPF, nome do titular e data de nascimento em ~900ms, permitindo a verificação em tempo real durante o cadastro ou transação.

A API CPFHub.io funciona para todos os volumes de consulta?

Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito — ideal para testes e projetos pequenos. Para volumes maiores, o plano Pro inclui 1.000 consultas mensais por R$149. Se o limite for ultrapassado, a API não bloqueia: cobra R$0,15 por consulta adicional.

Como garantir conformidade com a LGPD ao usar uma API de CPF?

Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não guarde o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A ANPD orienta que dados de identificação devem ser tratados com o princípio da necessidade.

Quanto tempo leva para integrar a API CPFHub.io?

A integração básica leva menos de 30 minutos: crie uma conta em cpfhub.io, gere a API key no painel e faça uma chamada GET para https://api.cpfhub.io/cpf/{CPF} com o header x-api-key. A documentação inclui exemplos em Python, Node.js, PHP, Java e outras linguagens.


Conclusão

A validação de CPF em plataformas de venda de passagens não é apenas uma boa prática -- é uma necessidade regulatória e operacional. Ela garante conformidade com as exigências da ANAC e ANTT, previne fraudes com cartões roubados, combate cambistas digitais e evita problemas no embarque por divergência de dados.

A integração com a CPFHub.io leva menos de 30 minutos e já inclui tratamento de erros, comparação de nomes e controle de limites por CPF — tudo o que uma plataforma de passagens precisa para operar com segurança e conformidade. Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e comece hoje mesmo.

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