SDKs

Migrar desde API Direct

Lleva parte o todo tu checkout al Web SDK sin cambiar tu backend: mismo /process/, mismos webhooks, misma conciliación.

Para comercios que ya cobran con API Direct server-to-server y quieren mover parte o todo su checkout al SDK de navegador.

Lo primero que hay que saber

Tu contrato de backend no cambia. El SDK hace POST al mismo /api/v1/process/ con la misma forma de body, devuelve la misma transacción y dispara los mismos webhooks. Tu conciliación, tu correlación por client_reference, tu handler de webhooks y tu polling a GET /api/v1/transactions/{id}/ siguen funcionando sin tocarse.

Lo único que cambia es esto:

HoyCon el SDK
Llamas a la bóveda para tokenizar la tarjetaLos campos seguros del SDK la tokenizan — nunca ves la tarjeta
Armas el body de /process/ y lo envíaspay() lo arma y lo envía
Manejas el redirect de 3DS tú mismoEl SDK lo presenta, redirect o embebido

Todo lo que pasa después de que /process/ responde queda igual.

Elige tu ruta

Dos migraciones viven en esta guía. Solo comparten el paso de preparación, así que lee únicamente la tuya.

Si quieresVe aCambios de servidor
Lanzar Apple Pay primero, sin tocar aún tu checkout actualRuta ANinguno
Mover tarjetas y APMs al navegador, y agregar Apple PayRuta BSolo si usas tarjetas guardadas

Son secuenciales, no alternativas. La Ruta A pone el SDK en tu página sin cambiar lo que tienes; la Ruta B mueve después el resto del checkout, un flujo a la vez. Ver A dónde lleva la Ruta A.

Preparación — ambas rutas

Carga el SDK (cliente)

Ambas opciones funcionan en cualquier framework, React y Next.js incluidos.

Las actualizaciones te llegan
CDN <script> — el SDK llega como window.TonderAutomáticamente; la URL sigue un canal de versión mayor (/web-sdk/v1/)
npmnpm install @tonder.io/web-sdkCuando subes la versión y despliegas

Una app en TypeScript puede instalar el paquete de npm como devDependency solo por los tipos y mantener el runtime por CDN.

Snippets de instalación y CDN: Instalación.

Crea la instancia (cliente)

Para API Direct guardas una llave secreta en tu servidor. El SDK necesita tu llave pública, y va en código de navegador. Son llaves distintas; no reutilices la secreta.

const tonder = createTonder({
  api_key: tonderPublicConfig.api_key,
  environment: 'stage', // cambia a 'production' al salir a producción
  session: { customer: { email: 'ada@example.com' } },
});
await tonder.init();

session.customer lleva la misma identidad que hoy envías como customer en el body de /process/. Referencia completa de configuración: createTonder(config).

Ruta A: empieza con Apple Pay

El primer release más pequeño posible: tu tokenización, tus llamadas a /process/ y tu conciliación se quedan exactamente donde están, y el SDK renderiza un botón cuyo cargo cae en el endpoint que ya lees.

La integración completa del botón — habilitación y registro de dominios, el chequeo de disponibilidad, el contenedor, los callbacks de events.payment — está en la guía de Apple Pay. Lo que importa entender desde la perspectiva de la migración:

Antes — tú controlas el submit

const token = await tokenize(cardData);
const tx = await fetch('/tu-backend/charge', { method: 'POST', body: /* … */ });
handleResult(tx);

Después — el SDK controla el toque, tú recibes el resultado

Apple exige que la hoja de pago se abra en el mismo tick que el toque, así que el SDK es dueño del clic y el resultado llega por los callbacks de events.payment en lugar de una promesa. Esos callbacks no son un mecanismo de Apple Pay: se disparan para todos los métodos que el SDK cobra. Cuando después muevas tarjetas y APMs, pay() devuelve una promesa y dispara los mismos callbacks, así que un solo conjunto de handlers sigue cubriendo todo. Puedes ver el botón real funcionando en el demo de Apple Pay (ábrelo en Safari).

Tu conciliación queda como está (servidor)

Nada que hacer. El cargo de Apple Pay cae en /api/v1/process/ como tus cargos con tarjeta, produce una transacción con la misma forma y dispara el mismo webhook. Tu handler existente ya lo cubre. Envía client_reference en el payment del botón exactamente como hoy y tu correlación sigue funcionando.

pay({ payment_method: { type: 'apple_pay' } }) se rechaza a propósito — el requisito de gesto de Apple es la razón. Usa el componente de botón, como muestra la guía de Apple Pay.

A dónde lleva la Ruta A

El SDK ya está cargado e inicializado en tu página, así que los pasos que quedan son más chicos que el que acabas de dar.

  1. Captura de tarjeta — el PAN y el CVV dejan de tocar tu JavaScript, lo que saca tu código de esa parte del alcance PCI, y tu llamada de tokenización a la bóveda desaparece. Ruta B, paso 1.
  2. APMs — mismos códigos de método, una llamada en lugar de un request que armas. Ruta B, paso 3.

Cada uno es un release aparte. No hay un corte único.

Ruta B: mueve tu checkout al SDK

1. Reemplaza la tokenización con campos seguros (cliente)

Este es el paso que elimina código en lugar de agregarlo. Hoy capturas los datos de tarjeta y llamas a la bóveda tú mismo; eso desaparece.

Antes

const tokens = await vault.tokenize({
  card_number, cvv, expiration_month, expiration_year, cardholder_name,
});

Después — tus <input> se vuelven contenedores vacíos y el SDK monta un iframe seguro en cada uno

<div id="collect-cardholder-name" class="card-field"></div>
<div id="collect-card-number" class="card-field"></div>
<div id="collect-expiration-month" class="card-field"></div>
<div id="collect-expiration-year" class="card-field"></div>
<div id="collect-cvv" class="card-field"></div>
const card_fields = tonder.create('card_fields');
await card_fields.mount();

Esos son los ids por defecto; cada campo configurado necesita su contenedor presente antes de mount(), o la llamada se rechaza con MOUNT_COLLECT_ERROR. Dales un max-height en tu CSS para que el iframe no crezca antes de asentarse.

Dos lugares distintos configuran estos campos, y vale la pena distinguirlos desde el principio:

Lo que quieresDónde va
Ids de contenedor propios, montar un subconjunto de campos, eventos por campotonder.create('card_fields', options) — ver la referencia
Labels, placeholders, estilos, mensajes de validacióncustomization.card_fields en createTonder()

Este es el cambio relevante para PCI. El PAN y el CVV dejan de tocar tu JavaScript por completo — van directo del comprador al iframe seguro de Tonder.

2. Reemplaza el POST a /process/ con pay() (cliente)

Los campos que envías son los mismos de hoy. Solo cambia quién llama.

Antes — tu servidor arma el sobre

{
  "operation_type": "payment",
  "amount": 250.00,
  "currency": "MXN",
  "client_reference": "ORD-001",
  "customer": { "name": "Ada Lovelace", "email": "ada@example.com" },
  "payment_method": { "type": "CARD", "card_number": "<tokenizado>" },
  "return_url": "https://merchant.example.com/return"
}

Después — lo hace el navegador, con los mismos valores

const transaction = await tonder.pay({
  amount: 250,
  currency: 'MXN',
  client_reference: 'ORD-001',
  return_url: 'https://merchant.example.com/return',
  payment_method: { type: 'card' },
});

Tres diferencias que vale la pena notar, todas simplificaciones:

  • operation_type desaparece. El SDK solo crea pagos; los reembolsos y retiros se quedan en tu servidor.
  • customer se movió a createTonder(). Pertenece a la sesión, no a cada cargo.
  • Los campos de tarjeta salen del body. El SDK los toma de los campos montados.

Referencia campo por campo, incluyendo idempotency_key y metadata: tonder.pay(input).

3. Mapea tus llamadas de APM (cliente)

La misma llamada, distinto payment_method.type. Tus códigos de APM se conservan sin cambios.

Hoy, en /process/Con el SDK
"type": "SPEI"payment_method: { type: 'spei' }
"type": "oxxopay"payment_method: { type: 'oxxopay' }
"type": "safetypaycash" + apm_configpayment_method: { type: 'safetypaycash', config: { … } }

SafetyPay sigue necesitando country, channel y bank_ids; el SDK puede listarte los bancos con getPaymentMethodBanks() en lugar de que los tengas codificados. Ver Métodos de pago alternativos.

Los APMs devuelven Pending y se liquidan después, exactamente como hoy. Tu manejo de webhooks no cambia.

4. Opcional: mueve la presentación de 3DS al SDK (cliente)

Si hoy rediriges al comprador a la página hosted tú mismo, puedes cedérselo al SDK y elegir cómo aparece:

  • presentation_mode: 'redirect' — el navegador navega, como hoy. Tu return_url sigue llegando a donde llega ahora.
  • presentation_mode: 'embedded' — el SDK abre un modal y el comprador nunca sale de tu página.

Ver Modo de presentación.

5. Opcional: tarjetas guardadas (servidor + cliente)

Solo si quieres tarjetas almacenadas. Es la única parte de la Ruta B que requiere un cambio de servidor: las operaciones con tarjetas guardadas necesitan un secure_token de corta vida acuñado por tu backend con la llave secreta de Tonder que ya tienes.

Una trampa. Con Card on File habilitado, hasta un pago de tarjeta de una sola vez necesita el token, porque el SDK guarda la tarjeta como parte del cargo. Es una configuración de la cuenta, así que el mismo código funciona para un negocio y lanza SECURE_TOKEN_REQUIRED para otro.

El endpoint a construir y qué operaciones necesitan el token: Tarjetas guardadas (secure_token).

6. Agrega Apple Pay (cliente)

Sigue la guía de Apple Pay desde el paso 1. Es el mismo trabajo hayas migrado el resto o no.

Conciliación — no cambies esto

El error más común al mover el checkout al navegador es empezar a confiar en el navegador.

Ya surtes pedidos desde webhooks, porque server-to-server no te dejó otra opción. Sigue haciendo exactamente eso. El SDK devuelve una transacción para que actualices la pantalla, no para que liberes mercancía — un navegador se puede cerrar o perder señal, y nada de eso cambia lo que pasó con el dinero.

  • client_reference — síguelo enviando, sigue correlacionando con él
  • idempotency_key — síguelo enviando, para que un cargo reintentado no se vuelva dos
  • Webhooks — mismo payload, mismo handler, sin envoltorio. Ver Cómo funcionan los webhooks
  • getTransaction() — el equivalente en navegador de tu GET /api/v1/transactions/{id}/, para páginas de retorno y consultas puntuales

Prueba la migración

Corre esto en stage antes de cambiar el tráfico de producción.

PruebaQué demuestra que funcionó
Un pago con tarjetaLa misma forma de transacción en tu handler existente que antes de migrar
Una tarjeta rechazadaLlega como transacción con estado declinado, no como error lanzado
Una tarjeta con 3DSRegresa a tu return_url, o se resuelve en el modal si es embebido
Un APMDevuelve Pending y se liquida por webhook, como hoy
Tu handler de webhooksEl código sin tocar sigue procesando los pagos creados por el SDK
Apple Pay, si lo adoptasteLa hoja abre en un dispositivo real — el Simulador de iOS no puede probar Apple Pay web
Un rechazo de Apple PayLlega en on_completed con estado declinado, no en on_error

Esa última fila es la que sorprende: on_completed significa que el cargo llegó a una respuesta final, no que la respuesta fue sí.

Qué se queda en tu servidor

El SDK no reemplaza esto. Siguen siendo llamadas de API Direct:

  • Reembolsos — operation_type: "refund"
  • Retiros — operation_type: "withdrawal"
  • Cualquier cargo que crees sin un navegador presente

Siguientes pasos

¿Te resultó útil esta página?

En esta página