Adios
BlogComercio electrónico con Next.js

Comercio electrónico con Next.js

Cómo crear un carrito y Stripe Checkout con Next.js

Implementa carritos anónimos y de clientes, precios controlados por el servidor, Stripe Checkout, webhooks firmados, pedidos idempotentes y validación de inventario en Next.js.

Equipo de AdiosActualizado 17 de julio de 20268 min de lectura

Un carrito registra la intención de compra. No autoriza un precio, reserva stock automáticamente ni demuestra que se haya completado el pago.

Elige la identidad y duración del carrito

Crea un token aleatorio de carrito para compradores anónimos y guárdalo en una cookie segura HTTP-only. La base de datos controla el carrito, los IDs de variante de cada línea, las cantidades, las marcas de tiempo y el estado. Tras iniciar sesión, asócialo o fusiónalo con el del cliente mediante reglas explícitas.

Define una caducidad por inactividad y decide si los carritos pagados se convierten en instantáneas inmutables. No guardes todo el carrito ni los precios de referencia en una cookie legible por el navegador que los clientes y extensiones puedan modificar.

Valida cada mutación del carrito

Una acción de añadir acepta un ID de variante y una cantidad, resuelve el carrito mediante el token controlado por el servidor, comprueba que la variante está activa, aplica límites de cantidad y hace upsert de la línea. La acción nunca acepta del navegador la propiedad de la cuenta, el precio del SKU ni la moneda.

Devuelve un error esperado para variantes inválidas o no disponibles. Revalida la vista del carrito tras escribir para que el usuario vea su actualización. La autorización y la comprobación de propiedad deben estar dentro de la acción, aunque la interfaz solo muestre controles válidos.

"use server";

export async function addToCart(variantId: string, quantity: number) {
  const cart = await requireCart();
  const variant = await getPurchasableVariant(variantId);
  if (!variant) return { error: "Variant unavailable" };

  await upsertCartLine(cart.id, variant.id, clampQuantity(quantity));
  updateTag("cart:" + cart.id);
}

Fusiona los carritos anónimos y de clientes

Al iniciar sesión, carga ambos carritos en una sola transacción del servidor. Combina variantes iguales hasta los límites de compra, conserva líneas únicas válidas e informa de los artículos eliminados o modificados. Marca el carrito anónimo como fusionado para que repetir la solicitud no duplique cantidades.

No elijas silenciosamente el carrito más caro o el más nuevo. El producto debe definir si prevalece el carrito del cliente, si se fusionan o si el comprador elige. Prueba el comportamiento en varias pestañas y con callbacks repetidos de inicio de sesión.

Recalcula los precios antes de Checkout

Carga en el servidor todas las variantes activas y sus precios actuales, aplica descuentos validados, calcula los datos fiscales y opciones de envío, y crea una instantánea del pedido pendiente. Si cambia un precio o la disponibilidad, devuelve el carrito actualizado para confirmación explícita, en lugar de cobrar silenciosamente otro importe.

Crea las líneas de Stripe a partir de los registros del servidor. Añade el ID del pedido pendiente como metadato y usa una clave estable de idempotencia para repetir un intento de pago cuando el negocio quiera una sola operación del proveedor.

Confirma el pago mediante webhooks

La URL de éxito muestra que Checkout ha vuelto; no demuestra la liquidación del pago. Verifica la firma del webhook con el cuerpo original de la solicitud, localiza el pedido pendiente mediante metadatos fiables y registra el evento Stripe bajo una restricción de unicidad.

Crea o marca el pedido final una vez y pon después en cola el correo de confirmación y la preparación del pedido. Un evento duplicado debe devolver éxito sin repetir esos efectos. Una sesión caducada puede liberar reservas y devolver el carrito a un estado editable.

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get("stripe-signature");
  const event = stripe.webhooks.constructEvent(
    rawBody,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET,
  );

  await processCheckoutEventOnce(event);
  return new Response(null, { status: 200 });
}

Protege las lecturas de carritos y pedidos

El acceso a un carrito anónimo exige el token imposible de adivinar emitido por el servidor. Los carritos y pedidos de clientes requieren al propietario autenticado. Las páginas de confirmación deben cargar usando tanto una referencia segura para el público como el contexto del propietario, en lugar de exponer IDs secuenciales de base de datos.

Devuelve DTO limitados a los Client Components. Los objetos del proveedor de pago, campos internos de fraude, payloads webhook y errores de base de datos permanecen en el servidor. Los registros deben usar IDs de pedidos y eventos sin imprimir innecesariamente datos de pago o direcciones.

Prueba la manipulación, los reintentos y la caducidad

Cambia la variante, cantidad y precio mostrado enviados; repite las acciones de añadir y fusionar; entrega dos veces un webhook; entrega eventos fuera de orden; haz caducar Checkout; retira stock durante el pago; y solicita el pedido de otro cliente. Todos los resultados deben ser seguros y explicables.

Despliega la compilación de producción en Adios con referencias a secretos de Stripe y la base de datos. Verifica la ruta HTTPS generada en modo de prueba, inspecciona los registros del entorno de ejecución y registra el webhook estable del dominio personalizado solo cuando la versión indique que está saludable.

Todos los artículos