Integrar los pagos de Clip en Node: Checkout Transparente vs Redireccionado y cargos con Card Token (2026) — Cesar Ayala
← Todos los artículos

Integrar los pagos de Clip en Node: Checkout Transparente vs Redireccionado y cargos con Card Token (2026)

Clip ofrece tres modos de checkout: Transparente (el cliente sigue en tu sitio, tokenizas la tarjeta y cobras por API — requiere PCI AoC salvo que uses el SDK de Clip), Redireccionado (un link de pago alojado) y Embebido. El flujo Transparente es Card Token (generatecardtoken, token de 15 minutos) y luego la Payments API (realizarpago) con el token, el monto y "currency":"MXN".

Los tres modos de checkout de Clip y cómo elegir (Transparente vs Redireccionado vs Embebido)

Clip expone tres modos de checkout para pagos en línea. Transparente: el cliente nunca sale de tu sitio, tú tokenizas la tarjeta y cobras por API — requiere una certificación PCI AoC válida si tu servidor toca datos de tarjeta, salvo que uses el SDK de Clip, que tokeniza sin que tú toques el PAN. Redireccionado: creas un link de pago, mandas al cliente a la página alojada de Clip y regresa a tu tienda. Embebido: el checkout de Clip incrustado dentro de tu página. El flujo Transparente son dos llamadas: generatecardtoken (Card Token API, el token vive 15 minutos) y luego realizarpago (Payments API) con el token, el monto y "currency": "MXN".

¿Cómo elegir? Si quieres control total del UI y ya tienes el SDK, ve por Transparente. Si quieres la integración de menor esfuerzo y no te importa mandar al cliente fuera, el Redireccionado es un link y listo. El Embebido es el punto medio. Este post completa el cuarteto de pasarelas junto a Stripe, Mercado Pago y Conekta: la misma disciplina de ingeniería, otra API.

Transparente vs Redireccionado: control de UI vs esfuerzo de integración

Transparente (API)

  • El cliente se queda en tu sitio
  • Tokenizas con generatecardtoken (15 min)
  • Cobras con realizarpago (Payments API)
  • Con el SDK NO necesitas PCI AoC; sin él, sí

Redireccionado (link de pago)

  • Creas un link de pago alojado
  • El cliente paga en la página de Clip
  • Regresa a tu tienda al terminar
  • Menor esfuerzo, cero manejo de PAN
El SDK de Clip te quita el PCI AoC de encima en el flujo Transparente. El Redireccionado es la ruta de menor esfuerzo.

¿Tu servidor toca datos de tarjeta? La pregunta del PCI AoC que el SDK te quita de encima

Esta es la decisión que define tu arquitectura, no un detalle legal para después. La regla es simple: si tu servidor toca el número de tarjeta (PAN), estás dentro del alcance de PCI DSS y necesitas una certificación PCI AoC (Attestation of Compliance) válida. Eso es papeleo, auditorías y costo real. La mayoría de los equipos no lo quiere y no lo necesita.

La salida es el SDK de Clip: tokeniza la tarjeta en el cliente, sin que el PAN pase jamás por tu backend. Con el SDK, el dato sensible viaja de Clip al cliente y de vuelta a Clip; tú solo manejas un Card Token ID opaco. Por eso con el SDK no necesitas certificación PCI. Es exactamente la misma lógica que aplica en Conekta con Conekta.js o en Stripe con Stripe.js: el navegador tokeniza, tu servidor nunca ve el plástico. Si vas por Transparente, usa el SDK. No hay razón práctica para meter tu servidor al alcance de PCI en un negocio normal.

Flujo Transparente, paso 1: captura la tarjeta con el SDK de Clip para obtener un Card Token ID (expira en 15 min)

El primer paso ocurre en el cliente: el SDK de Clip captura la tarjeta y llama a generatecardtoken, que devuelve un Card Token ID con expiración de 15 minutos. Ese token es lo único que tu backend recibe. La forma exacta del request del SDK cámbiala según su versión — verifica siempre contra developer.clip.mx/reference; aquí lo trato como ilustrativo del contrato:

// Cliente (navegador): el SDK de Clip captura la tarjeta y genera el token.
// El PAN nunca toca tu backend -> por eso no necesitas PCI AoC.
const cardToken = await clip.generatecardtoken({
  card: {
    // los campos los llena el SDK desde el formulario seguro de Clip
    number: '****',
    expiration_month: '12',
    expiration_year: '28',
    cvc: '***'
  }
})

// cardToken.id -> Card Token ID, válido 15 minutos.
// Mándalo a TU servidor para el cargo.
await fetch('/api/clip/charge', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ card_token_id: cardToken.id, order_ref: 'sub_1042' })
})

Los 15 minutos son un límite duro y son suficientes para cobrar de inmediato, pero no para guardar el token y cobrar mañana. Si necesitas cargos recurrentes, ese es otro mecanismo — no reutilices un Card Token ID vencido. Trata el token como de un solo uso y de corta vida.

Flujo Transparente, paso 2: cobra con la Payments API (realizarpago) — token + monto + “currency”:“MXN”

Con el Card Token ID en tu servidor, cobras con realizarpago (la Payments API, es decir POST /payments). El cuerpo lleva el token, el monto y "currency": "MXN". Este es el punto donde tu backend se involucra, y donde pones tu llave de idempotencia:

// Servidor: cobra con el Card Token ID recibido del cliente.
async function chargeWithClip({ cardTokenId, amount, orderRef }) {
  // Los card tokens se crean en api-secure.payclip.com; el cobro va a api.payclip.com. Confirma ambos en la referencia.
  const auth = Buffer.from(`${process.env.CLIP_API_KEY}:`).toString('base64')
  const res = await fetch('https://api.payclip.com/payments', {
    method: 'POST',
    headers: {
      // La referencia de Clip usa Basic auth (Base64 de tu API key). Confírmalo en developer.clip.mx/reference
      'Authorization': `Basic ${auth}`,
      'Content-Type': 'application/json',
      // idempotency key: un reintento no debe generar un segundo cargo real.
      // Confirma el nombre del header en la referencia de Clip.
      'x-idempotency-key': orderRef
    },
    body: JSON.stringify({
      card_token_id: cardTokenId,
      amount,                 // monto del cargo
      currency: 'MXN'         // exacto, en mayúsculas
    })
  })

  if (!res.ok) {
    // no asumas éxito: loguea, no entregues producto
    throw new Error(`Clip charge failed: ${res.status}`)
  }
  return res.json()
}

Fíjate en currency: 'MXN' tal cual, y en la llave de idempotencia. Como Clip no tiene sandbox, cada request que mandas aquí es real: sin idempotencia, un reintento de red te cobra dos veces al cliente. Ese detalle importa más en Clip que en pasarelas con ambiente de prueba, y lo desarrollo en el post hermano sobre probar sin sandbox.

Flujo Transparente de Clip, de la tarjeta al cargo confirmado

  1. SDK captura la tarjetaEn el navegador; el PAN nunca toca tu backend
  2. generatecardtokenDevuelve Card Token ID; vive 15 minutos
  3. Manda el token a tu servidorSolo el token opaco + tu referencia de orden
  4. realizarpago (POST /payments)card_token_id + amount + currency: MXN + idempotency key
  5. Consulta el pagoConsultar un Pago da el estatus real; no confíes en el cliente
  6. EntregaSolo con estatus confirmado por API. Idempotente por referencia
Dos llamadas: generatecardtoken en el cliente, realizarpago en el servidor. El estatus real se consulta, no se asume.

Autenticar la petición: el header Authorization con Basic auth (verifícalo en la referencia)

Todas las peticiones a la API de Clip llevan un token de autenticación (tu API key) en un header Authorization. La referencia de Clip usa Basic auth — el Base64 de tu key — no el Bearer que Stripe te acostumbró a teclear por reflejo. Verifícalo contra developer.clip.mx/reference antes de escribir el cliente y no copies el patrón de otra pasarela; confundir los dos te devuelve un 401 seco. En el código de arriba usé Basic con el Base64 de la key; confirma el formato exacto en la referencia.

Y lo obvio que se olvida: la API key es de producción y cobra dinero real. Guárdala en tu gestor de secretos, nunca en el repo, nunca en el bundle del cliente. La única credencial que toca el navegador es la del SDK para tokenizar; la key que cobra vive solo en el servidor.

Procesar el webhook: id / origin / event_type, luego consulta “Consultar un Pago” para el estatus real

Clip manda una notificación webhook cuando cambia el estatus de un pago. Una forma de notificación verificada trae campos como id, origin y event_type — por ejemplo {"id":"pinpad-...","origin":"pinpad-payments-api","event_type":"PINPAD_INTENT_STATUS_CHANGED"}. La notificación es una señal de “algo cambió”, no la verdad del pago. Para obtener el estatus final, consulta el endpoint “Consultar un Pago” (Get Payment). Este es el mismo patrón de webhooks vs polling: el webhook te despierta, la consulta te da la verdad.

// Recibe la notificación, NO confíes en su payload como estatus final.
app.post('/webhooks/clip', express.json(), async (req, res) => {
  const { id, origin, event_type } = req.body

  // 1) verifica autenticidad de la notificación (ver siguiente sección)
  // 2) responde rápido para no gatillar reintentos
  res.sendStatus(200)

  // 3) consulta el estatus REAL con "Consultar un Pago" (Get Payment)
  const payment = await getClipPayment(id) // GET del pago contra la API de Clip
  // el estatus real vive en la respuesta de la API, no en event_type
  await reconcile(payment)
})

No inventes enums de estatus a partir del event_type. El nombre del evento te dice que hubo un cambio; el estado del pago lo dictamina la respuesta de “Consultar un Pago”. Verifica los nombres de campo de esa respuesta contra la referencia.

Nunca confíes solo en la notificación: verificación de autenticidad, llaves de idempotencia y cero enums de estatus inventados

Aquí es donde la ingeniería de pasarelas se paga sola, y es idéntica a lo que ya haces con Stripe, Mercado Pago y Conekta. Tres reglas no negociables.

Verifica la autenticidad de cada webhook. Un endpoint público que otorga producto es un blanco. Confirma que la notificación viene de Clip antes de actuar — el mecanismo de firma/verificación, verifícalo en la referencia. El principio de fondo (verificar contra el raw body, rechazar lo que no valide) es el mismo que detallo en verificación de firma de webhook de Stripe.

Usa llaves de idempotencia en el cargo y deduplica el webhook por su id. Las notificaciones se reintentan; sin dedupe entregas dos veces. Los cargos se reintentan; sin idempotencia cobras dos veces.

Cero enums inventados. No hardcodees strings de estatus que “supones”. El estatus real sale de “Consultar un Pago”, con nombres de campo confirmados en la doc.

Reglas de confiabilidad para la integración de Clip

Webhook = señalid / origin / event_type dicen que algo cambió, no cuál es el estatus final.
Estatus realConsultar un Pago (Get Payment) es la fuente de verdad, no event_type.
Verifica autenticidadConfirma que la notificación viene de Clip antes de entregar. Método en la referencia.
IdempotenciaEn realizarpago y en el fulfillment; dedupe el webhook por su id.
Sin sandboxCada request es real. Idempotencia protege contra dobles cargos en reintentos.
Lo que separa una demo de algo que mueve dinero real. Idéntico a la disciplina de Stripe/MP/Conekta.

Si no necesitas control del UI ni quieres tocar tokens, el Redireccionado es la ruta corta. Creas un link de pago contra la API de Clip, mandas al cliente a la página alojada, paga ahí y regresa a tu tienda. Tu servidor nunca ve datos de tarjeta y la superficie de código es mínima:

// Crea un link de pago (Checkout Redireccionado). Forma ilustrativa:
// confirma el host, la ruta y los campos exactos en developer.clip.mx/reference.
async function createClipPaymentLink({ amount, orderRef }) {
  const auth = Buffer.from(`${process.env.CLIP_API_KEY}:`).toString('base64')
  const res = await fetch('https://api.payclip.com/payment-links', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${auth}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount,
      currency: 'MXN',
      // referencia interna para conciliar después
      metadata: { order_ref: orderRef }
    })
  })
  const link = await res.json()
  return link.url // redirige al cliente aquí
}

La trampa mental es la misma que en OXXO/SPEI de Conekta: el regreso del cliente a tu tienda no es prueba de pago. El redirect de vuelta significa “el navegador volvió”, no “el dinero entró”. La confirmación sigue llegando por webhook, y el estatus real lo consultas con “Consultar un Pago”. Entregar sobre el redirect es regalar producto.

Conciliar Clip contra tu ledger (y por qué el “éxito” del lado del cliente no es la fuente de verdad)

Ningún evento del lado del cliente — ni el then() del SDK, ni el redirect de vuelta, ni un event_type en un webhook — es tu fuente de verdad. La verdad es el estatus que devuelve “Consultar un Pago” contra la API de Clip. Tu conciliación debe cerrar contra eso, con tu order_ref en el metadata como llave para amarrar el pago de Clip a la fila de tu base de datos.

En la práctica: guarda cada intento con su referencia, marca el pago como confirmado solo cuando la API de Clip lo confirma, y corre un job periódico que reconsulte los pagos que quedaron en el aire (webhooks perdidos por un deploy o un timeout existen). Si operas varias pasarelas a la vez, esto escala a una conciliación multi-rail — el patrón completo está en conciliación de pagos multi-rail en México.

Sin sandbox: probar en producción con cargos reales de MXN$0.01 y luego reembolsar

Clip no tiene sandbox ni tarjetas de prueba — verificado en las preguntas frecuentes y en developer.clip.mx/reference/pruebas. Pruebas en producción, con cargos reales diminutos: algunos bancos aceptan MXN$1 o incluso MXN$0.01 con comisión prácticamente nula. El lado bueno es real: un cargo de prueba exitoso significa que de verdad estás listo para producción, no que pasaste un mock.

La disciplina para no quemarte: cobra un monto mínimo, reembólsalo, usa una llave de idempotencia para que un reintento no duplique el cargo, y protege tus credenciales de producción como lo que son. Todo el método — cómo estructurar la prueba, qué logs guardar y cómo automatizar el reembolso — está en el post hermano sobre probar Clip sin sandbox.

Probar Clip sin sandbox, sin quemarte

Cargo real mínimo
MXN$0.01 – $1
Consultar un Pago
Reembolsar
Idempotencia en el reintento
Cargo real mínimo, reembolso inmediato, idempotencia para no duplicar. Un éxito aquí es prueba real de producción.

Dónde encaja Clip junto a Stripe, Mercado Pago y Conekta

Clip cierra el cuarteto. Si ya cableaste Mercado Pago o Conekta, la forma de Clip te va a resultar familiar: tokenizas en el cliente, cobras en el servidor, confirmas por webhook + consulta, y nunca confías en el “éxito” del lado del cliente. Lo que distingue a Clip es la ausencia de sandbox — pruebas con cargos reales — y su combinación de terminal física + pagos en línea, que lo vuelve atractivo si ya usas sus terminales.

¿Cuándo elegir Clip? Si ya vendes con terminales Clip y quieres unificar el en línea bajo el mismo proveedor, o si el link de pago Redireccionado te resuelve sin fricción. Para decidir entre las cuatro pasarelas según comisiones, métodos y madurez de API, la comparación completa está en pasarelas de pago en México. Sea cual sea la que elijas, la ingeniería que mueve dinero real no cambia: verifica el webhook, usa idempotencia, concilia contra la API y trata la fuente de verdad como lo que es — la respuesta de la pasarela, no lo que dice el navegador.

Fuentes oficiales: developer.clip.mx, preguntas frecuentes de Clip y developer.clip.mx/reference/pruebas.