Mini site de documentaçãoDeveloper Atlas

Entrada rápida para navegar arquitetura, APIs, operação e guias técnicos do projeto sem depender da estrutura do repositório.

Ecommerce - Carrinho e Checkout

Esta nota explica o fluxo que vai do clique em `Adicionar` até a consolidação do pedido no backend atual.

Recorte da seçãoGuia orientado por fluxo

Leitura pensada para explicar responsabilidades, ordem de execução e trechos reais do código com foco no fluxo da implementação.

Atualizado15 de abr. de 2026
Seções22
Tags4
guiaecommercecarrinhocheckout

O que você encontra aqui

Esta nota explica o fluxo que vai do clique em Adicionar até a consolidação do pedido no backend atual.

A ideia é separar mentalmente três camadas:

  • CartContext, que guarda itens e quantidades;
  • OrderFormContext, que projeta o carrinho para um formato de checkout;
  • CheckoutView, que valida a jornada e envia o pedido.

Arquivos principais

  • src/app/e-commerce/providers.tsx
  • src/features/ecommerce/state/CartContext.tsx
  • src/features/ecommerce/state/OrderFormContext.tsx
  • src/features/ecommerce/components/cart/CartView.tsx
  • src/features/ecommerce/components/cart/CartSummary.tsx
  • src/features/ecommerce/components/cart/CartShipping.tsx
  • src/features/ecommerce/components/checkout/CheckoutView.tsx
  • src/app/api/checkout/route.ts
  • src/app/api/ecommerce/order-draft/route.ts
  • src/features/ecommerce/server/orderStore.ts
  • src/features/ecommerce/lib/vtexCheckoutService.ts

Trecho 1 - providers aninhados

tsx
<CartProvider>
  <OrderFormProvider>{children}</OrderFormProvider>
</CartProvider>

Leitura guiada

A ordem importa.

O OrderFormProvider depende do CartProvider, porque ele transforma os itens do carrinho em uma estrutura maior de checkout.

Em uma explicação orientada, eu colocaria assim:

"O carrinho é a matéria-prima. O order form é a projeção comercial e logística desse carrinho."

Trecho 2 - reducer do carrinho

ts
type Action =
  | { type: 'ADD'; payload: Omit<CartItem, 'qty'> & { qty?: number } }
  | { type: 'INC'; id: string }
  | { type: 'DEC'; id: string }
  | { type: 'REMOVE'; id: string }
  | { type: 'CLEAR' }
  | { type: 'HYDRATE'; payload: CartState };

O que isso ensina

O carrinho é propositalmente pequeno.

Ele sabe fazer poucas coisas, mas faz isso muito bem:

  • hidratar do storage;
  • adicionar;
  • incrementar;
  • decrementar;
  • remover;
  • limpar.

Essa simplicidade ajuda a não contaminar o carrinho com regra de frete, cupom e pagamento.

Trecho 3 - order form sincronizado com o carrinho

ts
const items: OrderFormItem[] = Object.values(cart.items).map((item) => ({
  id: item.id,
  name: item.name,
  image: item.image,
  price: item.price,
  listPrice: item.listPrice,
  unit: item.unit,
  packSize: item.packSize,
  quantity: item.qty,
}));

const pricing = buildOrderPricing({
  items,
  shipping: prev.shipping,
  coupon: prev.marketingData.coupon,
});

Explicação em linguagem natural

O OrderFormContext fica observando o carrinho e recalcula a visão comercial.

Ou seja:

  • CartContext não conhece totalizador;
  • OrderFormContext conhece.

Ele transforma quantidade e preço em uma entidade mais rica:

  • items do pedido;
  • totalizadores;
  • valor final;
  • mensagens de cupom;
  • shipping;
  • paymentData;
  • preferencias do cliente.

Trecho 4 - resumo do carrinho mexe no order form

tsx
const { orderForm, updateMarketing } = useOrderForm();

<button onClick={() => updateMarketing({ coupon })}>Aplicar</button>

O que isso ensina

Cupom não altera o carrinho bruto. Cupom altera o marketingData do order form.

Esse detalhe é importante na explicação porque mostra que nem toda mutação da compra deveria cair no mesmo estado.

Trecho 5 - simulacao logistica no carrinho

tsx
const result = await simulateLogisticsClient({
  postalCode: cep,
  address: addr,
  items: orderForm.items.map((item) => ({ id: item.id, quantity: item.quantity })),
});

setShipping({
  address: addr,
  deliveryOptions,
  pickupOptions,
  selectedOptionId: result.recommendedOptionId,
  selectedMode,
});

Leitura técnica

A área de frete do carrinho agora injeta o recorte logístico completo no order form.

Ou seja:

  • endereço;
  • opções de entrega;
  • opções de retirada;
  • modo selecionado;
  • opção selecionada.

Isso reduz divergência entre carrinho, checkout e pedido final.

Na fase de sortimento único, esse cálculo pode ser a primeira entrada logística real da compra.

Ou seja:

  • o cliente navega a vitrine sem bloqueio;
  • escolhe entrega ou retirada quando fizer sentido;
  • o carrinho e o checkout continuam responsáveis por consolidar prazo e custo antes do submit.

Trecho 6 - validação por etapas no checkout

ts
function handleContinue(nextStep: CheckoutStep) {
  if (shouldValidate('profile', nextStep) && !validateProfile()) return;
  if (shouldValidate('address', nextStep) && !validateAddress()) return;
  if (shouldValidate('shipping', nextStep) && !validateShipping()) return;
  if (shouldValidate('payment', nextStep) && !validatePayment()) return;
  openStep(nextStep);
}

O que isso ensina

O checkout não valida tudo o tempo todo.

Ele valida progressivamente, conforme o usuário avanca.

Isso é uma boa oportunidade de explicar com clareza a diferença entre:

  • wizard de etapas;
  • validação incremental;
  • validação final antes do submit.

Trecho 7 - fechamento do pedido

ts
fetch('/api/checkout', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
})
  .then(async (response) => {
    if (!response.ok) throw new Error(await response.text());
    return response.json();
  })
  .then((result) => {
    clear();
    setOrderForm((prev) => ({ ...prev, items: [], totalizers: [], value: 0 }));
    router.push(`/e-commerce/checkout/confirmation?orderId=${encodeURIComponent(result.orderId)}`);
  })

Explicação em linguagem natural

A tela de checkout monta um payload consolidado do pedido e envia para a API interna.

Se der certo:

  • limpa o carrinho;
  • limpa o miolo transacional do order form;
  • redireciona para confirmação.

Trecho 7.1 - rascunho do checkout

Antes da consolidação final, o OrderFormContext também pode persistir o estado útil do checkout em /api/ecommerce/order-draft.

Isso cria uma camada prática para:

  • carrinho abandonado;
  • retomada da jornada;
  • ligação posterior entre intenção de compra e pedido final.

Regras atuais:

  • a sessão precisa passar de 20 minutos para virar draft persistido;
  • se ficar inativa por 20 minutos, o draft entra como abandonado;
  • 5 dias depois ele é removido.

Trecho 8 - API de checkout

ts
if (!Array.isArray(body.items) || body.items.length === 0) {
  return NextResponse.json({ error: 'Carrinho vazio' }, { status: 400 });
}
if (!body.clientProfileData || !body.clientProfileData.email) {
  return NextResponse.json({ error: 'Dados do cliente incompletos' }, { status: 400 });
}
if (!body.shipping || !body.shipping.selectedAddress) {
  return NextResponse.json({ error: 'Endereco não informado' }, { status: 400 });
}
if (!Array.isArray(body.payments) || body.payments.length === 0) {
  return NextResponse.json({ error: 'Forma de pagamento não selecionada' }, { status: 400 });
}

O que isso ensina

Mesmo com validação na UI, a API ainda valida o mínimo obrigatorio.

Isso é o desenho correto:

  • a UI guia;
  • a API protege a consistencia.

E agora ela também:

  • atualiza a projeção da conta do cliente;
  • consolida o pedido operacional em tabela própria;
  • marca o rascunho como convertido quando existir.
  • quebra a compra em subpedidos quando a opção logística vier com múltiplas origens.

Ordem de execução do fluxo completo

  1. o usuário adiciona produto na PLP ou PDP;
  2. o CartContext persiste itens no navegador;
  3. o OrderFormContext deriva items, totalizadores e valor final;
  4. a página de carrinho exibe items, cupom e frete;
  5. o checkout coleta perfil, endereço, shipping e pagamento;
  6. o cliente envia POST /api/checkout;
  7. o OrderFormContext pode persistir um rascunho do checkout;
  8. a API valida o payload e cria o pedido consolidado;
  9. o pedido operacional entra em commerce_orders;
  10. a projeção do cliente continua em customer_orders;
  11. a UI limpa o estado e vai para confirmação.

Explicando de forma simples

"Carrinho e checkout não são a mesma coisa. O carrinho guarda a intenção de compra. O order form organiza essa intenção em estrutura comercial e logística. O checkout apenas conduz o usuário pela validação dessa estrutura até o submit final."

Fechamento

Depois desta nota, o aluno consegue seguir o fluxo inteiro da compra sem se perder entre componentes, contextos e API.