> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alignet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 4. Resultados y cierre

> Interpreta callbacks, confirma el pago desde backend y desmonta el formulario.

<div className="ao-guide-rail not-prose"><a href="/pagos-virtuales/checkout-web/introduccion">← Portada de Checkout Web</a><span>PASO 04 / 05</span></div>

Cuando llamas `paymentForm.init(...)`, Flex te permite capturar tres funciones callback. La más importante para el resultado del frontend es `responseCallback(response)`.

<Info>
  Toma `responseCallback(response)` como la entrada principal del resultado en frontend. El JSON que recibes ahí reutiliza la misma estructura del **API de Autorización - ecommerce** para el método de pago elegido dentro de Flex.
</Info>

## Callbacks disponibles

<CardGroup cols={3}>
  <Card title="responseCallback" icon="circle-check">
    Se ejecuta cuando el proceso llega a un resultado de negocio y te entrega el JSON de respuesta.
  </Card>

  <Card title="trackingCallback" icon="chart-line">
    Se ejecuta por cada evento del flujo, como cambio de método, clics o avance del usuario dentro del formulario.
  </Card>

  <Card title="onErrorCallback" icon="circle-xmark">
    Se ejecuta cuando ocurre un error técnico durante la carga o ejecución del componente.
  </Card>
</CardGroup>

## Ejemplo de implementación

```javascript theme={"system"} theme={"system"}
function responseCallback(response) {
    console.log("-------Respuesta-------");
    console.log({response});
}

function trackingCallback(trackdata) {
    console.log("-------Tracking de Eventos-------");
    console.log({trackdata});
}

function onErrorCallback(error) {
    console.log("-------Error en el proceso-------");
    console.log({error});
}

paymentForm.init(
    document.querySelector("#demo"),
    responseCallback,
    trackingCallback,
    onErrorCallback
);
```

## Cómo interpretar responseCallback

<Note>
  El campo `response.payment_method` te indica qué referencia de PayIn debes usar para interpretar el JSON de respuesta devuelto por Flex.
</Note>

<Warning>
  No uses `response.meta.status.code` como validador de pago aprobado o denegado. Este código solo indica el resultado del procesamiento del servicio o si la respuesta fue generada correctamente. Para validar el resultado del pago, revisa `response.transaction.state` y confirma desde backend con [Consulta](/api-reference/payin/consultar-transaccion-ecommerce) o [Notificaciones](https://docs.pay-me.com/payin/notificaciones), según el método de pago.
</Warning>

<Tabs>
  <Tab title="CARD">
    El objeto `response` sigue el contrato del [API de Autorización con Tarjeta](https://docs.pay-me.com/payin/autorizacion-tarjeta).

    Si tu flujo usa redirect o autenticación 3DS fuera de la misma pantalla, complementa esta lectura con [Consideraciones para métodos con Redirect](https://docs.pay-me.com/payin/autorizacion-metodos-con-redirect).
  </Tab>

  <Tab title="YAPE">
    El objeto `response` sigue el contrato del [API de Autorización con Yape](https://docs.pay-me.com/payin/autorizacion-yape).

    Aunque el resultado llegue por `responseCallback`, valida también la consistencia operativa desde backend antes de confirmar el pedido.
  </Tab>

  <Tab title="BANK_TRANSFER">
    El objeto `response` sigue el contrato del [API de Autorización con Transferencia Bancaria](https://docs.pay-me.com/payin/autorizacion-transferencia-bancaria).

    Este método puede requerir confirmación posterior. No tomes el frontend como única fuente de verdad: apóyate en [Consulta](https://docs.pay-me.com/payin/consulta-transferencia-bancaria) o [Notificaciones](https://docs.pay-me.com/payin/notificaciones).
  </Tab>

  <Tab title="QR">
    El objeto `response` sigue el contrato del [API de Autorización con QR](https://docs.pay-me.com/payin/autorizacion-qr).

    Generar el QR o mostrar una respuesta inicial no equivale a un pago final confirmado. Usa [Consulta](https://docs.pay-me.com/payin/consulta-qr) o [Notificaciones](https://docs.pay-me.com/payin/notificaciones) para cerrar la lógica de negocio.
  </Tab>

  <Tab title="CUOTEALO">
    El objeto `response` sigue el contrato del [API de Autorización con Cuotéalo](https://docs.pay-me.com/payin/autorizacion-cuotealo).

    Por tratarse de un flujo con redirección o seguimiento, confirma el estado final con [Consulta](https://docs.pay-me.com/payin/consulta-cuotealo) o [Notificaciones](https://docs.pay-me.com/payin/notificaciones).
  </Tab>

  <Tab title="PAGOEFECTIVO">
    El objeto `response` sigue el contrato del [API de Autorización con PagoEfectivo](https://docs.pay-me.com/payin/autorizacion-pagoefectivo).

    La generación del CIP o del enlace de pago no significa que el dinero ya fue recibido. Confirma el resultado con [Consulta](https://docs.pay-me.com/payin/consulta-pagoefectivo) o [Notificaciones](https://docs.pay-me.com/payin/notificaciones).
  </Tab>
</Tabs>

<Warning>
  `trackingCallback` sirve para observabilidad y `onErrorCallback` para fallos técnicos. Ninguno reemplaza la lógica de negocio que debes construir sobre `responseCallback` y la confirmación backend.
</Warning>

## Ciclo de vida de la instancia

<Note>
  Los ejemplos siguientes usan `unmountFlex()` del [ejemplo responsive](/pagos-virtuales/checkout-web/personalizar-checkout#comportamiento-responsive). Adapta la limpieza a tu presentación: embebida, popup o expandida.
</Note>

`paymentForm.terminate?.()` finaliza los procesos internos, listeners y recursos que mantiene la instancia de Flex. Esta operación es diferente de eliminar el formulario del DOM (`flexContainer.innerHTML = ""`) y aplica a cualquier tipo de presentación.

<Info>
  El operador `?.` ejecuta `terminate()` solo cuando el método existe. Esto evita un error si la versión cargada de Flex no implementa ese método.
</Info>

### Cuándo desmontar según la presentación

| Presentación  | Cuándo desmontar                                                          | Acción visual posterior                                       |
| :------------ | :------------------------------------------------------------------------ | :------------------------------------------------------------ |
| Embebida      | Cuando ocultas el checkout, cambias de sección o navegas a otra pantalla. | Retira o reemplaza el contenido donde estaba Flex.            |
| Popup (modal) | Cuando el comprador cierra el popup o termina el flujo.                   | Oculta el overlay y devuelve el foco a la interfaz principal. |
| Expandida     | Cuando contraes el checkout o abandonas la vista expandida.               | Contrae el panel o restaura el layout anterior.               |

En los tres casos, primero procesa cualquier resultado pendiente y luego ejecuta `unmountFlex()` antes de actualizar la presentación visual.

<Warning>
  No modifiques los estilos internos de Flex. Ejecuta `unmountFlex()` cuando retires el formulario, sin importar si está embebido, dentro de un popup o en una vista expandida.
</Warning>

### Pantalla de resultado propia

Cuando configuras `settings.display_result_screen: false`, Flex no presenta su pantalla final. Tu aplicación debe recibir el `response`, evaluar el estado de la transacción y mostrar una pantalla de resultado propia.

<Steps>
  <Step title="Monta el formulario">
    `paymentForm.init(...)` renderiza Flex dentro del contenedor.
  </Step>

  <Step title="Recibe el resultado">
    Flex ejecuta `responseCallback(response)`. Lee el resultado desde `response.transaction.state` y realiza las validaciones de negocio correspondientes.
  </Step>

  <Step title="Muestra tu pantalla">
    Guarda el `response` en el estado de tu aplicación y navega o renderiza la pantalla de resultado del comercio.
  </Step>

  <Step title="Desmonta Flex">
    Cuando el resultado ya fue procesado, ejecuta `terminate()`, limpia el contenedor y actualiza la presentación embebida, popup o expandida.
  </Step>
</Steps>

```javascript theme={"system"} theme={"system"}
const paymentForm = new FlexPaymentForms({
  nonce,
  payload,
  settings: {
    display_result_screen: false
  }
});

const flexContainer = document.querySelector("#flex-container");

paymentForm.init(
  flexContainer,
  (response) => {
    const transactionState = response.transaction?.state;

    // Procesa y conserva la respuesta antes de desmontar Flex.
    showMerchantResultScreen({ transactionState, response });
    unmountFlex();
  },
  (tracking) => {
    console.log("Evento de seguimiento", tracking);
  },
  (error) => {
    console.error("Error de Flex", error);
  }
);
```

<Warning>
  No ejecutes `terminate()` antes de recibir y procesar `responseCallback(response)`. Si finalizas la instancia antes, puedes interrumpir el flujo y perder el resultado que Flex todavía no ha entregado.
</Warning>

### Limpieza reutilizable

Encapsula la inicialización y el desmontaje en un controlador independiente del framework. Puedes usarlo desde JavaScript puro o conectarlo al ciclo de vida de React, Vue, Angular u otra tecnología.

```javascript theme={"system"} theme={"system"}
function createFlexController({
  container,
  configuration,
  onResult,
  onTracking,
  onError
}) {
  let instance = null;

  function mount() {
    // Evita conservar una instancia anterior al volver a abrir Flex.
    destroy();

    instance = new FlexPaymentForms(configuration);
    instance.init(container, onResult, onTracking, onError);
  }

  function destroy() {
    instance?.terminate?.();
    instance = null;
    container.replaceChildren();
  }

  return { mount, destroy };
}

const flexController = createFlexController({
  container: document.querySelector("#flex-container"),
  configuration: {
    nonce,
    payload,
    settings: {
      display_result_screen: false
    }
  },
  onResult: responseCallback,
  onTracking: trackingCallback,
  onError: onErrorCallback
});

// Abre o vuelve a crear el formulario.
flexController.mount();

// Finaliza la instancia al cerrar, navegar o retirar la presentación.
// flexController.destroy();
```

<CardGroup cols={3}>
  <Card title="JavaScript" icon="code">
    Ejecuta `destroy()` antes de retirar el contenedor de la página.
  </Card>

  <Card title="Aplicación SPA" icon="arrows-rotate">
    Conecta `destroy()` al cambio de ruta o al desmontaje de la vista.
  </Card>

  <Card title="Presentación dinámica" icon="window-restore">
    Ejecútalo al ocultar el bloque embebido, cerrar el popup o contraer la vista expandida.
  </Card>
</CardGroup>

`container.replaceChildren()` vacía el contenido de forma equivalente a `container.innerHTML = ""`, sin depender de un framework específico.

<Note>
  `terminate()` no devuelve la respuesta del pago. El resultado siempre llega mediante el primer callback de `paymentForm.init(...)`. Revisa [Capturar funciones callback](/pagos-virtuales/checkout-web/resultados-y-cierre) para interpretar y confirmar la operación.
</Note>

### Gestionar el cierre con show\_close\_button

Configura `settings.show_close_button: true` para mostrar el botón de cierre nativo de Flex. Esta opción funciona en las presentaciones embebida, popup y expandida.

```javascript theme={"system"} theme={"system"}
const paymentForm = new FlexPaymentForms({
  nonce,
  payload,
  settings: {
    show_close_button: true
  }
});
```

Cuando el comprador pulsa el botón, Flex envía el mensaje `cerro el carrito` mediante `onErrorCallback`. Aunque utiliza el callback de error, este mensaje representa una acción voluntaria del comprador y no un fallo técnico.

<CardGroup cols={3}>
  <Card title="Mostrar el botón" icon="circle-xmark">
    Activa `show_close_button` dentro de `settings`.
  </Card>

  <Card title="Interpretar el evento" icon="code-branch">
    Identifica el mensaje exacto `cerro el carrito` en `onErrorCallback`.
  </Card>

  <Card title="Cerrar la presentación" icon="power-off">
    Ejecuta `onClose()` para desmontar Flex y actualizar la interfaz.
  </Card>
</CardGroup>

Implementa una rama exclusiva para el cierre voluntario y conserva los demás mensajes como errores técnicos:

```javascript theme={"system"} theme={"system"}
function getFlexErrorMessage(error) {
  if (typeof error === "string") return error;
  return error?.message ?? "";
}

function onErrorCallback(error) {
  const errorMessage = getFlexErrorMessage(error).trim();

  if (errorMessage === "cerro el carrito") {
    onClose();
    return;
  }

  // Los demás mensajes se procesan como errores técnicos.
  handleFlexError(error);
}

function onClose() {
  unmountFlex();

  // Oculta el popup, retira el bloque embebido o contrae la vista expandida.
  hideFlexPresentation();
}
```

El flujo de cierre es `show_close_button` → `onErrorCallback` → `onClose()` → `unmountFlex()`. `hideFlexPresentation()` representa la lógica visual de tu aplicación: retirar el bloque embebido, ocultar el popup o contraer la vista expandida.

<Warning>
  Ejecuta `onClose()` solo cuando el mensaje completo sea `cerro el carrito`. Cualquier otro valor recibido en `onErrorCallback` debe continuar por el manejo de errores técnicos.
</Warning>

<Card title="5. Pruebas y producción" icon="arrow-right" href="/pagos-virtuales/checkout-web/pruebas-y-produccion">Revisa seguridad, pruebas funcionales y criterios para pasar a producción.</Card>
