Ir al contenido principal

Embedded Hosted Checkout - Pago de COPE directamente en tu página

El embedded hosted checkout permite que tu página mantenga su layout mientras COPE renderiza el formulario de pago en un iFrame. Tu sitio posee la experiencia envolvente. COPE posee la ruta de checkout, el cobro, la finalización de impuestos, la creación de pedidos y todos los estados terminales del comprador.

Usa embedded checkout cuando necesitas un storefront personalizado o un modal de checkout in-page. Usa redirect checkout cuando la integración más simple es suficiente o cuando un navegador bloquea el flujo del iFrame.

📄 Documentación completa: [Embedded hosted checkout - COPE]


Requisitos

Antes de crear sesiones de embedded checkout:

  • Crear una clave publicable para el negocio en COPE

  • Registrar cada origin de página padre que vaya a alojar el iFrame (p. ej. https://shop.example.com)

  • Usar solo el origin: esquema, host y puerto opcional – sin ruta, query string ni fragmento

  • Solo HTTPS en producción (http://localhost solo para desarrollo)

  • Configurar URLs de success y cancel permitidas para completion por redirect o fallback

El origin padre debe coincidir exactamente con el origin del navegador de la página que llama a checkout({ embed_origin }). https://shop.example.com y https://www.shop.example.com son origins distintos.


Integración

ts

import { CopeCart } from "@copecart/sdk"  const cope = new CopeCart({   publishableKey: "cope_pk_live_...", })  async function openEmbeddedCheckout(productId: string, planId: number) {   const cart = await cope.createCart({ currency: "EUR" })    await cope.addLine(cart.id, {     product_id: productId,     plan_id: planId,   })    await cope.setBuyerIdentity(cart.id, {     email: "buyer@example.com",     tax_location: { country: "DE", postal_code: "10115" },   })    await cope.reprice(cart.id)    const checkout = await cope.checkout(cart.id, {     embed_origin: window.location.origin,     success_url: "https://shop.example.com/thank-you",     cancel_url: "https://shop.example.com/cart",     consents: [{ type: "buyer_tos" }],   })    return cope.mountCheckout("#cope-checkout-frame", checkout, {     fallback: "redirect",     onReady: () => showCheckoutFrame(),     onResize: ({ height }) => resizeContainer(height),     onSuccess: () => showOrderConfirmation(),     onCancel: () => closeCheckoutFrame(),     onError: ({ code, retryable }) => reportCheckoutError(code, retryable),   }) }

La respuesta de checkout incluye ambas formas de URL:

Campo

Significado

checkout.checkoutUrl

Ruta de redirect, p. ej. https://app.cope.com/checkout/:token

checkout.embedCheckoutUrl

Ruta de iFrame, p. ej. https://app.cope.com/checkout/embed/:token

checkout.embedOrigin

Origin padre aprobado devuelto por COPE

mountCheckout() rechaza el mount si checkout.embedOrigin falta o no coincide exactamente con window.location.origin.


Comportamiento del mount

mountCheckout() crea el iFrame automáticamente:

  • srccheckout.embedCheckoutUrl

  • titleCOPE checkout

  • allowpayment * para soporte de wallets del navegador

  • referrerPolicyno-referrer

  • width100%

  • min-height720px

  • Sin atributo sandbox

No construyas el iFrame manualmente – el SDK también realiza el trusted mount handshake y filtra todos los mensajes del iFrame por origin, source window, message source y versión.


Fallback

Usa fallback: "redirect" para recuperación segura si el iFrame no completa el trusted ready handshake antes de readyTimeoutMs:

ts

cope.mountCheckout("#cope-checkout-frame", checkout, {   fallback: "redirect",   readyTimeoutMs: 8000,   onFallbackRedirect: () => {     console.log("Cayendo a hosted checkout")   }, })

Con el fallback: "error" por defecto, el SDK llama a:

ts

onError({ code: "load_failed", retryable: true })

Eventos

El iFrame envía eventos saneados al SDK. Los payloads de los callbacks no incluyen credenciales de checkout, payment client secrets ni PII del comprador.

Evento

Significado

onReady

iFrame cargado y listo

onSuccess

Pago completado con éxito

onCancel

Usuario canceló

onError

Error en el proceso de pago (incl. flag retryable)

onResize

Altura del iFrame ha cambiado

Para el stream de eventos saneados en bruto:

ts

cope.mountCheckout("#cope-checkout-frame", checkout, {   onMessage: (event) => {     analytics.track("cope_checkout_embed_event", event)   }, })

Modelo de seguridad

Embedded checkout tiene dos capas de autorización:

  1. COPE valida embed_origin contra la allowlist del negocio al crear la sesión de checkout

  2. La ruta del iFrame valida el mismo contrato de embed antes de renderizar el formulario de pago

El SDK envía el mensaje de mount con un targetOrigin específico – nunca *. Los mensajes del iFrame solo se aceptan cuando event.origin, event.source, event.data.source (cope.checkout) y event.data.version (1) coinciden todos.


Casos de uso

Modal de checkout in-page
El formulario de pago se abre como overlay dentro de tu página. Sin redirect, sin pérdida de contexto – el usuario permanece en tu navegación y layout durante todo el checkout.

Flujo de upgrade SaaS
Un usuario hace upgrade de free a pro directamente en el dashboard. mountCheckout() renderiza el formulario de pago como iFrame en un modal o sección dedicada, sin perder el estado actual.

Storefront personalizado
Construyes una página de producto completamente propia con tu branding – COPE solo renderiza el formulario de pago dentro del iFrame. Tu página mantiene control total sobre layout, colores y experiencia de usuario.

Checkout de alta conversión
Cada redirect es un punto de abandono potencial. Embedded checkout elimina esa fricción y mantiene al usuario en tu página hasta el pago exitoso.


Troubleshooting

Error

Causa probable

Solución

embed_origin_not_allowed

Origin no registrado

Registrar el origin exacto, crear nueva sesión

mountCheckout requires checkout.embedOrigin

checkout() llamado sin embed_origin

Pasar embed_origin: window.location.origin

Checkout embed origin ... does not match

Sesión creada para otro origin

Llamar checkout() y mountCheckout() desde el mismo origin

iFrame nunca listo

Token expirado, bloqueado o handshake fallido

Usar fallback: "redirect", revisar onError y consola

Wallet del navegador no disponible

Registro de dominio de wallet incompleto

Verificar allow="payment *" y setup de wallet para ambos origins

¿Ha quedado contestada tu pregunta?