La autenticación y los pagos son los dos elementos que convierten un sitio de Webflow en un producto, y ambos pueden ejecutarse en Webflow Cloud junto al propio sitio, con las comprobaciones de sesión y la verificación de webhooks realizadas en el lado del servidor, donde deben estar.
El patrón de "añadir autenticación y pagos a una aplicación web" es uno de los requisitos full-stack más comunes y, a la vez, uno de los más delicados de montar fuera de Vercel. He construido esta arquitectura en AWS Lambda, en Netlify Functions y en servidores Express independientes, y siempre llega un momento en que el modelo mental se rompe.
El gestor de webhooks vive en un servicio, la sesión en otro y el entorno de ejecución de cada pieza es ligeramente distinto. Cuando algo falla en producción, pasas los primeros diez minutos intentando averiguar en qué capa está el problema.
En Webflow Cloud, esta arquitectura se ejecuta en un solo lugar. Tu aplicación Next.js, incluyendo el middleware de Auth0, la ruta de Stripe Checkout y el gestor de webhooks, vive dentro de la infraestructura de Webflow. Un único pipeline de despliegue, un panel de variables de entorno y un conjunto de registros unificado cuando algo sale mal.
Esta guía cubre la integración completa: middleware de Auth0 v4 para la autenticación basada en sesiones, Stripe Checkout para el cobro de pagos y constructEventAsync verificación de webhooks para el aprovisionamiento tras el pago, incluyendo patrones específicos de Workers que difieren de los de un despliegue estándar en Node.js.
¿Qué necesitas para añadir autenticación y pagos a una aplicación de Webflow Cloud?
Necesitas un espacio de trabajo de Webflow con acceso a Cloud, un tenant de Auth0, una cuenta de Stripe, la CLI de Webflow y Node.js 22.13.0 o superior. Tanto Auth0 como Stripe tienen planes gratuitos generosos: el plan gratuito de Auth0 admite hasta 25.000 usuarios activos mensuales para escenarios B2C (500 MAU para B2B) e incluye soporte para inicio de sesión social.
Esto es lo que debes tener listo antes de escribir una sola línea de código.
Un espacio de trabajo de Webflow con acceso a Cloud
Webflow Cloud está incluido desde el plan gratuito Starter en adelante, con límites mayores de aplicaciones y almacenamiento en los planes de pago. Confirma que tienes acceso a la sección Cloud en tu panel de control de Webflow antes de empezar. Si no ves Cloud en la barra lateral izquierda, revisa tu plan.
Node.js 22.13.0 o superior y npm
El @auth0/nextjs-auth0 SDK v4 requiere Node.js 20 LTS o superior, y la CLI de Webflow 2.0 eleva ese requisito a 22.13.0, así que instala la versión 22.13.0 o superior para cumplir ambos. Confirma con node -v. Webflow Cloud solo admite npm como gestor de paquetes.
Además, Webflow Cloud requiere Next.js 15 o superior. Ejecuta next --version para confirmar antes de inicializar el proyecto. Si estás en Next.js 14 o anterior, actualiza primero.
CLI de Webflow
Instálala globalmente y autentícate antes de inicializar el proyecto:
npm install -g @webflow/webflow-cli
Confirma con webflow --version antes de continuar.
Autentica la CLI antes de empezar. webflow auth login abre una ventana del navegador para completar la autenticación y vincular tu sesión de terminal con tu espacio de trabajo de Webflow. Necesitarás tener esta sesión activa antes de ejecutar webflow cloud init en el Paso 1.
Un tenant de Auth0 con una aplicación web regular (Regular Web Application)
Crea una cuenta en auth0.com y abre el panel de control de Auth0.
En Applications → Applications, crea una nueva aplicación y selecciona Regular Web Applications como tipo. Una vez creada, abre la pestaña Credentials de la aplicación. Necesitarás el Client ID y el Client Secret de ahí, además del Domain que aparece en la configuración general de la aplicación.
El nivel gratuito de Auth0 admite hasta 25,000 usuarios activos mensuales e incluye soporte completo para inicio de sesión social. Para la mayoría de los productos, esto es suficiente para superar las etapas iniciales sin necesidad de un plan de pago.
Una cuenta de Stripe con claves secretas y de webhook
Crea una cuenta de Stripe en stripe.com.
Desde el panel de control de Stripe, ve a Developers → API keys para obtener tu clave secreta. Para el desarrollo, utiliza las claves del modo de prueba. Llevan un prefijo sk_test_ y nunca cobran dinero real.
También configurarás un endpoint de webhook más adelante (paso 5) y obtendrás un Signing secret independiente para la verificación del webhook.
Una vez que todo esté listo, la implementación completa lleva aproximadamente una hora.
6 pasos para añadir autenticación y pagos a una aplicación de Webflow Cloud
Estos seis pasos cubren la inicialización del proyecto, la configuración de los clientes de Auth0 y Stripe, el middleware y la protección de rutas, Stripe Checkout, el manejo de webhooks y el despliegue en producción. Los pasos 1-3 cubren la autenticación. Los pasos 4-5 cubren los pagos.
El paso 6 cubre la configuración de variables de entorno y del panel de control que separa una configuración local funcional de un despliegue en producción, lo cual es una lista más larga de lo que la mayoría de los desarrolladores esperan.
Si ya has inicializado un proyecto de Webflow Cloud siguiendo una guía anterior, la estructura del paso 1 te resultará familiar. Salta directamente al paso 2 una vez que los archivos de configuración estén confirmados.
1. Inicializa el proyecto e instala las dependencias
Comienza con la autenticación CLI y la inicialización del proyecto:
webflow auth login
webflow cloud init
webflow auth login autentica tu sesión CLI a través del navegador. webflow cloud init crea los cuatro archivos de configuración necesarios y vincula tu directorio local a un proyecto de Webflow Cloud. Si ya has trabajado con Webflow Cloud anteriormente (siguiendo la guía full-stack u otra integración en la nube), esta estructura te resultará familiar.
Los archivos webflow.json, wrangler.jsonc, open-next.config.ts, y cloudflare-env.d.ts son idénticos en todos los proyectos de Webflow Cloud.
Luego, instala ambos SDK con un solo comando:
npm install @auth0/nextjs-auth0 stripe
Una nota arquitectónica que vale la pena entender antes de escribir código: @auth0/nextjs-auth0 v4 es compatible con edge por defecto. La documentación de personalización del framework de Webflow Cloud establece explícitamente que solo se admite middleware de tiempo de ejecución edge; el middleware de tiempo de ejecución de Node.js no es compatible.
El diseño edge-first de Auth0 v4 significa que middleware.ts funciona en Webflow Cloud sin ninguna configuración especial. Este es un cambio significativo respecto a la v3, que requería una ruta de importación /edge separada para despliegues en edge.
Asegúrate de verificar los archivos de configuración.
El archivowebflow.json
El archivo debe contener exactamente:webflow.json
{
"cloud": {
"framework": "nextjs"
}
}
Si este archivo no existe después de , créalo manualmente en la raíz del proyecto. Este es el único archivo de configuración específico de Webflow. Todo lo demás es estándar de Next.js o Cloudflare.webflow cloud init
El archivowrangler.jsonc
El archivo debe incluir la bandera , que habilita las API de criptografía de Node.js de las que dependen internamente tanto Auth0 como Stripe:wrangler.jsoncnodejs_compat
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "nextjs",
"main": ".open-next/worker.js",
"compatibility_date": "2025-03-01",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"binding": "ASSETS",
"directory": ".open-next/assets"
},
"observability": { "enabled": true }
}
Sin , ambos SDK fallarán en tiempo de ejecución con errores de módulos faltantes.nodejs_compat
Instala el adaptador OpenNext y añade el script de vista previa:
npm install @opennextjs/cloudflare
Añade a los scripts:package.json
{
"scripts": {
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview"
}
}
Ejecuta antes de cada despliegue. La simulación de Wrangler que inicia detecta incompatibilidades en tiempo de ejecución antes de que lleguen a producción; es mucho más rápido que el ciclo de desplegar-depurar-volver a desplegar.npm run preview
2. Crea las factorías de cliente de Auth0 y Stripe
Los dos clientes tienen patrones de inicialización ligeramente diferentes, y la diferencia es importante para el tiempo de ejecución de Webflow Cloud.
Crea el cliente de Auth0
Crea :lib/auth0.ts
// lib/auth0.ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";
export const auth0 = new Auth0Client();
Auth0Client lee automáticamente las variables de entorno necesarias: , , , y . es opcional; si se omite, el SDK infiere la URL base a partir del host de la solicitud entrante en tiempo de ejecución.AUTH0_DOMAINAUTH0_CLIENT_IDAUTH0_CLIENT_SECRETAUTH0_SECRETAPP_BASE_URL
No los pases como argumentos del constructor; el SDK los resuelve desde cuando el módulo se carga por primera vez dentro del aislamiento de Cloudflare Workers, después de que el tiempo de ejecución haya inyectado las variables. Este patrón a nivel de módulo es el enfoque documentado de Auth0 para la v4 y funciona correctamente en Webflow Cloud.process.env
La variable es la que he visto configurada incorrectamente con más frecuencia. Debe establecerse con la URL de producción completa: , con el prefijo y sin barra diagonal al final.APP_BASE_URLhttps://your-production-domain.comhttps://
Si se omite, el SDK infiere dinámicamente la URL base a partir del host de la solicitud, lo cual funciona en entornos de vista previa pero puede producir resultados inesperados en Webflow Cloud, donde el host puede reflejar el dominio de la plataforma en lugar de tu URL de producción.
Para cualquier despliegue de producción estable, establécela explícitamente. Si se configura accidentalmente como en tus variables de producción, Auth0 redirigirá a localhost desde tu despliegue en vivo.http://localhost:3000
Crea la factoría del cliente de Stripe
Crea :lib/stripe.ts
// lib/stripe.ts
import Stripe from "stripe";
export function getStripeClient() {
const key = process.env.STRIPE_SECRET_KEY;
if (!key) throw new Error("Missing STRIPE_SECRET_KEY environment variable");
return new Stripe(key, {
httpClient: Stripe.createFetchHttpClient(),
});
}
La opción no es opcional en Webflow Cloud. El cliente HTTP predeterminado de Stripe utiliza para sus solicitudes de API. Aunque el tiempo de ejecución de Workers de Webflow Cloud incluye la bandera , es uno de los módulos de red de Node.js que sigue sin estar disponible en el entorno de Workers.httpClient: Stripe.createFetchHttpClient()node:httpsnodejs_compatnode:https
Stripe.createFetchHttpClient()reemplaza el transporte predeterminado con la API de la Plataforma Web , que está disponible de forma nativa en todos los entornos de Workers. Este es el único cambio de configuración de Stripe que requiere Webflow Cloud. Cualquier otro método del SDK funciona exactamente igual que en un entorno estándar de Node.js.fetch
El cliente de Stripe permanece dentro de una función de factoría (en lugar de a nivel de módulo, como con Auth0) porque lee desde , y el patrón de factoría asegura que la clave se lea en tiempo de ejecución en lugar de durante la evaluación del módulo.STRIPE_SECRET_KEYprocess.env
3. Añade el middleware de Auth0 y protege las rutas
Auth0 v4 gestiona la autenticación completamente a través del middleware de Next.js. No hay que crear ningún gestor de rutas, ni usar handleAuth() export, ni crear un directorio app/api/auth/; el archivo de middleware monta automáticamente las seis rutas de autenticación y gestiona la renovación de la sesión en cada solicitud.
Este es un cambio arquitectónico importante respecto a la v3, y es el que tiene más probabilidades de romper una migración si estás trabajando con documentación antigua.
En Webflow Cloud, esto es importante por una razón específica: solo se admite el middleware con entorno de ejecución Edge. El diseño "edge-first" de Auth0 v4 significa que middleware.ts funciona en Webflow Cloud sin ninguna configuración especial; a diferencia de la v3, que requería una ruta de importación /edge independiente.
Aquí tienes la configuración completa del middleware, incluyendo el patrón de coincidencia y los componentes de navegación que utilizan las nuevas rutas.
Crea middleware.ts
Crea en la raíz del proyecto. En Auth0 v4, este archivo reemplaza al gestor de rutas "catch-all" de la v3; ya no existe middleware.tsapp/api/auth/[auth0]/route.ts .
El middleware monta automáticamente las seis rutas de autenticación y gestiona la renovación de la sesión:
// middleware.ts
import type { NextRequest } from "next/server";
import { auth0 } from "./lib/auth0";
export async function middleware(request: NextRequest) {
return await auth0.middleware(request);
}
export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
],
};
Este único archivo monta las siguientes rutas: /auth/login, /auth/logout, /auth/callback, /auth/profile, /auth/access-token y . El prefijo de la ruta ha cambiado respecto a la v3: todas son /auth/backchannel-logout, no /auth/*./api/auth/*
Si estás migrando desde un proyecto v3, todos los enlaces de inicio y cierre de sesión de tu aplicación deben actualizarse a las nuevas rutas.
Nota sobre Next.js 16: Next.js 16 marca como obsoleto en favor de middleware.ts. Sin embargo, proxy.ts se ejecuta en el entorno de Node.js, y Webflow Cloud solo admite middleware con entorno de ejecución Edge. No cambies el nombre de proxy.ts a middleware.ts para un despliegue en Webflow Cloud.proxy.ts
Mantén middleware.ts tal cual. Auth0 confirma que middleware.ts sigue funcionando sin problemas bajo el entorno de ejecución Edge en Next.js 16. La obsolescencia solo se aplica a la ruta del entorno de ejecución de Node.js, que Webflow Cloud no utiliza de todos modos.
Añade la navegación de inicio y cierre de sesión
El enrutamiento de Auth0 v4 funciona a través de etiquetas <a> estándar, no mediante componentes <Link> de Next.js.
El SDK procesa estas rutas en la capa de middleware antes que el enrutador del lado del cliente de Next.js, por lo que <Link> provoca un comportamiento inesperado:
// app/components/Nav.tsx
import { auth0 } from "@/lib/auth0";
export default async function Nav() {
const session = await auth0.getSession();
return (
<nav>
{session ? (
<>
<span>Welcome, {session.user.name}</span>
<a href="/auth/logout">Sign out</a>
</>
) : (
<a href="/auth/login">Sign in</a>
)}
</nav>
);
}
auth0.getSession() devuelve el objeto de sesión actual si el usuario está autenticado, o null si no lo está. Utilizo esta llamada en todos los componentes de servidor que necesitan bifurcarse según el estado de autenticación. Es una única llamada asíncrona sin configuración adicional. El descifrado de la sesión y la validación de cookies ocurren automáticamente dentro del SDK.
Protege una página con un protector de sesión
Para restringir el acceso a una página mediante autenticación, llama a getSession() al principio del componente de servidor y redirige si es nulo:
// app/dashboard/page.tsx
import { auth0 } from "@/lib/auth0";
import { redirect } from "next/navigation";
export default async function Dashboard() {
const session = await auth0.getSession();
if (!session) {
redirect("/auth/login?returnTo=/dashboard");
}
return (
<main>
<h1>Dashboard</h1>
<p>Welcome back, {session.user.email}</p>
</main>
);
}
El parámetro de consulta returnTo le indica a Auth0 a dónde redirigir después de un inicio de sesión exitoso. Esto viene integrado en el SDK; no requiere configuración adicional. Lo uso en todas las páginas protegidas para que los usuarios vuelvan a donde estaban después de iniciar sesión, en lugar de aparecer en una página de inicio genérica.
4. Crea la ruta de Stripe Checkout
Stripe Checkout es la página de pagos alojada de Stripe. Tu gestor de rutas crea una sesión de Checkout en el servidor, especificando el producto, el precio y las URL de redirección. El usuario es redirigido a la página de Stripe para completar el pago y, después, vuelve a tu aplicación.
La clave para conectar Auth0 y Stripe es . Lo configuro como : el identificador único y estable de Auth0 para el usuario. La declaración no cambia si el usuario actualiza su correo electrónico o perfil, y persiste a lo largo de las sesiones.client_reference_idsession.user.subsub
En el gestor de webhooks (paso 5), recuperas este ID de la sesión de Checkout completada para saber exactamente a qué usuario debes dar acceso.
Crea la ruta de la sesión de pago
La ruta comprueba primero si hay una sesión de Auth0 activa; las solicitudes no autenticadas devuelven un 401 antes de realizar cualquier llamada a la API de Stripe. La declaración de la sesión se convierte en el , que Stripe devuelve en la carga útil del webhook cuando se completa el pago.subclient_reference_id
Así es como el gestor de webhooks (paso 5) sabe a qué usuario dar acceso.
// app/api/checkout/route.ts
import { auth0 } from "@/lib/auth0";
import { getStripeClient } from "@/lib/stripe";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const session = await auth0.getSession();
if (!session) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
}
const stripe = getStripeClient();
const checkoutSession = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "payment",
client_reference_id: session.user.sub,
customer_email: session.user.email,
line_items: [
{
price_data: {
currency: "usd",
product_data: {
name: "Pro access",
},
unit_amount: 4900, // $49.00 in cents
},
quantity: 1,
},
],
success_url: `${process.env.APP_BASE_URL}/dashboard?payment=success`,
cancel_url: `${process.env.APP_BASE_URL}/dashboard?payment=cancelled`,
});
return NextResponse.json({ url: checkoutSession.url });
}
El y el utilizan de las variables de entorno (la misma que usa Auth0) en lugar de dominios codificados. Esto significa que el desarrollo local y la producción utilizan orígenes diferentes sin necesidad de cambiar el código, solo los valores de las variables.success_urlcancel_urlAPP_BASE_URL
Añade el activador de pago
En el lado del cliente, un componente de botón llama a esta ruta y redirige a la URL de Stripe Checkout:
// app/components/CheckoutButton.tsx
"use client";
export default function CheckoutButton() {
async function handleCheckout() {
const response = await fetch("/api/checkout", { method: "POST" });
const { url } = await response.json();
if (url) window.location.href = url;
}
return (
<button onClick={handleCheckout}>
Get Pro Access — $49
</button>
);
}
Este componente solo gestiona la redirección. La comprobación de la sesión, la creación de la sesión de Checkout y la generación de la URL ocurren en el lado del servidor, dentro del gestor de rutas. Si un usuario no autenticado activa este botón por error (por ejemplo, si la página que lo contiene no tiene un protector de sesión), el gestor de rutas devuelve un 401 y la redirección nunca se ejecuta.
5. Gestiona los webhooks de Stripe con constructEventAsync
Los webhooks son la forma en que Stripe notifica a tu aplicación que un pago se ha realizado correctamente. Cuando el usuario completa Stripe Checkout, Stripe envía un evento a tu punto de conexión.checkout.session.completed
Tu gestor verifica la firma del evento, recupera los datos de la sesión y proporciona acceso al usuario identificado por .client_reference_id
Dos patrones difieren de un gestor de webhooks estándar de Node.js en Webflow Cloud:
constructEventAsyncconstructEvent: en lugar de la API WebCrypto de Cloudflare Workers () es asíncrona. El de Stripe utiliza el módulo criptográfico síncrono de Node.js para la verificación de firmas HMAC, que no funciona en el entorno de ejecución de Workers, ni siquiera con . con utiliza la API Web Crypto y funciona de forma nativa.SubtleCryptoconstructEventnodejs_compatconstructEventAsyncStripe.createSubtleCryptoProvider()
Lee primero el cuerpo sin procesar: la verificación de la firma de Stripe requiere el cuerpo de la solicitud sin procesar y sin analizar. En un gestor de rutas de Next.js en Webflow Cloud, te proporciona el cuerpo sin procesar como una cadena. Debes llamar a esto una vez, antes que nada, y pasar esa cadena a .await request.text()constructEventAsync
Leo el cuerpo sin procesar en la primera línea de cada gestor de webhooks, sin excepciones. Me he quemado con esto dos veces; una por un middleware de registro que clonó la solicitud, y otra por llamar accidentalmente a antes de que se ejecutara el gestor de webhooks.request.json()
El flujo del cuerpo es destructivo: una vez consumido, desaparece.
// app/api/webhooks/stripe/route.ts
import { getStripeClient } from "@/lib/stripe";
import Stripe from "stripe";
import { NextRequest, NextResponse } from "next/server";
const webCrypto = Stripe.createSubtleCryptoProvider();
export async function POST(request: NextRequest) {
// Read raw body FIRST — before any other body access
const body = await request.text();
const signature = request.headers.get("stripe-signature");
if (!signature) {
return NextResponse.json(
{ error: "Missing stripe-signature header" },
{ status: 400 }
);
}
const stripe = getStripeClient();
let event: Stripe.Event;
try {
event = await stripe.webhooks.constructEventAsync(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!,
undefined,
webCrypto
);
} catch (err) {
console.error("Webhook signature verification failed:", err);
return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
}
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
const userId = session.client_reference_id;
if (userId) {
// userId is the Auth0 sub — provision access here
// e.g., write to Supabase, Cloudflare KV, or your database
console.log(`Payment completed for user: ${userId}`);
}
}
return NextResponse.json({ received: true });
}
El proveedor se crea una vez en el ámbito del módulo porque no depende de variables de entorno; es una utilidad estática del SDK de Stripe. El cliente de Stripe permanece dentro de la función porque lee en el momento de la llamada. Este es el mismo patrón de fábrica que la ruta de pago.webCryptoSTRIPE_SECRET_KEY
El recuperado del es el de Auth0; el mismo valor que pasaste en el paso 4. En este punto del gestor, ya tienes un evento de pago verificado y un ID de usuario.userIdsession.client_reference_idsub
Lo que hagas con ello depende de tu modelo de acceso: escribir un registro en Supabase, marcar una opción en Cloudflare KV, actualizar tu CMS o llamar a un servicio externo. El patrón es el mismo en cualquier caso.
6. Configura las variables de producción y despliega
El desarrollo local y la producción requieren valores distintos para varias variables. La forma más fiable que he encontrado para gestionar esto es tratar el panel de Configuración de la nube de Webflow → Variables como la única fuente de verdad para producción, y .env.local como la anulación local.
Nunca copies .env.local valores a producción.
Establece todas las variables en Configuración de la nube de Webflow → Variables
Antes de ejecutar webflow cloud deploy, confirma que todas las variables siguientes estén configuradas en el panel de Configuración → Variables de tu proyecto en la nube de Webflow. Las variables en .env.local son de solo lectura durante el desarrollo local; no se despliegan ni son leídas por el worker de producción.
# Auth0
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
AUTH0_SECRET=64-hex-char-string-from-openssl
APP_BASE_URL=https://your-production-domain.com
# Stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Genera AUTH0_SECRET con openssl rand -hex 32; esto produce exactamente 64 caracteres hexadecimales. Si el valor es más corto o contiene caracteres no hexadecimales, el descifrado de la sesión de Auth0 fallará con un error críptico en cada inicio de sesión.
Actualiza las URL de la aplicación Auth0
Antes de desplegar, actualiza la configuración de tu aplicación Auth0.
En el panel de control de Auth0, bajo Aplicaciones → [tu aplicación] → Configuración, actualiza estos tres campos para tu dominio de producción:
- URL de devolución de llamada permitidas:
https://your-production-domain.com/auth/callback - URL de cierre de sesión permitidas:
https://your-production-domain.com - Orígenes web permitidos:
https://your-production-domain.com
Auth0 realiza una coincidencia exacta de URL: una barra diagonal final o un www. prefijo que no coincida provocará un error de devolución de llamada. Si utilizas tanto una URL de prueba de Webflow Cloud como un dominio personalizado, añade ambos a cada campo, separados por comas. Mantengo esta lista de verificación junto a la de despliegue: cada dominio nuevo se añade a estos tres campos antes del primer despliegue.
Registra el endpoint del webhook de Stripe
En el panel de control de Stripe, bajo Desarrolladores → Webhooks, haz clic en Añadir endpoint e introduce la URL de tu webhook de producción:
https://your-production-domain.com/api/webhooks/stripe
Selecciona el checkout.session.completed evento.
Después de guardar, revela el secreto de firma (empieza por whsec_) y añádelo a la Configuración de la nube de Webflow → Variables como STRIPE_WEBHOOK_SECRET. Este secreto de firma de producción es distinto al que genera la CLI de Stripe para el desarrollo local; mantenlos separados.
Luego, despliega:
webflow cloud deploy
Tras el despliegue, prueba todo el flujo desde la URL en vivo: inicia sesión con Auth0, activa el proceso de pago, completa el pago usando la tarjeta de prueba de Stripe (4242 4242 4242 4242; cualquier fecha de caducidad futura; cualquier CVV) y verifica que el webhook se active.
Revisa en el Dashboard de Stripe → Developers → Webhooks → [endpoint] → Events para confirmar la entrega. Una 200 respuesta confirma que el controlador se ejecutó correctamente. Un 400 casi siempre indica un error de análisis del cuerpo o una discrepancia en la firma.
¿Qué causa que los flujos de autenticación y pago fallen en Webflow Cloud?
La mayoría de los fallos se agrupan en cinco categorías: variables de entorno incorrectas o ausentes, discrepancias en la URL de Auth0, orden de acceso al cuerpo en los controladores de webhook, el método de verificación de Stripe equivocado y patrones de ruta v3 residuales.
Aquí tienes el diagnóstico para cada uno.
El inicio de sesión en Auth0 falla con el error "callback URL mismatch"
Causa: O bien APP_BASE_URL en Settings → Variables de Webflow Cloud no coincide con el dominio de producción, o el dominio de producción no se ha añadido al campo Allowed Callback URLs de Auth0.
Solución: Confirma que APP_BASE_URL esté configurado como https://your-domain.com; coincidencia exacta, https:// prefijo, sin barra diagonal al final. Luego abre el Dashboard de Auth0 → Applications → [tu app] → Settings y verifica que https://your-domain.com/auth/callback esté en el campo Allowed Callback URLs.
Ambos deben coincidir exactamente. Auth0 realiza una coincidencia de URL exacta, no por prefijo ni comodín por defecto. Un www. o una diferencia en la barra diagonal final causarán un error de discrepancia.
Los enlaces de inicio y cierre de sesión devuelven un 404
Causa: Casi siempre es un problema de migración de v3 a v4. Auth0 v4 monta las rutas en /auth/*, no en /api/auth/*. Cualquier enlace que apunte a /api/auth/login o /api/auth/logout dará un 404 porque esas rutas ya no existen.
Solución: Busca en todo el código fuente /api/auth/ y reemplaza todas las apariciones por /auth/. Esto incluye enlaces codificados, redirecciones de Next.js, returnTo parámetros y cualquier ruta de API que construya URLs de inicio de sesión mediante programación.
Además, confirma que el middleware.ts matcher no excluya las rutas /auth/*; esas rutas deben pasar por el middleware para funcionar.
El webhook de Stripe lanza el error "Body has already been used"
Causa: El flujo del cuerpo request se consumió antes de llegar al controlador del webhook. Esto sucede cuando el middleware llama a request.json(), request.text(), o request.body en la solicitud entrante antes de que el controlador de ruta la lea. El flujo es destructivo; una vez leído, no se puede volver a leer.
Solución: Confirma que ningún middleware toque el cuerpo de la solicitud en la ruta del webhook. En tu middleware matcher, excluye la ruta del webhook explícitamente si es necesario: añade "/((?!api/webhooks).*)" para evitar que la lógica de sesión o de análisis del cuerpo se ejecute en las solicitudes de webhook.
Luego confirma que await request.text() sea la primera operación en el controlador de ruta.
La verificación de la firma del webhook falla y lanzaconstructEventAsync
Causa: O bien se está usando constructEvent (síncrono) en lugar de constructEventAsync, o STRIPE_WEBHOOK_SECRET es el secreto de desarrollo de Stripe CLI en lugar del secreto de firma del endpoint de producción.
Solución: Confirma que el controlador del webhook use stripe.webhooks.constructEventAsync() con Stripe.createSubtleCryptoProvider() como quinto argumento. constructEvent utiliza operaciones node:crypto síncronas que no funcionan en el entorno de ejecución de Workers; el mensaje de error suele ser "Cannot read properties of undefined" o una excepción relacionada con criptografía.
Además, confirma que STRIPE_WEBHOOK_SECRET en Settings → Variables de Webflow Cloud sea el valor whsec_... del panel de Signing secret del endpoint en el Dashboard de Stripe; no el valor whsec_... mostrado por stripe listen durante el desarrollo local. Son secretos diferentes.
Crea controles de acceso y suscripciones de pago sobre esta base
Esta guía cubrió la integración completa: middleware de Auth0 v4 para autenticación basada en sesiones y Stripe Checkout para cobros, con constructEventAsync verificación de cada webhook en el entorno de ejecución de Workers.
Explora la página de integración de Webflow + Auth0 para ver otros patrones de autenticación, como inicio de sesión social, SSO empresarial y autenticación multifactor. La página de integración de Webflow + Stripe también cubre suscripciones y portales de cliente para cuando un pago único no es suficiente.
Para la parte de plataforma de ambas integraciones, incluyendo variables de entorno, enlaces de almacenamiento y cómo Webflow Cloud compila y despliega una aplicación Next.js, la documentación para desarrolladores de Webflow cubre el entorno donde realmente se ejecutan tus Route Handlers y middleware.
Preguntas frecuentes
¿Qué cambió de Auth0 v3 a v4 para los despliegues en Webflow Cloud?
Auth0 v3 requería un controlador de ruta general en app/api/auth/[auth0]/route.ts usando handleAuth(). En v4, este archivo se elimina por completo; middleware.ts monta automáticamente todas las rutas de autenticación. Las rutas también cambiaron: v4 usa /auth/* en lugar de /api/auth/*. Más allá del cambio estructural, v4 es compatible con el edge por defecto, por lo que cumple con el requisito de middleware exclusivo para edge de Webflow Cloud sin configuración adicional.
¿Por qué Stripe requiereconstructEventAsyncen lugar deconstructEventen Webflow Cloud?
constructEvent usa el módulo síncrono crypto de Node.js para calcular la firma HMAC. La Web Cryptography API de Cloudflare Workers (SubtleCrypto) es asíncrona por diseño; no existe un HMAC síncrono disponible en el entorno de ejecución de Workers. constructEventAsync usa SubtleCrypto en su lugar, y Stripe.createSubtleCryptoProvider() especifica qué proveedor utilizar. Esta es una limitación del propio entorno de ejecución de Cloudflare Workers, no algo específico de Webflow Cloud. Se aplica a cualquier uso de Stripe en un entorno Workers.
¿Puedo añadir suscripciones en lugar de pagos únicos?
Sí. Cambia mode: "payment" por mode: "subscription" en stripe.checkout.sessions.create() y reemplaza el objeto price_data con un campo price que haga referencia a un ID de precio recurrente de tu panel de Stripe. En el controlador de webhooks, añade escuchas para los eventos customer.subscription.created, customer.subscription.updated y customer.subscription.deleted para gestionar el estado de la suscripción.






