Card
This page will help you get started with the Card checkout (tarjeta de crédito/débito).
Changelog
| Fecha | Versión | Breaking changes | Cambios |
|---|---|---|---|
| 08 oct 2026 | 1.3.0 | NO | onError informa los fallos de envío de tarjeta con CARD_NOT_SUPPORTED, BIN_INFORMATION_MISSING o UNKNOWN. |
| 12 ago 2026 | 1.2.0 | NO | submit() ahora devuelve Promise<SubmitResult> (submitted, id) para distinguir envío aceptado vs reintento. El estado final sigue llegando por onFinish. |
| 11 may 2026 | 1.1.0 | NO | Mejoras en la experiencia embebida: fondo blanco, sin header / breadcrumb / resumen interno, y nueva pantalla de espera más sobria. |
| 24 feb 2026 | 1.0.0 | NO | Primera versión |
V1.3.0
Integración
Agregar el script de checkout de Koin.
- Producción: https://payments.koin.com.br/checkout/sdk/v1/methods.js
- Sandbox: https://portal-dev.koin.com.br/checkout/sdk/v1/methods.js
API
- Luego de agregar el script de checkout de Koin a tu página tendrás disponible dentro de window un objeto llamado KoinPayments.
- Crear una instancia de la clase order para el método de pago CARD de la siguiente manera (se espera un string con el orderId generado en Koin):
// CARD - Initialization method
// Initialize CARD by Order
const card = await window.KoinPayments.order(orderId);- Una vez hecho esto, tendremos un objeto creado con los siguientes métodos:
- initialize(afterInitializedCallback, options?): El método afterInitializedCallback se va a ejecutar solo cuando se haya podido inicializar el SDK de Card con éxito. Caso contrario se puede recibir el error utilizando onError. Opcionalmente podés pasar options:
{ language?: 'es' | 'en' | 'pt' }para forzar el idioma del checkout. - mount(containerCssSelector, hidePaymentButton?, hidePaymentSummary?): Monta el checkout de tarjeta en el contenedor indicado. Los errores relacionados a esta función se devuelven en el onError. Parámetros:
- containerCssSelector: Selector CSS que apunte a un <div> donde se va a colocar el checkout de Koin.
- hidePaymentButton (opcional): Si es
true, oculta el botón de pago dentro del checkout. - hidePaymentSummary (opcional): Si es
true, oculta el resumen del pago dentro del checkout.
- submit(): Promise<SubmitResult>: Envía programáticamente el formulario de pago dentro del iframe (equivalente a que el usuario haga clic en el botón de confirmar) y devuelve el resultado del envío. Debe llamarse después de mount().
- onError({code, origin, message}): Manejo de errores de Koin. Siempre siguen el mismo formato.
- onEvent(code, payload): Eventos para comunicar el estado del proceso. El payload es opcional (p. ej. para INSTALLMENT_SELECTED puede incluir datos de cuotas).
- onFinish(data): Callback que se ejecuta cuando la orden está finalizada o su estado se actualizó (por ejemplo ya cobrada, cancelada o expirada). El
datacontiene{ status, message }.statusesAuthorized,Collected,Cancelled,FailedoVoided(PascalCase; no es el código deonEvent). Si la orden ya estaba terminada al llamar a initialize, el mensaje esOrder already finished. Si el iframe informa un estado final, el mensaje esOrder status updated. - destroy(): Elimina la instancia existente del SDK de Card si hay una.
- initialize(afterInitializedCallback, options?): El método afterInitializedCallback se va a ejecutar solo cuando se haya podido inicializar el SDK de Card con éxito. Caso contrario se puede recibir el error utilizando onError. Opcionalmente podés pasar options:
Resultado de submit
El método submit() siempre resuelve una Promise con el siguiente contrato:
type SubmitResult = {
submitted: boolean;
id: string;
};- id: Es el mismo
orderIdutilizado para crear la instancia medianteKoinPayments.order(orderId). - submitted: true: La tarjeta se tokenizó correctamente y la solicitud HTTP de compra fue aceptada. En este punto el comercio puede cerrar el SDK.
- submitted: false: El envío no pudo completarse. El comercio debe mantener el SDK abierto para que el usuario pueda corregir el formulario o volver a intentarlo.
submitted: true no indica el estado final de la transacción. El resultado definitivo, por ejemplo Collected o Failed, se informa mediante onFinish.
Los errores de los campos no forman parte de SubmitResult: se muestran en la interfaz del SDK y se informan mediante el evento FORM_VALIDATION.
submit() resuelve con submitted: false cuando el formulario no es válido, no hay una cuota seleccionada, el checkout todavía no está listo, falla la tokenización o la creación de la compra, no se recibe respuesta en 30 segundos, o ya existe otro submit() pendiente. Una llamada concurrente no inicia una compra duplicada. Si falla la tokenización, el mismo fallo llega por onError con CARD_NOT_SUPPORTED, BIN_INFORMATION_MISSING o UNKNOWN.
Eventos
| Code | Description | Origen |
|---|---|---|
| CONFIGURATION_SET | Ocurre cuando la configuración del SDK fue aplicada correctamente. | Koin |
| MOUNT_STARTED | Ocurre cuando se inicia el montaje del checkout. | Koin |
| CHECKOUT_LOADED | Ocurre cuando el iframe del checkout terminó de cargar. | Koin |
| OPENED | Ocurre cuando la orden está abierta. | Koin |
| INSTALLMENT_SELECTED | Ocurre cuando el usuario seleccionó un plan de cuotas (el payload puede incluir datos de la cuota). | Koin |
| FORM_VALIDATION | Ocurre cuando el usuario presinó el botón de pagar, devuelve si hubo o no error en el formulario de tarjeta | Koin |
Errores
| Code | Description | Origen |
|---|---|---|
| INIT_MISSING_WISH_ID | Ocurre cuando la consulta interna por order_id que fue usado en KoinPayments.order() no retorna ningún wish_id. | Koin |
| INIT_MISSING_ORDER_ID | Ocurre cuando el order_id que fue usado en KoinPayments.order() es undefined o null. | Koin |
| MOUNT_ERROR | Ocurre cuando no se pudo encontrar el contenedor especificado para montar el checkout o ocurrió un error interno al crear el mismo. | Koin |
| INITIALIZE_ERROR | Ocurre cuando la configuración no es válida. | Koin |
| CONFIGURATION_ERROR | Ocurre cuando existe un error de configuración del listener de comunicación. | Koin |
| CARD_NOT_SUPPORTED | Ocurre cuando la tokenización rechaza la tarjeta porque no está soportada. message es Card not supported. | Koin |
| BIN_INFORMATION_MISSING | Ocurre cuando la respuesta de tokenización no incluye la información del BIN. message es Bin information is missing from token data response. | Koin |
| UNKNOWN | Ocurre cuando el envío de la tarjeta falla por un motivo distinto de los dos anteriores. message es el texto del fallo, o Submit failed si no hay texto. | Koin |
Estados de onFinish
data.status es el estado de la orden en PascalCase. No es el código de onEvent. OPENED, INSTALLMENT_SELECTED y FORM_VALIDATION no llaman a onFinish.
| status | Description |
|---|---|
| Authorized | Ocurre cuando el pago fue autorizado. |
| Collected | Ocurre cuando el pago fue cobrado. |
| Cancelled | Ocurre cuando el usuario canceló el flujo. |
| Failed | Ocurre cuando el pago falló. |
| Voided | Ocurre cuando la autorización fue anulada. |
FAQ
¿onFinish se dispara siempre con la orden en un estado final?
onFinish se dispara siempre con la orden en un estado final?Sí. onFinish se dispara solo con un estado final de la tabla de arriba. No se dispara con eventos intermedios, una revisión de antifraude, una autenticación 3DS ni un pago pendiente.
Authorized es un estado final. Una orden que quedó Authorized no vuelve a Cancelled ni a Voided.
¿Qué pasa si hay timeout, error de red, o el usuario cierra la pestaña?
Si el usuario cierra la pestaña, el SDK no lo informa por ningún evento. Timeout y error de red tampoco disparan onFinish. En esos casos el estado de la orden se toma de la notificación por webhook y del GET /v1/payment/orders/{id}. onError no es ese estado: aunque un fallo del SDK pueda llegar por ahí, no reemplaza a la notificación por webhook ni al GET.
Si la orden expira, onFinish sí se dispara, con status Cancelled.
¿El status de onFinish puede diferir del GET o del webhook?
onFinish puede diferir del GET o del webhook?No. El status de onFinish sale del mismo GET /v1/payment/orders/{id} y es un estado final. Consultar ese GET en ese momento devuelve la misma respuesta.
La fuente de verdad es siempre la notificación por webhook y ese GET. No recomendamos, bajo ningún término, reemplazar la notificación por webhook o el GET Status por el onFinish del SDK.
Cuando se dispara onFinish, ¿el estado ya está en la API?
onFinish, ¿el estado ya está en la API?Sí. Como el status sale de ese mismo GET, no hay demora. El estado ya está reflejado en la API.
Ejemplo
1 - Crear una instancia de checkout
Solo deberán pasar el orderId generado en Koin.
const card = await window.KoinPayments.order(orderId);2 - Agregar listeners
Sólo se puede usar una sola vez cada método, onError, onEvent y onFinish.
card.onError((error) => {
console.log("Error from callback: ", error.code, error.message, error.origin);
});
card.onEvent((event, payload) => {
console.log("Event from callback: ", event, payload);
switch (event) {
case "OPENED":
break;
case "INSTALLMENT_SELECTED":
// payload puede contener datos de la cuota seleccionada
break;
case "FORM_VALIDATION":
// payload contiene el resultado de la validación
break;
default:
break;
}
});
card.onFinish((data) => {
console.log("Finish from callback: ", data.status, data.message);
switch (data.status) {
case "Authorized":
// El pago fue autorizado
break;
case "Collected":
// El pago fue cobrado
break;
case "Cancelled":
// El usuario canceló el flujo
break;
case "Failed":
// El pago falló
break;
case "Voided":
// La autorización fue anulada
break;
}
});3 - Iniciar flujo y montar el checkout
El método initialize se puede usar la cantidad de veces que se necesite. El método mount se debe usar dentro del callback del initialize:
async function onClick() {
card.initialize(() => {
console.log("Initialized");
// Montar checkout; opcionalmente ocultar botón o resumen
card.mount("#checkout-container");
// card.mount("#checkout-container", true, false); // ocultar botón de pago
});
}4 - Enviar el pago por código (submit)
Si usás hidePaymentButton: true, podés llamar a submit() cuando el usuario confirme desde tu propia UI:
card.initialize(() => {
card.mount("#checkout-container", true, false);
});
// Más tarde, cuando el usuario confirme (ej. tu propio botón):
document
.querySelector("#my-confirm-button")
.addEventListener("click", async () => {
const result = await card.submit();
if (result.submitted) {
console.log("Compra enviada para la orden:", result.id);
card.destroy();
return;
}
console.log("El SDK debe permanecer abierto para corregir o reintentar");
});5 - Código completo
const card = await window.KoinPayments.order(orderId);
card.onError((error) => {
console.log("Error: ", error.code, error.message, error.origin);
});
card.onEvent((event, payload) => {
console.log("Event: ", event, payload);
});
card.onFinish((data) => {
console.log("Finish from callback: ", data.status, data.message);
switch (data.status) {
case "Authorized":
// El pago fue autorizado
break;
case "Collected":
// El pago fue cobrado
break;
case "Cancelled":
// El usuario canceló el flujo
break;
case "Failed":
// El pago falló
break;
case "Voided":
// La autorización fue anulada
break;
}
});
async function onClick() {
card.initialize(() => {
console.log("Initialized");
card.mount("#checkout-container", true, false);
});
}
document
.querySelector("#my-confirm-button")
.addEventListener("click", async () => {
const { submitted, id } = await card.submit();
console.log("Submit result: ", { submitted, id });
if (submitted) {
card.destroy();
}
});Updated 17 days ago
