Un botón en el Designer que enlace a una pequeña aplicación de pago en Webflow Cloud permite que el servidor establezca el precio y procese el pago de PayPal directamente en tu propio dominio.
El generador de botones de PayPal te ofrece un fragmento de código que incluye el precio en el HTML de tu página y completa el pago en la plataforma de PayPal. Esto funciona para un artículo con precio fijo. Cuando necesitas que el servidor establezca el precio y confirme el pago en tu propio dominio, el botón requiere una página respaldada por un servidor.
Webflow Cloud te proporciona esa página sin que tengas que salir de tu sitio. En esta guía, crearemos una pequeña aplicación de Next.js montada en /checkout que establece el precio en el servidor y captura el pago cuando el comprador regresa de PayPal.
¿Qué necesitas para vincular un botón de PayPal a una página de pago en Webflow?
Necesitas cumplir cinco requisitos previos antes de empezar:
- Sitio de Webflow: Un sitio existente con una página de marketing publicada que contenga el botón de pago; el plan Premium o superior solo es necesario para el montaje en un dominio personalizado.
- Credenciales REST de PayPal: El ID de cliente y el secreto de cliente de una aplicación REST de PayPal obtenidos desde el panel de desarrollador de PayPal, tanto para las versiones de prueba (sandbox) como para las reales.
- Node.js y npm: Ambos instalados localmente. Webflow Cloud solo admite npm, por lo que no se pueden usar los comandos pnpm ni yarn en esta plataforma.
- Proyecto Next.js 15+: Un proyecto de Next.js 15 o superior en un repositorio que puedas conectar a Webflow Cloud; puedes generar uno con create-next-app.
- Cuenta de comprador de prueba (sandbox): Una cuenta de comprador de prueba en el entorno sandbox de PayPal para que puedas completar un pago de principio a fin sin usar una tarjeta real.
Las llamadas a PayPal residen en el código del servidor y el botón del Designer enlaza a la aplicación terminada.
6 pasos para vincular un botón de PayPal a una página de pago en Webflow Cloud
Esta construcción de seis pasos utiliza un Route Handler para establecer el importe evitando ediciones desde el navegador, crea y captura el pedido de PayPal desde el servidor, y vincula el botón del Designer a la aplicación montada en /checkout.
Se ejecuta en una aplicación de Next.js montada en Webflow Cloud en /checkout, y el cambio en el Designer consiste simplemente en un enlace a esa ruta.
1. Prepara la estructura de la aplicación Next.js y configura las variables de PayPal localmente
Crea un proyecto nuevo de Next.js con npm y el App Router, luego asígnale los valores de PayPal necesarios mediante variables de entorno para mantener las credenciales fuera del código fuente. Mantengo la aplicación de pago en su propio repositorio para que la compilación no tenga nada más que instalar.
Ejecuta la creación de la estructura desde el directorio donde quieras alojar el proyecto:
npx create-next-app@15 checkout
cd checkout
La etiqueta @15 fija la estructura a la versión principal de Next.js 15 compatible; esta configuración funciona en Next.js 15 o superior.
Acepta TypeScript, el App Router y el alias de importación predeterminado @/* cuando se te solicite, y rechaza el directorio src/; los archivos posteriores importan desde @/lib/paypal y dependen de ese alias. Deja next.config.ts como está.
La documentación de configuración de Webflow Cloud indica: "No necesitas añadir un adaptador, una ruta base ni un modo de salida", porque Webflow Cloud inyecta la ruta base durante la compilación.
Configuración de variables de entorno para el desarrollo local
Seis variables van en un archivo .env.local en la raíz del proyecto, con valores de entorno de pruebas (sandbox) por ahora:
| Variable | Valor para desarrollo local | ¿Secreto? |
|---|---|---|
| PAYPAL_API_BASE | https://api-m.sandbox.paypal.com |
No |
| PAYPAL_CLIENT_ID | El ID de cliente de tu aplicación sandbox | No |
| PAYPAL_CLIENT_SECRET | El secreto de cliente de tu aplicación sandbox | Sí |
| PAYPAL_RETURN_URL | http://localhost:3000/return | No |
| PAYPAL_CANCEL_URL | http://localhost:3000/cancel | No |
| NEXT_PUBLIC_BASE_PATH | Déjalo vacío | No |
| Variable → Valor para desarrollo local → ¿Secreto? |
|---|
| PAYPAL_API_BASE |
https://api-m.sandbox.paypal.com |
| No |
| PAYPAL_CLIENT_ID |
| El ID de cliente de tu aplicación sandbox |
| No |
| PAYPAL_CLIENT_SECRET |
| El secreto de cliente de tu aplicación sandbox |
| Sí |
| PAYPAL_RETURN_URL |
| http://localhost:3000/return |
| No |
| PAYPAL_CANCEL_URL |
| http://localhost:3000/cancel |
| No |
| NEXT_PUBLIC_BASE_PATH |
| Déjalo vacío |
| No |
NEXT_PUBLIC_BASE_PATH se mantiene vacío localmente porque next dev sirve la aplicación en la raíz; en Webflow Cloud se convierte en /checkout. Al ejecutar npm run dev ahora, se sirve la página de inicio en localhost:3000.
2. Escribe el cliente de PayPal y el Route Handler de pedidos
He puesto todas las llamadas a PayPal en un único módulo de servidor y luego expongo una sola ruta POST que crea un pedido y devuelve su enlace de aprobación. El módulo obtiene un token de acceso con el ID y el secreto de cliente, crea un pedido, lee el estado de un pedido y captura uno; el Route Handler solo llama a los dos primeros y establece el importe por sí mismo.
El módulo del servidor es lib/paypal.ts:
type PayPalConfig = { base: string; clientId: string; clientSecret: string };
function config(): PayPalConfig {
const base = process.env.PAYPAL_API_BASE;
const clientId = process.env.PAYPAL_CLIENT_ID;
const clientSecret = process.env.PAYPAL_CLIENT_SECRET;
if (!base || !clientId || !clientSecret) {
throw new Error("PayPal environment variables are missing");
}
return { base, clientId, clientSecret };
}
export async function getAccessToken(): Promise<string> {
const { base, clientId, clientSecret } = config();
const credentials = btoa(`${clientId}:${clientSecret}`);
const res = await fetch(`${base}/v1/oauth2/token`, {
method: "POST",
headers: {
Authorization: `Basic ${credentials}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: "grant_type=client_credentials",
});
if (!res.ok) {
throw new Error(`PayPal token request failed: ${res.status}`);
}
const data = (await res.json()) as { access_token: string };
return data.access_token;
}
export async function createOrder(amount: string, currency: string) {
const { base } = config();
const token = await getAccessToken();
const res = await fetch(`${base}/v2/checkout/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
intent: "CAPTURE",
purchase_units: [{ amount: { currency_code: currency, value: amount } }],
payment_source: {
paypal: {
experience_context: {
return_url: process.env.PAYPAL_RETURN_URL,
cancel_url: process.env.PAYPAL_CANCEL_URL,
user_action: "PAY_NOW",
},
},
},
}),
});
if (!res.ok) {
throw new Error(`PayPal order creation failed: ${res.status}`);
}
return (await res.json()) as {
id: string;
links: { rel: string; href: string }[];
};
}
export type PayPalOrder = {
id: string;
status: string;
purchase_units?: { payments?: { captures?: { id: string; status: string }[] } }[];
};
export async function captureOrder(orderId: string) {
const { base } = config();
const token = await getAccessToken();
const res = await fetch(`${base}/v2/checkout/orders/${orderId}/capture`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Prefer: "return=representation",
"PayPal-Request-Id": `capture-${orderId}`,
},
});
if (!res.ok) {
throw new Error(`PayPal capture failed: ${res.status}`);
}
return (await res.json()) as PayPalOrder;
}
export async function getOrder(orderId: string) {
const { base } = config();
const token = await getAccessToken();
const res = await fetch(`${base}/v2/checkout/orders/${orderId}`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Prefer: "return=representation",
},
});
if (!res.ok) {
throw new Error(`PayPal order lookup failed: ${res.status}`);
}
return (await res.json()) as PayPalOrder;
}
Construyo la credencial básica con btoa en lugar de usar un asistente específico de Node. Las variables de entorno se leen dentro de config() cuando se ejecuta la función, por lo que si falta un valor, se produce el error legible definido allí en lugar de fallar al importar.
El cuerpo de la orden envía el propósito y el monto, además de las URL de retorno y cancelación dentro de , lo que reemplaza al obsoleto en Orders v2.payment_source.paypal.experience_contextapplication_context
Gestión de la aprobación de órdenes de PayPal y la idempotencia de captura
Con esa fuente de pago configurada, PayPal etiqueta el enlace de aprobación , por lo que la ruta acepta cualquiera de las dos etiquetas. La llamada de captura envía un encabezado , que PayPal utiliza para la idempotencia, de modo que una captura repetida de la misma orden devuelve el primer resultado en lugar de fallar.payer-actionPayPal-Request-Id
Ambas búsquedas solicitan para que la respuesta incluya los detalles de la captura.return=representation
Añade el manejador de rutas en app/api/paypal/order/route.ts:
import { NextResponse } from "next/server";
import { createOrder } from "@/lib/paypal";
const PRICE = "20.00";
const CURRENCY = "USD";
export async function POST() {
try {
const order = await createOrder(PRICE, CURRENCY);
const approve = order.links.find(
(link) => link.rel === "payer-action" || link.rel === "approve"
);
if (!approve) {
return NextResponse.json(
{ error: "PayPal returned no approval link" },
{ status: 502 }
);
}
return NextResponse.json({ orderId: order.id, approveUrl: approve.href });
} catch (error) {
console.error(error);
return NextResponse.json({ error: "Could not create order" }, { status: 500 });
}
}
PRECIO y MONEDA son constantes del servidor a propósito; cualquier cosa que envíe el navegador puede ser editada, por lo que el monto nunca proviene del cuerpo de la solicitud. No añadas export const runtime = 'edge' a este archivo; el adaptador de OpenNext Cloudflare que utiliza Webflow Cloud para las compilaciones no admite ese destino.
¿Puede alguien fuera de tu sitio llamar directamente a la ruta de la orden?
Sí. es pública, por lo que cualquiera puede crear órdenes de $20.00 en un bucle; el precio es fijo, pero el volumen no. La página de retorno captura cualquier token de orden que reciba.POST /checkout/api/paypal/order
Antes de pasar a producción, verifica el encabezado Origin, limita la tasa de peticiones de la ruta de la orden y vincula cada ID de orden a una sesión de pago del lado del servidor antes de capturarla. nunca sale del servidor.PAYPAL_CLIENT_SECRET
Con npm run dev en ejecución, una solicitud POST a localhost:3000/api/paypal/order ahora devuelve un JSON que contiene orderId y una approveUrl de PayPal.
3. Crea la página de pago y el botón de PayPal
Renderiza la página de pago como un componente de servidor y mantén el botón en un pequeño componente de cliente que llame a tu manejador de rutas y luego envíe al comprador a la URL de aprobación.
Una solicitud fetch en producción debe incluir la ruta base. La página "bring-your-own-app" en la documentación de Webflow Cloud especifica claramente que "las llamadas fetch del lado del cliente deben incluir manualmente la ruta base". Debido a que el botón mantiene el estado del navegador, es un componente de cliente.
Guárdalo como app/components/PayPalButton.tsx:
"use client";
import { useState } from "react";
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
export default function PayPalButton() {
const [pending, setPending] = useState(false);
const [error, setError] = useState<string | null>(null);
async function startCheckout() {
setPending(true);
setError(null);
try {
const res = await fetch(`${basePath}/api/paypal/order`, { method: "POST" });
if (!res.ok) {
setError("Checkout could not start. Please try again.");
return;
}
const data = (await res.json()) as { approveUrl: string };
window.location.assign(data.approveUrl);
} catch {
setError("Checkout could not start. Please try again.");
} finally {
setPending(false);
}
}
return (
<div>
<button onClick={startCheckout} disabled={pending}>
{pending ? "Redirecting to PayPal..." : "Pay with PayPal"}
</button>
{error && <p role="alert">{error}</p>}
</div>
);
}
Añadir el prefijo NEXT_PUBLIC_BASE_PATH hace que el mismo código acceda a /api/paypal/order localmente y a /checkout/api/paypal/order en producción.
Creación del componente principal de la página de pago
Reemplaza el archivo app/page.tsx generado con la página de pago propiamente dicha:
import PayPalButton from "./components/PayPalButton";
export default function CheckoutPage() {
return (
<main>
<h1>Checkout</h1>
<p>Workshop seat: $20.00 USD</p>
<PayPalButton />
</main>
);
}
El precio mostrado es solo texto. El manejador de rutas decide de forma independiente qué valor enviar en la solicitud de la orden. Al hacer clic en Pagar con PayPal en localhost:3000, se crea la orden y se redirige el navegador a la approveUrl devuelta por PayPal.
4. Capturar el pago al regresar
Cuando PayPal redirige al comprador, el servidor busca el pedido y llama a captureOrder, a menos que el estado devuelto ya sea COMPLETED, todo esto antes de renderizar nada. La página de retorno lee el parámetro de consulta del token y el componente del servidor llama directamente a getOrder y captureOrder. El manejo de la ruta base solo se aplica a las peticiones del navegador.
La página de retorno, app/return/page.tsx, realiza la comprobación y la captura en el servidor:
import { captureOrder, getOrder } from "@/lib/paypal";
export default async function ReturnPage({
searchParams,
}: {
searchParams: Promise<{ token?: string }>;
}) {
const { token } = await searchParams;
if (!token) {
return (
<main>
<h1>Missing order reference</h1>
</main>
);
}
try {
const order = await getOrder(token);
const result =
order.status === "COMPLETED" ? order : await captureOrder(token);
const capture = result.purchase_units?.[0]?.payments?.captures?.[0];
const paid = capture?.status === "COMPLETED";
return (
<main>
<h1>{paid ? "Payment received" : "Payment pending"}</h1>
<p>PayPal order {result.id}</p>
</main>
);
} catch (error) {
console.error(error);
return (
<main>
<h1>We could not confirm your payment</h1>
</main>
);
}
}
En esta implementación, searchParams es una promesa, de ahí el uso de await.
El pago recibido depende del estado de la propia captura, no del pedido, porque una captura puede estar PENDING pendiente mientras PayPal la revisa; en ese caso, el comprador verá "Pago pendiente".
Un comprador que recarga /checkout/return después de pagar vuelve a ver "Pago recibido", y la PayPal-Request-Id cabecera evita que dos cargas simultáneas realicen la captura dos veces.
Creación de la página de cancelación del proceso de pago
Añade una página de cancelación sencilla en app/cancel/page.tsx para la URL de cancelación:
export default function CancelPage() {
return (
<main>
<h1>Payment cancelled</h1>
</main>
);
}
Al aprobar un pago en el entorno de pruebas (sandbox) y regresar, ahora se muestra "Pago recibido" junto con el ID del pedido de PayPal en pantalla.
5. Montar la aplicación en Webflow Cloud y añadir las mismas variables
Envía el repositorio para que Webflow Cloud pueda compilarlo. En la configuración de Webflow Cloud del sitio, crea un proyecto, conéctalo al repositorio y establece la ruta de montaje del entorno en /checkout antes del primer despliegue.
La ruta de montaje debe establecerse primero porque Webflow Cloud inyecta la ruta base durante la compilación, y NEXT_PUBLIC_BASE_PATH debe coincidir con ella. Elige una ruta de montaje que no esté en uso para evitar conflictos de enrutamiento con una página de Webflow existente.
Si el sitio ya tiene una página en /checkout, móntala en algo como /pay y utiliza ese valor para NEXT_PUBLIC_BASE_PATH y el enlace del Diseñador.
Configuración de las variables de despliegue de Webflow Cloud
Añade las seis variables al entorno con los valores de despliegue:
- PAYPAL_API_BASE: Mantén la URL base del entorno de pruebas para el primer despliegue; cámbiala a la base real,
https://api-m.paypal.com, solo después de que un pago de prueba se complete de principio a fin. - PAYPAL_CLIENT_ID y PAYPAL_CLIENT_SECRET: Introduce el par que coincida con la URL base y marca el secreto como tal para que Webflow Cloud lo oculte en los registros de compilación.
- PAYPAL_RETURN_URL y PAYPAL_CANCEL_URL: Utiliza URLs absolutas en el dominio desplegado, por ejemplo https://your-site.webflow.io/checkout/return y https://your-site.webflow.io/checkout/cancel.
- NEXT_PUBLIC_BASE_PATH: Establécela en /checkout, coincidiendo exactamente con la ruta de montaje, para que la petición del cliente se resuelva en el manejador de rutas en producción.
La página de variables de entorno de Webflow Cloud indica que "tanto las variables de entorno secretas como las no secretas están disponibles para el proceso de compilación de tu aplicación y para la aplicación desplegada en tiempo de ejecución", razón por la cual un valor NEXT_PUBLIC_, integrado en la compilación, y un secreto leído en tiempo de solicitud pueden compartir el mismo panel.
Una vez guardados los seis, despliega el entorno. Cuando termine la compilación, https://your-site.webflow.io/checkout mostrará la página de pago y el botón enviará a un comprador de prueba a PayPal y lo devolverá a /checkout/return con el mensaje de pago recibido.
6. Vincula el botón del Designer a /checkout
Apunta un botón normal o un bloque de enlace en la página de marketing hacia /checkout y publica. Selecciona el elemento en el Designer, abre sus ajustes de enlace, elige un enlace URL e introduce /checkout; una ruta relativa debería resolverse según el dominio que sirva la página, por lo que el mismo enlace funcionará tanto en webflow.io como en un dominio personalizado.
Dale al botón el estilo que mejor le siente a tu marca; como el comprador aprueba el pago en la propia página de PayPal, el botón del sitio solo tiene que dejar claro qué pasará después. Etiqueta el botón con el precio y la acción, para que la cifra que aparece en él coincida con la de la página de pago.
Publica el sitio. La página publicada ahora incluye un botón que abre /checkout en el mismo dominio, y un comprador de prueba puede ir desde ese botón hasta completar un pago en PayPal y regresar. El comprador solo abandona tu dominio para ir a la página de aprobación de PayPal.
¿Por qué falla una página de pago de PayPal en Webflow Cloud?
Los fallos suelen deberse a cuatro motivos: un destino de tiempo de ejecución (runtime) de Route Handler no compatible, la falta de una ruta base en el lado del cliente, discrepancias entre las credenciales de PayPal y las URLs base de la API, o un plan de sitio que no permite montar la aplicación en un dominio personalizado.
Empieza revisando la salida de la compilación para fallos de despliegue; para fallos en tiempo de ejecución, inspecciona el navegador y los registros de la aplicación.
Una compilación de Webflow Cloud falla en un Route Handler que funciona con next dev
Causa: El Route Handler contiene export const runtime = 'edge', y Webflow Cloud despliega Next.js a través del adaptador OpenNext Cloudflare, que no es compatible con ese destino de tiempo de ejecución edge de Next.js.
La documentación utiliza la palabra "edge" con dos sentidos en la misma página. Webflow Cloud se ejecuta en Cloudflare Workers, que es una plataforma edge, y la página de "bring-your-own-app" describe el despliegue en el tiempo de ejecución Edge para alojamiento distribuido globalmente; esa misma página te indica que añadas la directiva a las rutas de la API.
Un desarrollador que sigue esa instrucción interpreta la frase sobre la plataforma como un requisito de tiempo de ejecución y genera una compilación que no funciona.
Solución: Haz que cada Route Handler y página se compile a través del mismo adaptador que el resto de la aplicación eliminando la exportación.
El middleware es el único lugar donde la documentación de Webflow Cloud indica que el código de tiempo de ejecución Edge funciona: middleware.ts se ejecuta en el tiempo de ejecución de Workers, y la página de personalización del framework señala que "el middleware de tiempo de ejecución de Node.js no es compatible. Solo el middleware de tiempo de ejecución Edge funciona en el tiempo de ejecución de Workers".
Si en su lugar utilizaste create-next-app@latest y obtuviste Next 16, mantén el archivo con el nombre middleware.ts en lugar de renombrarlo a proxy.ts, ya que proxy se ejecuta en el tiempo de ejecución de Node y no puede optar por Edge, por lo que el archivo renombrado no podrá ejecutarse en Webflow Cloud.
El botón "Pagar con PayPal" devuelve un error 404 para /api/paypal/order en el sitio en vivo, pero funciona con next dev
Causa: La ruta base se inyecta en el momento de la compilación, lo que cubre el enrutamiento del lado del servidor, pero la petición fetch del navegador no se reescribe. Localmente, la aplicación se encuentra en la raíz, por lo que /api/paypal/order se resuelve correctamente.
En el sitio en vivo, la aplicación está montada en /checkout, y una petición a /api/paypal/order le pide al propio sitio de Webflow una ruta que solo existe bajo la ruta de montaje. La solicitud suele devolver un 404 en lugar del JSON del pedido.
Solución: Abre el panel de red del navegador y lee la URL de la solicitud; si le falta /checkout, es que NEXT_PUBLIC_BASE_PATH no está configurado en el entorno Cloud o tiene un valor que no coincide con la ruta de montaje.
Configúralo exactamente como /checkout, con la barra inclinada inicial y sin ninguna al final, y vuelve a desplegar para que el paquete del cliente se reconstruya con el nuevo valor de NEXT_PUBLIC_BASE_PATH.
No intentes solucionar esto añadiendo basePath a next.config.ts; la documentación de configuración indica que no es necesario y el valor inyectado ya gestiona el lado del servidor.
El pago con PayPal muestra 'Checkout could not start' en Webflow Cloud, aunque el mismo código funciona localmente.
Causa: El punto de conexión de tokens de PayPal rechaza la solicitud cuando las credenciales no coinciden con la URL base con la que están vinculadas. El Route Handler captura ese error, por lo que el navegador recibe un 500 y el estado de error de la solicitud del token solo aparece en los registros de la aplicación.
La función config() lanza su propio error de "variables de entorno faltantes" cuando falta un valor, por lo que un rechazo de token significa que PayPal rechazó un ID de cliente y una clave secreta que sí existían; una posible discrepancia es usar credenciales de sandbox con la base de la API real, o credenciales reales con la base de sandbox, después de que uno de los tres valores fuera modificado en el entorno de Webflow Cloud y los otros dos no. Webflow Cloud oculta las claves secretas en los registros de compilación, por lo que el registro no te mostrará el valor almacenado.
Solución: Para restaurar el proceso de pago, vuelve a introducir PAYPAL_API_BASE, PAYPAL_CLIENT_ID y PAYPAL_CLIENT_SECRET juntos, utilizando el conjunto de sandbox o real correspondiente desde el panel de desarrollador de PayPal, luego guarda y vuelve a desplegar. Registra el estado de res. desde getAccessToken() mientras diagnosticas; nunca la credencial ni el token.
/checkout no es accesible en el dominio personalizado, aunque funciona en webflow.io.
Causa: Montar una aplicación de Webflow Cloud en un dominio personalizado requiere un plan de sitio Premium o superior, mientras que Webflow Cloud está incluido desde el plan gratuito Starter. En un plan inferior, la aplicación no puede montarse en el dominio personalizado.
Solución: Cambia el sitio a un plan Premium o superior y monta el entorno en el dominio personalizado. Hasta que cambies de plan, prueba el flujo completo en webflow.io, con PAYPAL_RETURN_URL y PAYPAL_CANCEL_URL configurados para ese dominio.
Cuando el dominio personalizado esté activo, actualiza ambas URLs a ese dominio y despliega; de lo contrario, PayPal enviará a los compradores a la dirección de pruebas después de pagar. El enlace del Designer sigue siendo /checkout en todo momento, ya que una ruta relativa sigue al dominio desde el que se sirve la página.
Qué puedes construir a continuación con PayPal y Webflow
Para permitir que los especialistas en marketing establezcan el precio, sustituye la constante PRICE en el Route Handler por una búsqueda en el servidor en una colección del CMS a través de la API de CMS de Webflow. Así podrás cambiar el precio en el CMS en lugar de en el código.
También añadiría un webhook de PayPal que verifique la firma de PayPal y capture el pedido en , para que a un comprador que aprueba y luego cierra la pestaña se le siga cobrando y registrando. Si solo necesitas un botón de precio fijo y no quieres usar un servidor, la CHECKOUT.ORDER.APPROVEDintegración de Webflow con PayPal es la ruta más rápida.
Si prefieres aceptar tarjetas a través de Stripe sin ejecutar un servidor, Stripe Checkout cubre esa opción. Para la búsqueda de precios, la documentación para desarrolladores de Webflow cubre las APIs del CMS que tendrías que llamar.
Preguntas frecuentes
¿Qué ven tus compradores si cancelan en PayPal?
PayPal envía a tus compradores a PAYPAL_CANCEL_URL, que es /checkout/cancel en tu sitio desplegado, y verán la página de Pago cancelado. Si cambian de opinión, al hacer clic de nuevo en tu botón del Designer se envía un nuevo POST a tu Route Handler, que crea un pedido de PayPal totalmente nuevo y comienza la aprobación desde el principio.
¿Puede tu página de pago vender más de un producto o moneda?
Esta implementación puede vender varios productos o monedas si el navegador envía solo un ID de producto. Tu Route Handler asigna ese ID a un precio y una moneda en una tabla del servidor y pasa ambos a createOrder, cuyo argumento de moneda rellena currency_code. Extrae el texto que se muestra en tu página de la misma tabla, para que el precio que leen los compradores coincida con lo que cobra PayPal.
¿Qué sucede si tu comprador aprueba el pago pero cierra la pestaña antes de regresar?
No se cobra nada. Solo app/return/page.tsx llama a captureOrder, por lo que un pedido aprobado que nunca llega a esa página permanece sin capturar, y PayPal lo deja caducar después de su ventana de aprobación. El oyente de webhooks descrito anteriormente cierra esa brecha capturando el pago en el momento de la aprobación.





