Guía de integración paso a paso
Esta guía te lleva desde cero hasta una integración en producción con una Checkout Session. Si solo quieres ver código, salta al ejemplo mínimo. Los nombres exactos de cada parámetro están en la Referencia.
La integración tiene dos partes:
- Tu backend crea una Checkout Session. En ella defines qué se hace con la tarjeta (cobrar, inscribirla o ambas), el cliente, el monto y, si corresponde, la tarjeta ya guardada que se usa.
- Tu frontend monta el Checkout Form y llama a
processCheckoutSessioncon el id de esa sesión. El formulario solo captura la tarjeta: todo lo demás sale de la sesión.
1. Prerrequisitos
Antes de escribir código necesitas:
| Qué | Cómo lo obtienes |
|---|---|
| Dominios habilitados | Envía a tu ejecutivo los dominios de staging y producción desde los que cargarás el formulario. Los iframes no se renderizan fuera de ellos. |
| API key | La usa tu backend para crear la Checkout Session. Nunca la expongas en el frontend. |
organizationId y accountId | Ids públicos de tu organización y cuenta (org_…, acc_…). Te los entrega Toku. |
id_customer | Id público del cliente (cus_…) dueño de la tarjeta. Lo obtienes al crear el Customer. |
| Webhook endpoint | Para confirmar el resultado server-side. Revisa Notificaciones y webhooks. |
2. Ejemplo mínimo
Pago de una tarjeta nueva que además queda inscrita para cobros futuros.
Backend: crear la Checkout Session
curl --request POST 'https://api.trytoku.com/mx/checkout-sessions' \
--header 'x-api-key: <tu API key>' \
--header 'x-api-version: 2026-10-08' \
--header 'content-type: application/json' \
--data '{
"mode": "PAYMENT",
"billing_data": {
"id_account": "acc_REEMPLAZAR",
"id_customer": "cus_REEMPLAZAR",
"amount": 1500,
"currency": "MXN"
},
"payment_options_configuration": {
"card": { "enable_toggle_switch": true }
},
"expiration_datetime": "2026-10-15T23:59:59"
}'La respuesta trae el id de la sesión. Entrégaselo a tu frontend como checkoutSessionId.
Frontend: procesar la sesión
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<title>Pagar con tarjeta</title>
</head>
<body>
<form id="cardForm">
<label>Número de tarjeta</label>
<div id="pan" style="width: 17em; height: 3em;"></div>
<label for="expirationDate">Vencimiento (MM/YY)</label>
<input type="text" id="expirationDate" placeholder="MM/YY" maxlength="5" autocomplete="cc-exp" />
<label>CVV</label>
<div id="cvv" style="width: 4em; height: 3em;"></div>
<label for="cardholderName">Nombre del titular</label>
<input type="text" id="cardholderName" autocomplete="cc-name" />
<button id="submitButton" type="button">Pagar</button>
</form>
<script src="https://storage.googleapis.com/toku-embedded-portal/checkout-form.min.js"></script>
<script>
const ORGANIZATION_ID = 'org_REEMPLAZAR';
const CHECKOUT_SESSION_ID = 'id-de-la-checkout-session';
const form = new TokuCheckoutForm({
environment: 'PROD',
panDivId: 'pan',
cvvDivId: 'cvv',
cvvStyle: { width: '3em', height: '1.5em' },
panStyle: { width: '15em', height: '1.5em' },
organizationId: ORGANIZATION_ID,
onSuccess: result => {
console.log('Pago realizado', result.data.externalTransactionId);
},
onError: error => {
console.error(error.errorCode, error.errorMessage);
},
});
document.getElementById('submitButton').addEventListener('click', () => {
try {
form.processCheckoutSession({
organizationId: ORGANIZATION_ID,
checkoutSessionId: CHECKOUT_SESSION_ID,
expirationDate: document.getElementById('expirationDate').value,
customerName: document.getElementById('cardholderName').value,
});
} catch (validationError) {
console.error(validationError.message);
}
});
</script>
</body>
</html>3. Paso a paso
3.1 Crear la Checkout Session
Para revisar los detalles puedes revisar la documentación de Checkout Session. En esta guía te explicamos cómo usarlas para Checkout Form.
El mode define la operación:
| Sesión | Qué hace | amount | Qué devuelve en onSuccess |
|---|---|---|---|
ENROLLMENT | Inscribe la tarjeta como medio de pago para cobros futuros. | No | paymentMethodId (pm_…) |
PAYMENT | Cobra el monto con la tarjeta, sin inscribirla. | Sí | externalTransactionId (trs_…) |
PAYMENT con enable_toggle_switch: true | Cobra el monto y además inscribe la misma tarjeta. | Sí | externalTransactionId (trs_…); la tarjeta queda inscrita como medio de pago |
Campos de la sesión:
| Campo | Obligatorio | Descripción |
|---|---|---|
mode | Sí | ENROLLMENT o PAYMENT. |
billing_data.id_account | Sí | Cuenta (acc_…) de la operación. |
billing_data.id_customer | Sí | Cliente (cus_…) dueño de la tarjeta. |
billing_data.amount y currency | Solo en PAYMENT | Monto a cobrar. También se pueden enviar ids_invoice o ids_subscription. |
expiration_datetime | Sí | Fecha y hora ISO 8601 en que expira la sesión. |
payment_options_configuration.card | No | enable_toggle_switch (en PAYMENT, inscribe además la tarjeta; sin él solo se cobra) y selected_card (pin_…, tarjeta ya guardada del cliente). |
external_id | No | Texto libre (hasta 128 caracteres) para reconocer la operación, por ejemplo tu número de pedido. |
unique_external_id | No | Igual que external_id, pero hace idempotente la creación: si lo repites, se devuelve la sesión ya creada con ese valor. |
additional_metadata | No | Objeto JSON libre que queda guardado en la sesión y vuelve en sus respuestas. |
customer_data | No | Datos de contacto del cliente, por ejemplo phone_number y mail. |
Inscripción de una tarjeta nueva
{
"mode": "ENROLLMENT",
"billing_data": {
"id_account": "acc_…",
"id_customer": "cus_…"
},
"expiration_datetime": "2026-10-15T23:59:59",
"external_id": "alta-tarjeta-987",
"unique_external_id": "alta-tarjeta-987-intento-1",
"additional_metadata": { "canal": "web" }
}Pago e inscripción de una tarjeta ya guardada
Un PaymentInstrument es una tarjeta guardada en la bóveda PCI de Toku. Indícalo en selected_card; debe pertenecer al mismo cliente de la sesión.
{
"mode": "PAYMENT",
"billing_data": {
"id_account": "acc_…",
"id_customer": "cus_…",
"amount": 1500,
"currency": "MXN"
},
"payment_options_configuration": {
"card": { "enable_toggle_switch": true, "selected_card": "pin_…" }
},
"expiration_datetime": "2026-10-15T23:59:59",
"external_id": "pedido-12348",
"unique_external_id": "pedido-12348-intento-1"
}
Reglas de la sesión
- Una sesión sirve para un intento: si el intento falla, crea una sesión nueva (o usa un
unique_external_iddistinto).- Una sesión con
selected_cardno acepta número de tarjeta, y una sesión sinselected_cardlo exige.- Si la tarjeta guardada no tiene nombre del titular ni vencimiento, la creación de la sesión se rechaza (409), salvo que tu organización tenga habilitada esa opción.
- Una sesión expirada, cerrada o con algun error de validación responde error.
3.2 Cargar el script
| Ambiente | Script |
|---|---|
PROD | https://storage.googleapis.com/toku-embedded-portal/checkout-form.min.js |
El script expone la clase global TokuCheckoutForm. Cárgalo antes del código que la usa.
3.3 Armar el formulario
Checkout Form inyecta un iframe dentro de los contenedores que le indiques para el número de tarjeta y el CVV. El resto de los campos son tuyos.
- Crea un
<div>para el PAN y otro para el CVV conwidthyheightexplícitos. El iframe ocupa el 100% del contenedor. - Agrega tus propios inputs para la fecha de expiración y el nombre del titular. Esos valores se los pasarás tú a
processCheckoutSession.
Sesión con tarjeta guardadaSi la sesión trae
selected_card, omitepanDivId. El iframe del PAN se crea oculto y el cliente solo escribe el CVV y el vencimiento. Si el vencimiento cambió, se actualiza en la tarjeta guardada antes de operar.
3.4 Inicializar TokuCheckoutForm
const form = new TokuCheckoutForm({
environment: 'PROD',
panDivId: 'pan',
cvvDivId: 'cvv',
cvvStyle: { width: '3em', height: '1.5em' },
panStyle: { width: '15em', height: '1.5em' },
organizationId: 'org_…',
onSuccess: result => {},
onError: error => {},
});Reglas que el constructor valida y lanza como Error si no se cumplen:
environmentdebe serPROD.cvvDivIdycvvStyleson obligatorios.- Los contenedores indicados en
panDivIdycvvDivIddeben existir en el DOM al momento de construir el objeto.
3.5 Procesar la Checkout Session
processCheckoutSession(parámetros) envía la tarjeta a la sesión. No recibe un tipo de operación: la sesión ya lo define. Valida los parámetros de forma síncrona y, si algo falta, lanza un Error.
form.processCheckoutSession({
organizationId: 'org_…',
checkoutSessionId: 'id de la Checkout Session',
expirationDate: '09/28',
customerName: 'Nombre del titular',
});| Parámetro | Obligatorio | Descripción |
|---|---|---|
organizationId | Sí | Organización (org_…). |
checkoutSessionId | Sí | id de la Checkout Session creada en el paso 3.1. |
expirationDate | Sí | Vencimiento de la tarjeta, MM/YY. |
customerName | No | Nombre del titular de la tarjeta; viaja cifrado junto a la tarjeta. |
installments | No | Meses sin intereses, entero mayor o igual a 1. |
customerInformation | No | Objeto de textos (name, mail, phone_number, government_id); cada campo reemplaza el del cliente de la sesión. |
additionalMetadata | No | Objeto libre con datos propios del intento, por ejemplo un identificador antifraude. Queda guardado junto a la operación. |
Ejemplo con todos los parámetros opcionales:
form.processCheckoutSession({
organizationId: 'org_…',
checkoutSessionId: 'id de la Checkout Session',
expirationDate: '09/28',
customerName: 'Nombre del titular',
installments: 3,
customerInformation: {
name: 'Nombre Apellido',
mail: '[email protected]',
phone_number: '+525512345678',
government_id: 'XXXX000000XXXXXX00',
},
additionalMetadata: {},
});3.6 Manejar el resultado
Después de processCheckoutSession, Toku ejecuta la operación de la sesión y espera el resultado hasta 5 minutos.
onSuccess(result):
{
"action": "OPERATION_FINISHED",
"operationResult": "SUCCESS",
"data": { "externalTransactionId": "…" }
}En una sesión ENROLLMENT, data trae paymentMethodId en lugar de externalTransactionId.
onError(error):
{
"action": "OPERATION_FINISHED",
"operationResult": "FAILURE",
"errorCode": "9999",
"errorMessage": "An unexpected error ocurred"
}errorMessage es el mensaje que Toku redacta para el cliente final. errorCode son los últimos cuatro caracteres del código de respuesta de Toku; 9999 con el mensaje genérico en inglés indica un error no clasificado (por ejemplo, una sesión expirada o ya usada).
Mientras esperas el resultado, deshabilita el botón de envío para evitar operaciones duplicadas.
3.7 Confirmar server-side
onSuccess corre en el navegador del cliente y puede no llegar a tu backend. Trata el webhook como fuente de verdad:
| Sesión | Webhook de éxito | Webhook de fallo |
|---|---|---|
ENROLLMENT | payment_method.attached | payment_method_inscription_intent.failed |
PAYMENT | transaction.success | transaction.failed |
PAYMENT con enable_toggle_switch: true | transaction.success y payment_method.attached | transaction.failed |
Cada transacción y medio de pago trae el id_checkout_session de la sesión que lo originó, para que lo cruces con tu pedido.
4. Personalizar estilos
Hay dos niveles, y se combinan:
| Opción | Qué controla | Ejemplo |
|---|---|---|
panStyle / cvvStyle | Estilos inline del <input> dentro del iframe. Objeto con propiedades CSS. | { width: '15em', height: '1.5em', border: '1px solid #ccc', 'border-radius': '8px' } |
panStylesheet / cvvStylesheet | Una hoja de estilos completa que se inyecta en el iframe. Útil para :focus, ::placeholder y fuentes. | 'input { font-family: Inter, sans-serif } input:focus { outline: 2px solid #aa90fe }' |
options.panPlaceholder / options.cvvPlaceholder | Texto del placeholder de cada campo. | 'Número de tarjeta' |
options.maskPan | Oculta el número cuando el campo pierde el foco. | true |
Puedes cambiar estilos en runtime con setPanStyle, setCvvStyle, setPanStylesheet y setCvvStylesheet, por ejemplo para marcar un campo en rojo desde onPanBlur cuando luhnCheck es false.
Las fuentes de tu página no están disponibles dentro del iframe. Decláralas en el stylesheet con @import o @font-face.
5. Salir a producción
- Usar los ids y la API key de producción.
- Crear la Checkout Session desde tu backend, nunca desde el navegador.
- Confirmar con tu ejecutivo que el dominio de producción está habilitado.
- Tener el webhook endpoint de producción suscrito a los eventos de la tabla del paso 3.7.
- Crear una sesión nueva por cada intento de pago o inscripción.
- Deshabilitar el botón de envío mientras la operación está en curso.
- Mostrar mensaje de error al cliente cuando llegue
onError.
Deprecado: integración sin Checkout Session (processOperation)
Integración deprecadaEsta integración se mantiene solo por compatibilidad. Las integraciones nuevas deben usar una Checkout Session y
processCheckoutSession.
Antes de la Checkout Session, el frontend indicaba el tipo de operación y todos sus datos en processOperation(tipo, parámetros). Si processOperation recibe un checkoutSessionId, se procesa como processCheckoutSession e ignora el tipo y el resto de los parámetros.
Prerrequisitos adicionales
| Qué | Cómo lo obtienes |
|---|---|
customerId | Id público del cliente (cus_…). Lo obtienes al crear el Customer. |
subscriptionId | Id público de la suscripción (sub_…) a la que se inscribe la tarjeta o que se cobra. Lo obtienes al crear la Subscription. Solo para pagos sobre invoices. |
invoiceIds | Ids públicos de las deudas a pagar (in_…). Solo para pagos sobre invoices. |
El constructor de TokuCheckoutForm recibe además accountId: 'acc_…'.
Inscripción
form.processOperation('INSCRIPTION', {
organizationId: 'org_…',
accountId: 'acc_…',
customerId: 'cus_…',
subscriptionId: 'sub_…',
tokuProducts: ['cards_batch_mx_recurring_pst'],
expirationDate: '09/28',
customerName: 'Nombre del titular',
});Pago con tarjeta nueva
form.processOperation('PAYMENT', {
organizationId: 'org_…',
accountId: 'acc_…',
customerId: 'cus_…',
amount: 1500,
tokuProducts: ['payment_orchestration'],
expirationDate: '09/28',
customerName: 'Nombre del titular',
});Los demás parámetros que no están indicados en los ejemplos son opcionales:
invoiceIds: las deudas que paga este cobro. Recomendado para que el pago quede asociado a tus deudas.flowName: nombre del flujo de pago, solo si Toku te indicó uno.
Pago con tarjeta guardada
Construye el formulario sin panDivId y entrega el PaymentInstrument en paymentInstrumentId. El cliente solo escribe el CVV; la fecha de expiración que envíes actualiza la tarjeta que tiene guardada.
form.processOperation('PAYMENT', {
organizationId: 'org_…',
accountId: 'acc_…',
customerId: 'cus_…',
amount: 1500,
tokuProducts: ['payment_orchestration'],
paymentInstrumentId: 'pin_…',
expirationDate: '09/28',
flowName: '0'
});El resultado (onSuccess / onError) y los webhooks son los mismos que en los pasos 3.6 y 3.7, usando INSCRIPTION y PAYMENT en vez de los modos de la sesión.
Updated 1 day ago