Web
Todos los métodos, opciones y errores del Web SDK.
Referencia del SDK Web de Tonder (@tonder.io/web-sdk,
TypeScript, tipos incluidos). La guía paso a paso está en SDK Web; el código
fuente y README en github.com/tonderio/web-sdk.
import { createTonder, AppError, ErrorKeyEnum } from '@tonder.io/web-sdk';¿Prefieres cargarlo con <script> en vez de npm? Las URLs de CDN aprobadas para stage y producción
están en SDK Web → Instalación.
Las tarjetas guardadas / Card-on-File requieren @tonder.io/web-sdk 0.1.11 o posterior.
createTonder(config)
Crea una instancia del SDK para un comprador/sesión.
interface TonderConfig {
api_key: string;
environment: 'sandbox' | 'stage' | 'production';
session?: {
customer?: {
email: string;
first_name?: string;
last_name?: string;
phone?: string;
};
secure_token?: string;
};
presentation_mode?: 'redirect' | 'embedded';
events?: {
presentation?: {
on_open?(): void;
on_close?(): void;
};
};
customization?: TonderCustomization;
}| Campo | Requerido | Descripción |
|---|---|---|
api_key | Sí | Llave pública de Tonder para navegador. |
environment | Sí | 'sandbox', 'stage' o 'production'. |
session.customer | Para pay() y tarjetas guardadas | Identidad del cliente. |
session.customer.phone | Para tarjetas guardadas, en negocios configurados para requerir teléfono | Teléfono del cliente en formato E.164 (+52 + 10 dígitos). No reemplaza al email, que siempre identifica al cliente. Consulta Negocios que requieren teléfono para tarjetas guardadas. |
session.secure_token | Para tarjetas guardadas | Token de corta vida generado en tu backend. |
presentation_mode | No | 'redirect' (default) o 'embedded'. |
events.presentation.on_open | No | Se llama al abrir la vista hosted embebida. |
events.presentation.on_close | No | Se llama cuando el comprador cierra la vista embebida. |
customization.card_fields | No | Personalización de los campos seguros (abajo). |
Lanza INIT_ERROR si falta config o api_key, o si environment es inválido.
customization.card_fields
Todos los campos son opcionales; lo omitido usa los defaults del SDK.
| Campo | Tipo | Descripción |
|---|---|---|
labels | CardLabels | Texto sobre cada campo seguro (cardholder_name, card_number, cvv, expiration_month, expiration_year). |
placeholders | CardPlaceholders | Placeholder dentro de cada campo seguro. |
styles | CardStyles | Estilos globales (card_form) y por campo para inputs, etiquetas, errores e icono de la tarjeta (enable_card_icon). |
error_messages | CardFieldErrorMessages | Mensajes de validación (required, invalid y por campo). |
Los valores de estilo usan claves CSS-in-JS del renderizador seguro (p. ej. font_size,
font_family, color, border_color). styles aplica dentro del iframe seguro; el layout
del contenedor (p. ej. .card-field { max-height: 90px; }) se controla con tu propio CSS.
const tonder = createTonder({
api_key: 'pk_test_...',
environment: 'sandbox',
customization: {
card_fields: {
labels: { card_number: 'Número de tarjeta', cvv: 'Código de seguridad' },
placeholders: { card_number: '4111 1111 1111 1111', expiration_month: 'MM' },
styles: {
card_form: {
input_styles: {
base: { color: '#111827', font_family: 'Inter, sans-serif', font_size: '16px' },
focus: { border_color: '#2563eb' },
invalid: { color: '#b91c1c' },
},
label_styles: { base: { color: '#374151', font_weight: '600' } },
error_styles: { base: { color: '#b91c1c' } },
},
enable_card_icon: true,
},
error_messages: { required: 'Completa este campo.', invalid: 'Revisa este campo.' },
},
},
});Métodos
| Método | Qué hace | Requiere init() |
|---|---|---|
tonder.init() | Carga la configuración del comercio y prepara el SDK. Seguro llamarlo más de una vez. | — |
tonder.create('card_fields', options?) | Crea el componente de campos seguros. | No |
card_fields.mount() | Monta los campos seguros en los contenedores. | Sí |
card_fields.unmount() | Desmonta los campos de este componente. | No |
card_fields.reveal(input) | Muestra valores seguros de una tarjeta guardada (nunca el CVV). | Sí |
tonder.pay(input) | Crea un pago. | Sí |
tonder.getTransaction(id) | Lee el estado actual de una transacción. | No |
tonder.enrollCard() | Guarda la tarjeta nueva montada para session.customer sin cobrarla. | Sí |
tonder.getCustomerCards() | Lista las tarjetas guardadas del cliente. | Sí |
tonder.canSaveCards() | true cuando las operaciones con tarjetas guardadas están disponibles para esta sesión. false cuando tu negocio requiere teléfono y session.customer.phone falta o está vacío. | Sí |
tonder.removeCustomerCard(card_id) | Elimina una tarjeta guardada. | Sí |
tonder.getPaymentMethods() | Lista los métodos de pago activos del negocio. | No |
tonder.getPaymentMethodBanks() | Lista bancos SafetyPay agrupados por canal. | No |
tonder.create('card_fields', options?)
Sin options, el SDK monta el formulario completo de tarjeta nueva usando los contenedores por
defecto:
| Campo | Contenedor por defecto |
|---|---|
cardholder_name | #collect-cardholder-name |
card_number | #collect-card-number |
expiration_month | #collect-expiration-month |
expiration_year | #collect-expiration-year |
cvv | #collect-cvv (o #collect-cvv-<card_id> para tarjetas guardadas) |
interface CardFieldsOptions {
fields?: (CardField | { field: CardField; container_id?: string })[];
card_id?: string; // para el CVV de una tarjeta guardada
unmount_context?: 'all' | 'none' | 'current' | 'create' | string;
events?: Partial<Record<CardField, {
on_change?(state: CardFieldState): void;
on_blur?(state: CardFieldState): void;
on_focus?(state: CardFieldState): void;
on_ready?(state: CardFieldState): void;
}>>;
}tonder.pay(input)
interface PayInput {
amount: number;
currency?: string;
return_url: string;
payment_method:
| { type: 'card' }
| { type: 'saved_card'; card_id: string }
| { type: string; config?: Record<string, unknown> };
metadata?: Record<string, unknown>;
billing_address?: {
street?: string;
street2?: string;
state?: string;
country?: string;
zip_code?: string;
};
client_reference: string;
idempotency_key?: string;
}| Campo | Requerido | Descripción |
|---|---|---|
amount | Sí | Monto del pago (unidades decimales). Debe ser mayor que 0. |
currency | No | Código de moneda. MXN por defecto. |
return_url | Sí | URL usada tras la autenticación hosted o el redirect. |
payment_method | Sí | Tarjeta nueva, tarjeta guardada o un método alternativo habilitado. |
billing_address | No | Dirección de facturación del comprador. Todos sus campos son opcionales. |
client_reference | Sí | Tu referencia de orden — aparece en dashboards, exportes, webhooks y reportes. |
idempotency_key | No | Llave estable por intento de pago para que los reintentos no dupliquen cargos. |
metadata | No | Contexto no sensible para conciliación y reportes. |
Llaves de metadata con significado en reportes:
| Llave | Uso en reportes |
|---|---|
operation_date | Fecha/hora de operación de negocio para reportes y conciliación. |
customer_email | Email mostrado en reportes de transacciones. |
customer_id | Identificador de cliente del comercio para filtrado. |
business_user | Usuario interno, terminal POS o automatización que originó el pago. |
Devuelve Promise<RawTransaction>. Una transacción que necesita 3DS o instrucciones hosted puede
incluir next_action.redirect_to_url.url; las respuestas de APM/SPEI pueden incluir clabe,
bank_name, payment_instructions y voucher_pdf.
tonder.getCustomerCards()
interface Card {
card_id: string;
card_number: string; // enmascarado
expiration_month: string;
expiration_year: string;
card_scheme: string;
subscription_id: string | null;
}subscription_id solo se devuelve cuando Card-on-File está habilitado para el negocio. Si es
null, monta el campo CVV de la tarjeta guardada antes de llamar pay() con esa tarjeta.
En un negocio que requiere teléfono para tarjetas guardadas, getCustomerCards() lanza
SAVE_CARDS_UNAVAILABLE sin enviar ninguna petición cuando session.customer.phone falta o está
vacío — revisa primero tonder.canSaveCards().
tonder.canSaveCards()
Devuelve true cuando las operaciones con tarjetas guardadas están disponibles para esta sesión, y
false cuando tu negocio requiere teléfono y session.customer.phone falta o está vacío. Si es
false, oculta la UI de tarjetas guardadas y ofrece solo tarjeta nueva: getCustomerCards(),
enrollCard() y pay() con saved_card lanzarían SAVE_CARDS_UNAVAILABLE. Consulta
Negocios que requieren teléfono para tarjetas guardadas.
tonder.enrollCard()
interface EnrollResult {
card_id: string;
subscription_id?: string; // viene cuando Card-on-File está habilitado
}Guarda la tarjeta nueva montada para session.customer sin cobrarla; requiere session.customer y
session.secure_token. Al resolverse, los campos de tarjeta nueva se limpian — actualiza la lista
con getCustomerCards(). Cuando Card-on-File está habilitado para tu negocio, un
pay({ payment_method: { type: 'card' } }) con tarjeta nueva también guarda la tarjeta, y vuelve de
getCustomerCards() con un subscription_id.
tonder.getPaymentMethods()
interface PaymentMethodInfo {
id: number;
payment_method: string; // el valor que va en payment_method.type de pay()
label: string;
logo: string;
category: string;
}Devuelve Promise<PaymentMethodInfo[]> con los métodos alternativos activos del negocio. Úsalo para
construir la lista de métodos en tu UI en vez de codificarla a mano: payment_method es el valor
que después pasas como payment_method.type en pay().
[
{
"id": 7,
"payment_method": "oxxopay",
"label": "Oxxo Pay",
"logo": "https://...",
"category": "cash"
}
]tonder.getPaymentMethodBanks()
interface PaymentMethodBank {
id: number;
name: string;
code: string;
country: string;
channel: 'WP' | 'OL'; // WP = efectivo, OL = transferencia
logo?: string;
}
interface PaymentMethodBanks {
cash: PaymentMethodBank[];
transfer: PaymentMethodBank[];
}Para safetypayCash / safetypayTransfer, arma payment_method.config con country
(bank.country), channel (bank.channel) y bank_ids: [{ id: bank.code }] — el código de
ruteo bancario, no el bank.id interno.
Tipos
import type {
TonderConfig,
PayInput,
RawTransaction,
Customer,
Card,
EnrollResult,
PaymentMethodInfo,
PaymentMethodBank,
PaymentMethodBanks,
CardFieldsOptions,
CardFieldsComponent,
TonderEvents,
PresentationEvents,
} from '@tonder.io/web-sdk';RawTransaction
pay() y getTransaction() devuelven los campos en snake_case, igual que el API y los
webhooks de Tonder:
interface RawTransaction {
id: string;
operation_type: string;
status: string;
amount: number;
currency: string;
client_reference?: string;
metadata?: Record<string, unknown>;
provider?: string;
created_at?: string;
status_code?: number;
next_action?: {
redirect_to_url?: {
url: string;
verify_transaction_status_url?: string;
};
};
decline_code?: string;
decline_reason?: string;
payment_instructions?: Record<string, unknown>;
voucher_pdf?: string;
clabe?: string;
bank_name?: string;
[key: string]: unknown;
}Errores
Los fallos del SDK se lanzan como AppError. Los rechazos de pago no se lanzan: se devuelven
como transacciones — lee transaction.status. Usa error.code para ramificar; no parsees
error.message.
try {
const transaction = await tonder.pay({ /* ... */ });
} catch (error) {
if (error instanceof AppError) {
console.error(error.code, error.status_code, error.details.system_error);
if (error.code === ErrorKeyEnum.MISSING_CUSTOMER) {
// Recrea el SDK con session.customer.
}
} else {
throw error;
}
}Códigos principales:
| Código | Cuándo ocurre | Cómo resolverlo |
|---|---|---|
INIT_ERROR | Falló la inicialización del SDK. | Verifica api_key, environment y el acceso de red. |
NOT_INITIALIZED | Un método requiere init() completado. | Llama await tonder.init() antes de la operación. |
MISSING_CUSTOMER | Falta session.customer. | Crea el SDK con session.customer.email. |
SECURE_TOKEN_REQUIRED | Las operaciones de tarjetas guardadas requieren session.secure_token. | Genera el token en tu backend y pásalo en createTonder(). |
SECURE_TOKEN_INVALID | El secure_token es inválido o expiró (1 hora). | Genera un token nuevo y recrea el SDK. |
SAVE_CARDS_UNAVAILABLE | Tu negocio requiere teléfono para tarjetas guardadas y session.customer.phone falta o está vacío. | Recrea el SDK con session.customer.phone (E.164), u oculta la UI de tarjetas guardadas usando canSaveCards(). |
CUSTOMER_OPERATION_ERROR | El SDK no pudo registrar o consultar session.customer (por ejemplo, falta email). | Pasa session.customer.email y reintenta. |
CUSTOMER_AUTH_TOKEN_NOT_VALID | La sesión del cliente con Tonder ya no es válida. | Recrea el SDK con un secure_token nuevo. |
INVALID_PAYMENT_REQUEST | amount, return_url, client_reference o payment_method inválidos. | Valida el request antes de llamar pay(). |
INVALID_APM_CONFIG | safetypayCash/safetypayTransfer sin config.country, config.channel o config.bank_ids. | Arma el config con el banco de getPaymentMethodBanks(). |
MOUNT_COLLECT_ERROR | Los campos seguros no pudieron montarse o recolectar datos válidos. | Verifica que los contenedores existan y los campos estén completos. |
SECURE_FIELDS_LOAD_ERROR | El navegador no pudo cargar los campos seguros. | Revisa CSP, bloqueadores de anuncios y red. |
PAYMENT_PROCESS_ERROR | El pago no pudo crearse/procesarse. | Inspecciona error.details y reintenta solo si es seguro/idempotente. |
FETCH_TRANSACTION_ERROR | Falló la consulta de la transacción. | Verifica el id y reconcilia desde backend/webhooks. |
POLL_TIMEOUT_ERROR | El 3DS embebido terminó pero la reconciliación no llegó a un estado final a tiempo. | No cumplas desde el cliente; reconcilia con getTransaction() o webhooks. |
FETCH_CARDS_ERROR | getCustomerCards() falló. | Reintenta; mientras tanto ofrece tarjeta nueva. |
SAVE_CARD_ERROR / REMOVE_CARD_ERROR | Falló el guardado/eliminación de la tarjeta. | Pide otra tarjeta o revisa error.details. |
SAVE_CARD_PROCESS_ERROR | Los datos de la tarjeta no pudieron procesarse al guardarla. | Pide al comprador que revise los datos de la tarjeta. |
CARD_ON_FILE_DECLINED | La tarjeta no pudo enrolarse en Card-on-File. | Pide otra tarjeta. |
FETCH_PAYMENT_METHODS_ERROR / FETCH_PAYMENT_METHOD_BANKS_ERROR | El catálogo de métodos/bancos no pudo recuperarse. | Reintenta u ofrece códigos de método conocidos directo en pay(). |
La lista completa (incluidos códigos raros/de compatibilidad) está en el README del SDK.
Estados de pago
Lee el estado en transaction.status:
| Estado | Significado | Qué hacer |
|---|---|---|
Success | Pago completado. | Confirma el pedido. |
Authorized | Pago autorizado por el procesador. | Continúa según tu configuración y reconcilia con webhooks. |
Pending | Aún no es final (3DS por redirect, APM/SPEI asíncrono). | Espera el webhook o consulta con getTransaction(). |
Processing | El proveedor sigue procesando. | No cumplas todavía. |
Declined | El emisor/procesador rechazó el pago. | Muestra un mensaje recuperable. |
Failed | El pago falló. | Muestra un mensaje recuperable u ofrece otro método. |
Cancelled | Pago cancelado o anulado. | No cumplas; permite iniciar un pago nuevo. |
Expired | No se completó a tiempo. | Pide al cliente iniciar un pago nuevo. |
