Pular para o conteúdo
Kayro Gomes

E-commerce full-stack com Payload CMS

Loja virtual completa com catálogo, carrinho persistente, checkout integrado e painel admin customizado para gestão de produtos e pedidos.

  • Next.js 15
  • Payload CMS 3
  • TypeScript
  • Postgres
  • Stripe
  • Tailwind CSS
Foto de capa ilustrativa com gradiente azul e roxo — usada no hero da home

Construí essa loja para explorar como Payload CMS 3 lida com collections relacionais complexas (Products → Variants → Inventory → Orders). O Next.js 15 entra no checkout com Server Actions para mutações, e o Stripe Elements para o pagamento real. Tudo tipado end-to-end com TypeScript estrito.

Decisões de arquitetura

Optei por manter o carrinho no servidor (via cookie httpOnly + Postgres) em vez de localStorage. Sacrifica um pouco de performance no primeiro add-to-cart, mas garante que o carrinho sobrevive a logout, troca de dispositivo e tem o estoque checado em tempo real.

Em produção, e-commerce precisa de concorrência real no estoque. Dois clientes comprando a última unidade ao mesmo tempo não pode resultar em oversell. Postgres com SELECT FOR UPDATE resolve isso nativo; SQLite precisaria de lock no nível da aplicação.

1'use server'
2
3import { getPayload } from 'payload'
4import config from '@payload-config'
5import { cookies } from 'next/headers'
6
7export async function addToCart(productId: string, variantId: string) {
8 const cookieStore = await cookies()
9 const cartId = cookieStore.get('cart-id')?.value
10
11 const payload = await getPayload({ config })
12
13 // Cria o cart se não existir, com transação
14 const cart = await payload.db.executeTransaction(async (tx) => {
15 // ... lógica com SELECT FOR UPDATE
16 })
17
18 cookieStore.set('cart-id', String(cart.id), { httpOnly: true, sameSite: 'lax' })
19 return { success: true, cartId: cart.id }
20}
Imagem ilustrativa de exemplo usada em posts do blog

Imagem ilustrativa de exemplo. Substitua no admin em Mídia.

O que vem depois

Próximo passo é adicionar busca facetada no catálogo (por preço, categoria, tags) usando o índice GIN do Postgres, e webhooks do Stripe para reconciliação de pagamento em caso de retry do cliente.