factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
Consulta veicular para despachantes — DOC.CAR by DOCTYPE.
Translation pending
DataTypeScript · CSSGitHub @doctype-startupOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
App web para consulta de veiculos
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
SaaS de consulta veicular para despachantes — placa, ficha técnica, FIPE, débitos e restrições em um só lugar. Um produto DOCTYPE. Abra http://localhost:3000.Read the full README ↗
SaaS de consulta veicular para despachantes — placa, ficha técnica, FIPE, débitos e restrições em um só lugar. Um produto DOCTYPE.
npm install
cp .env.example .env.local # preencha com suas chaves (veja "Backend" abaixo)
npm run dev
Abra http://localhost:3000.
app/login — login e cadastro de despachante (Supabase Auth)app/forgot-password — recuperação de senhaapp/assinar — página com os 3 planos (Essencial/Profissional/Escritório) + "Fale com a gente" pra alto volume; usuário sem assinatura ativa é redirecionado pra cáapp/dashboard — consulta de veículo por placa, com ficha técnica, FIPE, débitos e restrições (protegido: exige login + assinatura ativa); topbar mostra o saldo de consultas avançadas do períodoapp/dashboard/historico — histórico de placas consultadas, salvo no banco; tem botão "Limpar histórico" (apaga tudo, com confirmação)app/api/checkout — cria a sessão de Stripe Checkout para o usuário logadoapp/api/stripe/webhook — recebe eventos do Stripe e atualiza a assinatura no bancoapp/api/veiculo — consulta dados reais de veículo por placa (exige login + assinatura ativa)app/api/consulta-avancada — consulta avançada real de multas/roubo-furto/Renajud, sob demanda (exige login + assinatura ativa)app/admin — painel administrativo (exige is_admin); inclui "Criar acesso de teste", que convida um e-mail (novo ou já cadastrado) com cota fixa e prazo definido na hora, sem cobrançaapp/api/cron/expirar-testes — roda 1x/dia (agendado em vercel.json) e revoga sozinho os testes cujo prazo já passoulib/vehicle.ts — camada de dados mock (débitos — ainda simulado, exibido junto com o dado real)lib/dados-veiculo.ts — client do provedor de dados veiculares ativo (veja "Dados reais de veículo" abaixo)lib/dados-avancados.ts — client do provedor de consulta avançada (veja "Consulta avançada" abaixo)lib/plans.ts — os 3 planos (nome, cota mensal de consultas avançadas, preço) e o preço da consulta avulsa fora da cotalib/uso-avancada.ts — conta o uso do período atual e registra cada consulta avançada realizada (tabela avancada_usage)lib/infosimples.ts — client da Infosimples, hoje não usado (endpoint de veículo não autorizado na conta atual); mantido caso a consulta de multas ANTT/SIFAMA seja retomadalib/supabase/ — clients Supabase (browser, server, admin/service-role) e o proxy de sessãolib/stripe.ts — client Stripeproxy.ts — protege as rotas de /dashboard redirecionando para /login sem sessãosupabase/migrations/0001_init.sql — schema do banco (perfis, assinaturas, histórico)components/Guardiao.tsx — mascote oficial DOCTYPE, usado nos momentos de espera/aprovação/confirmação do apppublic/mascote/ — imagens do Guardião (veja "Identidade visual" abaixo)O app segue o manual de marca DOCTYPE: laranja #FF6400 como cor de ação, navy #06133F como base institucional, texto em #101419, fundo neutro #F7F8FA e cinza de apoio #8A93A2. Títulos usam Montserrat e o corpo de texto usa Inter (carregadas via next/font/google em app/layout.tsx), sem dependência externa de CSS.
O mascote Guardião — o "monitor" de todos os produtos DOCTYPE — aparece nos momentos-chave da jornada (tela de login, aguardando a primeira consulta, durante a verificação da placa, pedindo aprovação da assinatura, confirmando envio de e-mail). O componente components/Guardiao.tsx referencia os arquivos em public/mascote/:
guardiao-aguardando.png — login e estados vazios ("aguardamos seu retorno")guardiao-verificando.png — carregando o resultado de uma consultaguardiao-aprovacao.png — página de assinaturaguardiao-enviado.png — confirmações de e-mail (cadastro, recuperação de senha)guardiao-sucesso.png — reservado para estados de sucessoEsses arquivos ainda não estão no repositório — o componente se esconde automaticamente (sem quebrar o layout) quando a imagem correspondente não existe. Basta adicionar os PNGs com esses nomes em public/mascote/ para o mascote aparecer.
O app precisa de duas contas externas configuradas antes de funcionar de verdade:
Crie um projeto em supabase.com.
Abra SQL Editor e rode o conteúdo dos arquivos em supabase/migrations/, em ordem (0001_init.sql, 0002_..., etc.) — cada um é rodado manualmente, não há pipeline automático de migração.
Em Authentication > Providers, confirme que "Email" está habilitado. Em Authentication > Emails, decida se exige confirmação de e-mail (recomendado em produção).
Em Project Settings > API Keys, copie:
https://xxxx.supabase.co) → NEXT_PUBLIC_SUPABASE_URLsb_publishable_...) → NEXT_PUBLIC_SUPABASE_ANON_KEYsb_secret_...) → SUPABASE_SERVICE_ROLE_KEY (secreta — só no servidor)(Em projetos Supabase mais antigos essas chaves aparecem como anon public / service_role — funcionam da mesma forma, só o nome muda.)
Depois de adicionar/alterar variáveis na Vercel, é preciso fazer um novo deploy para elas passarem a valer — variáveis de ambiente não afetam deploys já publicados.
SMTP próprio (obrigatório para suportar múltiplos cadastros). Por padrão o Supabase manda os e-mails de confirmação/recuperação de senha pelo servidor deles, compartilhado entre todos os projetos gratuitos — o limite é de poucos e-mails por hora, e estoura rápido (erro email rate limit exceeded). Pra suportar dezenas de despachantes se cadastrando, configure um SMTP próprio:
smtp.resend.com465Crie um produto e três preços recorrentes mensais em Product catalog — um por plano (Essencial, Profissional, Escritório; valores e cotas em lib/plans.ts). Copie cada price_... → STRIPE_PRICE_ESSENCIAL / STRIPE_PRICE_PROFISSIONAL / STRIPE_PRICE_ESCRITORIO.
Em Developers > API keys, copie a Secret key → STRIPE_SECRET_KEY.
Em Developers > Webhooks, crie um novo destino de evento (não reaproveite um endpoint de outro projeto) apontando para https://SEU-DOMINIO/api/stripe/webhook, escutando: customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, checkout.session.completed, checkout.session.async_payment_succeeded. Copie o Signing secret → STRIPE_WEBHOOK_SECRET.
O último evento (async_payment_succeeded) cobre métodos de pagamento assíncronos (boleto, por exemplo): checkout.session.completed dispara assim que o cliente gera o boleto, não quando ele paga — payment_status continua "unpaid" até a compensação (1-3 dias úteis). O webhook (app/api/stripe/webhook/route.ts) checa payment_status === "paid" antes de liberar qualquer crédito/documento, então sem esse evento configurado, uma compra paga via boleto nunca seria processada depois que compensasse.
Se sua conta usa Sandboxes (contas Stripe novas), confirme que o produto, a chave e o webhook estão todos dentro do mesmo sandbox — cada sandbox tem chaves e webhooks isolados.
Cobrança avulsa do excedente: quando o despachante estoura a cota de consultas avançadas ou simples do plano no período (e, no caso de consulta simples, não tem crédito de recarga disponível), app/api/consulta-avancada e app/api/veiculo criam um item de fatura avulso (stripe.invoiceItems.create) nos valores de PRECO_AVULSO_CENTAVOS / PRECO_AVULSO_SIMPLES_CENTAVOS (lib/plans.ts) — o Stripe inclui isso automaticamente na próxima fatura do cliente, sem precisar de nenhum job agendado.
Pacotes de recarga de créditos: não exigem nenhum Price cadastrado no Stripe — app/api/checkout-creditos cria a sessão de checkout com inline (valores em , ), e o webhook () credita os créditos ao usuário via . Créditos expiram 6 meses após a compra, ou imediatamente se a assinatura for cancelada (, chamada tanto pelo webhook quanto pela revogação manual de acesso no admin).
gateway.apibrasil.io, produto endereco-telefone-por-placa) — mesmo token das outras consultas desse provedor → APIBRASIL_TOKEN. Não precisa de variável própria.lib/dados-veiculo.ts chama esse provedor e devolve ficha do veículo (placa, chassi, Renavam, marca/modelo, ano, cor, combustível) e o dossiê completo do proprietário atual.app/api/crm/proprietario), que expõe dado pessoal parecido atrás desses dois controles. Foi uma escolha deliberada do produto; revise as implicações de LGPD antes de mudar esse comportamento de novo.
cpfCnpjCliente em app/dashboard/page.tsx) — não vem da API, não é persistido, existe só naquela consulta/impressão.lib/infosimples.ts fica sem uso por enquanto (endpoint de veículo não autorizado na conta atual) — mantido só caso a consulta de multas ANTT/SIFAMA seja retomada."http", nunca no simples "essa placa não existe"), lib/dados-veiculo.ts tenta automaticamente um segundo fornecedor (wdapi2.com.br, revendido também como placas.com.br/apiplacas.com.br) → WDAPI2_TOKEN. Esse provedor só devolve dado do veículo (placa, chassi mascarado, marca/modelo, ano, cor, FIPE) — sem dossiê do proprietário. Sem o token configurado, o fallback é simplesmente pulado.PLACA_DEBITOS_API_TOKEN.lib/dados-avancados.ts chama esse provedor sob demanda — só quando o despachante clica no botão "Consultar multas, roubo/furto e Renajud reais" na ficha, nunca automaticamente numa consulta normal, já que cada chamada tem custo. Devolve: multas em aberto (item a item: descrição, valor, pontos, gravidade, órgão competente, artigo e o papel do responsável — "Proprietário" ou "Condutor", nunca um nome de pessoa), totais agregados, ocorrência de roubo/furto (tipo, data, município) e restrições judiciais ativas no Renajud (tribunal, número do processo, tipo de restrição). O provedor não devolve data nem local de cada multa individual — só o que está listado acima existe na resposta dele.sanitizeAvancada() em lib/dados-avancados.ts só libera o nome e, quando o documento tem 14 dígitos (pessoa jurídica), o CNPJ; documento de 11 dígitos (CPF de pessoa física) nunca é exposto por esse provedor específico — diferente da consulta simples (item 3 da seção acima), que hoje já traz o CPF por decisão de produto. O campo infrator de cada multa também foi conferido: é só a categoria de quem responde pela multa, não um nome — validado com uma amostra real antes de liberar a listagem item a item.Diferente dos dois provedores acima (consulta por placa de terceiros), "Meus Veículos" (app/dashboard/meus-veiculos) é um cofre pessoal por despachante: cada um cadastra manualmente ou importa o JSON obtido na própria sessão autenticada do Portal SENATRAN — dado que o despachante já possui legitimamente antes de entrar no sistema.
meus_veiculos, migração 0007_meus_veiculos.sql) e o PDF do CRLV num bucket privado do Supabase Storage (crlv-pdfs), ambos isolados por despachante via RLS (auth.uid() = user_id).meus_veiculos_auditoria) registra usuário, ação e veículo por id interno — nunca nome, CPF/CNPJ, Renavam ou chassi.Recurso pago à parte (R$65,00 por emissão, Stripe Checkout avulso), separado da consulta de dados do veículo — usa outro provedor (API Brasil), acionado só quando o despachante pede.
app.apibrasil.io > Bearer Token) → APIBRASIL_TOKEN.lib/crlv.ts chama POST https://gateway.apibrasil.io/api/v2/consulta/veiculos/credits com {"tipo":"crlve","placa","uf","homolog":false}, autenticado com Authorization: Bearer <token>./dashboard/documentos → app/api/checkout-crlv cria uma sessão de Stripe Checkout avulsa (preço inline, PRECO_CRLV_CENTAVOS em lib/crlv.ts, sem exigir Price cadastrado) → só depois do pagamento confirmado (checkout.session.completed) o webhook chama a API Brasil e salva o resultado em documentos_crlv (migração 0011_documentos_crlv.sql). O PDF retornado (base64) é decodificado e salvo no mesmo bucket privado crlv-pdfs já usado por "Meus Veículos" (pasta ${user_id}/emissoes/${id}.pdf), reaproveitando a policy de RLS existente — nenhum bucket novo é necessário.Consulta a situação de um transportador (RNTRC/ANTT) — útil pra despachante que atende frota/transporte de carga, não só veículo de passeio. Mesmo provedor/token do CRLV-e (APIBRASIL_TOKEN), endpoint diferente.
lib/rntrc.ts chama POST https://gateway.apibrasil.io/api/v2/consulta/api-rntrc/credits com {"tipo":"search","homolog":false,"filters":{"cpf_cnpj":"..."} ou {"rntrc":"..."},"pagination":{"page":1,"page_size":1}}./dashboard/logistica: CPF/CNPJ direto, número do RNTRC direto, ou placa do caminhão — nesse último caso, app/api/logistica/rntrc primeiro resolve a placa pro CNPJ do proprietário (via consultarVeiculoPorPlaca, mesma função da consulta simples) e só então busca o RNTRC desse CNPJ. A API do RNTRC indexa por transportador, não por veículo — não existe filtro por placa nela. Só funciona quando o dono é pessoa jurídica: o app nunca expõe CPF de proprietário pessoa física (LGPD), então não há CPF disponível aqui pra buscar quando o dono é pessoa física.app/api/consulta-avancada (cota do plano → crédito de recarga → avulso cobrado no Stripe), gravando o uso em avancada_usage (coluna placa guarda o CPF/CNPJ ou RNTRC consultado nesse caso, não necessariamente uma placa de veículo).Consulta o proprietário atual de um veículo sem sanitização — nome completo e CPF/CNPJ, ao contrário de todo o resto do app (que nunca expõe CPF de pessoa física). Decisão deliberada, pra atender despachante/profissional veicular que precisa desse dado completo — atrás de proteções extras, não do jeito aberto.
lib/proprietario.ts chama o mesmo endpoint do CRLV-e (POST .../consulta/veiculos/credits) com {"tipo":"proprietario-atual-v2","placa":"...","homolog":false}./dashboard/crm, migração 0013_crm_proprietario.sql):
profiles.cpf_cnpj, via /api/perfil/cpf-cnpj) — nunca pedido no cadastro inicial.app/api/crm/proprietario rejeita se não bater com o cadastrado.crm_proprietario_auditoria: usuário, placa, quando) — nunca o nome/CPF do proprietário retornado, só o fato de que a consulta aconteceu.avancada_usage/recargas_avancada), sem tabela de crédito própria.lib/proprietario.ts em nenhum outro fluxo sem manter as três proteções acima — é a única exceção deliberada à regra de nunca expor CPF de pessoa física.Em Project Settings > Environment Variables do projeto doc-car-app, adicione as variáveis acima. Depois de salvar, é preciso um novo deploy (as variáveis não afetam deploys já publicados) — basta mesclar qualquer PR em main para gerar um.
Sem essas variáveis configuradas, o app sobe normalmente mas login/cadastro, assinatura e consulta real mostram erro — é esperado até a configuração ser concluída.
O projeto está conectado à Vercel via este repositório GitHub: todo push no branch main gera um deploy automático. Não é necessário publicar manualmente — basta abrir um PR, revisar e fazer merge em main.
resend[email protected])DOC.CARprice_dataPACOTES_RECARGAlib/plans.tscheckout.session.completedregistrarRecarga()expirarCreditosPorCancelamento()PIX na compra de recarga: app/api/checkout-creditos já pede payment_method_types: ["card", "pix"] — mas o Stripe só oferece PIX de fato se ele estiver ativado pra sua conta. Confirme em Settings > Payment methods que PIX aparece como "On" (contas brasileiras novas costumam já vir habilitadas; se não vier, tem que ativar manualmente ali). Sem isso, o checkout cai só no cartão. A assinatura mensal (app/api/checkout) continua só cartão — PIX não é reutilizável pra cobrança automática recorrente.