MilWebWhatsApp
Voltar aos projetos

E-commerce white-label · checkout no WhatsApp

Loja de iPhone

1 base white-label → várias lojas

Problema

Lojas de iPhone vendem só pelo Instagram, sem uma vitrine própria e profissional.

Solução

Loja completa com catálogo, painel admin de produtos e estoque, e pedido direto no WhatsApp. É uma base white-label que vira várias lojas trocando só cor, logo e contato.

Como foi construído

Next.js 15 (App Router) com TypeScript, Tailwind v4 com tema 100% CSS-first (sem tailwind.config: as cores vivem em @theme inline no globals.css) e Supabase cobrindo Auth, Postgres e Storage. A personalização multi-cliente não é feita por fork. Um único arquivo (src/config/theme.config.ts) define nome, WhatsApp, logo e paleta, que o <ThemeInjector> converte em CSS variables (--store-*) no :root, e os componentes só consomem utilitários (bg-bg, text-primary). Para revenda em série, o mesmo theme.config.ts lê variáveis NEXT_PUBLIC_STORE_* e NEXT_PUBLIC_COLOR_* com fallback: cada loja nova é o mesmo código com Environment Variables diferentes na Vercel. O único artefato por cliente é o arquivo do logo em /public.

O estoque vai além de um campo único: products.stock é sempre o total, mas duas tabelas extras (units, inventory) habilitam um modo multi-loja física, com estoque por unidade. Um trigger no Postgres soma o inventory a cada mudança e recalcula o total automaticamente, então o resto da aplicação (carrinho, badge de estoque, JSON-LD) nunca precisa saber se a loja tem uma ou dez unidades. Ativar ou desativar esse modo semeia ou limpa o inventário preservando o total, com uma guarda contra corrida que checa se a unidade recém-criada é de fato a única, pra dois admins não duplicarem o estoque criando a 'primeira' loja ao mesmo tempo.

Não há checkout com gateway: o carrinho vive 100% no client (Zustand com persist em localStorage) e o fechamento é sempre por WhatsApp. Um método syncWithServer reconcilia o carrinho salvo com o estado fresco do banco a cada visita. Ele remove item que ficou inativo ou zerou estoque, clampa a quantidade ao estoque disponível e sinaliza lastSyncChanged pra UI avisar o cliente. Todo link wa.me passa por normalizeWhatsapp(), que aplica a regra do nono dígito da Anatel e o DDI 55, e por waUrl(), nunca montado à mão. O componente <WhatsappCta> decide sozinho o número certo: vai direto se a loja tem 0 ou 1 unidade, ou abre um seletor modal quando há 2 ou mais lojas atendendo, cada uma com o próprio WhatsApp.

A postura de segurança leva a sério que este é um white-label que vira produto de terceiros. RLS habilitada em toda tabela do schema público, sem exceção, com uma allow-list de admins numa tabela sem nenhuma policy, acessível só via função SECURITY DEFINER is_admin(). A service_role não é usada em lugar nenhum: as mutações do admin rodam com a sessão do dono, tendo o RLS como defesa final. O rate limit de login persiste no Postgres, então sobrevive a cold start serverless, e nunca grava o IP em claro: salva um SHA-256(salt + IP), por exigência de LGPD. Até o JSON-LD do produto escapa '<' como \u003c antes de serializar, pra um texto vindo do banco com '</script>' não injetar HTML na página.

White-label por config

Um arquivo (theme.config.ts) e variáveis de ambiente definem marca, cor e WhatsApp. Cada cliente é o mesmo código com env vars diferentes na Vercel.

Estoque por unidade

Trigger no Postgres soma o inventory de cada loja física e recalcula products.stock automaticamente. O app nunca sabe se há 1 ou 10 lojas.

Checkout 100% WhatsApp

Sem gateway: número normalizado (regra do 9º dígito), carrinho reconciliado com o estoque real e seletor de loja quando há 2+ unidades atendendo.

RLS em toda tabela

Nenhum uso de service_role; allow-list de admins via função SECURITY DEFINER e IP de login hasheado (SHA-256) para LGPD.

Mais telas

Home da loja com hero animado e catálogo de destaquesSeção de catálogo com estoque, condição e parcelamento por produtoSeção de lojas físicas com endereço, horário e WhatsApp por unidade

Stack

  • Next.js
  • TypeScript
  • Tailwind
  • Supabase
  • Zustand

Pronto para transformar sua ideia em um produto digital?

Me conta o que você precisa. Respondo rápido e te passo um orçamento gratuito, sem compromisso.

  • Contrato de garantia
  • Suporte pós-entrega
  • O código é seu
  • Orçamento gratuito