Payment Widget - React Native

El Payment Widget es una herramienta integrada que permite a los comercios ofrecer múltiples métodos de pago a sus clientes de manera sencilla y eficiente. Este widget es compatible tanto con Métodos de Pago Alternativos (APMs) como con tarjetas de crédito y débito, proporcionando flexibilidad en las opciones de pago para los usuarios.

El Payment Widget soporta:

  • Métodos de Pago Alternativos (APMs): como OXXO, KueskiPay, entre otros.
  • Tarjetas de Crédito y Débito: MasterCard, Visa, American Express, etc.

Paso 1: Completar los Primeros Pasos

Antes de integrar el Payment Widget, es necesario que completes la sección de primeros pasos. En esta sección, te explicamos los requisitos necesarios y cómo inicializar el SDK. Puedes encontrar más información en Primeros Pasos con el SDK de React Native.

Una vez que hayas completado estos pasos, podrás continuar con el paso 2.


Paso 2: Mostrar el Widget

2.1 Mostrar el Widget en un Modal

Sigue los siguientes pasos para mostrar el Payment Widget de DEUNA en un Modal:

  1. Crea una instancia de la clase DeunaSDK.
  2. Agrega el componente DeunaWidget a tu vista.
  3. Llama a la función initPaymentWidget.

import {
  DeunaSDK,
  DeunaWidget
} from '@deuna/react-native-sdk';

const deunaSDK = DeunaSDK.initialize({
  publicApiKey: "YOUR_PUBLIC_API_KEY",
  environment: "sandbox",
});

const YourScreen = () => {
  const onShowPaymentWidget = () => {
    deunaSDK.initPaymentWidget({
      orderToken: "YOUR_ORDER_TOKEN",
      callbacks: {
        onSuccess: async (order) => {
          await deunaSDK.close(); // Cierra el Modal y libera los recursos del widget de pago.
          // TU CÓDIGO AQUÍ
        },
        onError: (error) => {
          // Manejo de errores
        },
        onClosed: (action) => {
          // Widget cerrado
        },
      },
    });
  };
  return (
    <View>
      <DeunaWidget instance={deunaSDK} />
      <Button
        onPress={onShowPaymentWidget}
        title="Show Payment Widget"
      ></Button>
    </View>
  );
};


2.2 Mostrar el Widget de forma embebida.

Para mostrar el widget de DEUNA de forma embebida necesitaras pasar el parámetro mode: Mode.EMBEDDED cuando llames a la función initPaymentWidget.

IMPORTANTE: Para visualizar correctamente el widget de DEUNA de forma embebida, el componente DeunaWidget debe estar dentro de un componente que delimite sus dimensiones.


import {
  DeunaSDK,
  DeunaWidget,
  Mode, // AGREGAR ESTA LINEA
} from "@deuna/react-native-sdk";

const deunaSDK = DeunaSDK.initialize({
  publicApiKey: "YOUR_PUBLIC_API_KEY",
  environment: "sandbox",
});

const YourScreen = () => {
  const onShowPaymentWidget = () => {
    deunaSDK.initPaymentWidget({
      orderToken: "YOUR_ORDER_TOKEN",
      mode: Mode.EMBEDDED, // AGREGAR ESTA LINEA
      callbacks: {
        onSuccess: async (order) => {
          await deunaSDK.close(); // Libera los recursos del widget de pago.
          // TU CÓDIGO AQUÍ
        },
        onError: (error) => {
          // Manejo de errores
        },
        onClosed: (action) => {
          // Widget cerrado
        },
      },
    });
  };
  return (
    <View>
      {/* Delimita las dimensiones del Widget */}  
      <View style={{ flex: 1, width: "100%" }}>
        <DeunaWidget instance={deunaSDK} />
      </View>
      <Button
        onPress={onShowPaymentWidget}
        title="Show Payment Widget"
      ></Button>
    </View>
  );
};


Parámetros

AtributosTipoModalEmbebidoDescripción
orderTokenstringEl orderToken es un token único generado para la orden de pago. Este token es generado a través del API de DEUNA y debes implementar el endpoint correspondiente en tu backend para obtener esta información.

IMPORTANTE: Al momento de crear la orden con la API de DEUNA no se debe definir una URL de redirección (redirect_url) para que el callback onSuccess se ejecute correctamente.
callbacksJson (Callbacks)Los callbacks son funciones de retorno que se encargan de escuchar y manejar los eventos del widget de pago. Estos eventos permiten gestionar acciones específicas basadas en el estado del pago. Los principales callbacks incluyen: onSuccess, onError, onClosed, onCanceled, onCardBinDetected, onInstallmentSelected, onDownloadFile.
userToken (Opcional)stringEl bearer token del usuario de DEUNA. Cuando este es enviado, todas las acciones dentro del widget van a hacer sobre este usuario de DEUNA.

Importante: para que este userToken sea usado y se le muestren las tarjetas guardadas al cliente, el correo asociado a ese userToken debe ser el mismo que se envía al crear la orden en billing_address.email. En caso que ambos correos no coincidan, se usará el flujo sin mostrar las tarjetas por seguridad.
styleFile (Opcional)stringUUID proporcionado por DEUNA. Esto aplica si quieres configurar un archivo custom styles personalizado (Cambiar colores, textos, logo, etc). Si se proporciona un valor válido para styleFile el payment widget utilizará la configuración de la UI proporcionada por la configuración del tema que coincida con el UUID proporcionado.
paymentMethods (Opcional)Json[]Una lista métodos de pago permitidos. Este parámetro determina qué tipo de widget se debe renderizar.
language(Opcional)stringEste parámetro permite especificar el idioma en el que se mostrará la interfaz del widget. Debe proporcionarse como un código de idioma válido (por ejemplo, "es" para español, "en" para inglés, "pt" para portugués).

Comportamiento:

- Si se proporciona: El widget utilizará el idioma especificado en este parámetro, independientemente de la configuración del comerciante.
- Si no se proporciona: El widget utilizará el idioma configurado por el comerciante.
behavior (Opcional)JsonUtiliza este parámetro para configurar el comportamiento del widget.

Parámetro callbacks > onDownloadFile (Opcional)

Utiliza el callback onDownloadFile para escuchar cuando una solicitud para descargar un archivo ha sido recibida (Descarga de un Voucher, una imagen, etc.).

deunaSDK.initPaymentWidget({
  orderToken: "YOUR_ORDER_TOKEN",
  mode: Mode.EMBEDDED, // AGREGAR ESTA LINEA
  callbacks: {
    onDownloadFile: (file) => {
      const { type, data } = file;
      const mapper = {
        [DownloadType.URL]: () => {
          // TODO: Implement download from url
        },
        [DownloadType.BASE64]: () => {
          // TODO: Implement download from image base64
        },
      };
      mapper[type]();
    },
  },
});

Parámetro behavior (Opcional)

El método initPaymentWidget acepta el parámetro behavior que permite personalizar el comportamiento del widget de pago, incluyendo:

  • Habilitar pagos con múltiples tarjetas.
  • Entre otras opciones de configuración.

Nota: Estas configuraciones aplican a todos los métodos de pago habilitados en el widget.


Comportamiento de los métodos de pago paymentMethods

El parámetro paymentMethods dentro de behavior permite configurar comportamientos globales para todos los métodos de pago habilitados en el widget.

  • flowType (Tipo de Flujo)

    Valores (string): twoStep o singleStep

    Compatibilidad actual: Exclusivo para PayPal.

    Controla el flujo de visualización para métodos de pago específicos que requieren mostrar información previa antes del formulario de pago.

    deunaSDK.initPaymentWidget({
      orderToken: "TOKEN",
      .
      .
      .
      behavior: {
        paymentMethods: {
          flowType: "twoStep",
        },
      },
    });
    
    

Pago dividido en multiples tarjetas (Split Payments)

La función de Split Payments permite a los clientes dividir el pago de una compra entre múltiples tarjetas de crédito/débito.

Requisitos Previos:

  • La opción debe estar habilitada en la configuración del comercio.
  • Actualmente solo se admite división entre 2 tarjetas como máximo.
deunaSDK.initPaymentWidget({
  orderToken: "TOKEN",
  .
  .
  .
  behavior: {
    paymentMethods: {
      creditCard: {
        splitPayments: {
          maxCards: 2, // Número máximo de tarjetas permitidas
        },
      },
    },
  },
});

Configuración de Auto Purchase para PayPal

La función Auto Purchase (compra automática) para PayPal permite procesar pagos instantáneamente cuando:

  • El cliente tiene previamente vinculada su cuenta de PayPal.
  • Ha autorizado pagos rápidos en su cuenta.
deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  paymentMethods: [
    {
      paymentMethod: "wallet",
      processors: ["paypal_wallet"],
      configuration: {
        express: true,
      },
    },
  ],
});

Parámetros de Configuración

ParámetroTipoValor por DefectoDescripción
expressbooleantrueCuando es true, procesa el pago automáticamente si el cliente tiene PayPal vinculado. Cuando es false, permite seleccionar/confirmar la cuenta.

IMPORTANTE: la configuración de express solo funcionará si en el parámetro paymentMethods se le pasa únicamente la configuración para PayPal.

2.1. Mostrar u ocultar métodos de pago

El Payment Widget puede desplegar varios métodos de pago disponibles para una orden, sin que el comercio tenga que agregar individualmente cada botón de APM en su Frontend.

Al invocar la función initPaymentWidget, el parámetro paymentMethods define los métodos de pago que el widget mostrará:

  1. Si se pasa 1 solo método en paymentMethods el widget abre automáticamente el formulario del método de pago sin mostrar botones de otros AMPs.

deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "...", // optional
  styleFile: "...", // optional
  callbacks: { ... }, // replace with actual callbacks object
  paymentMethods: [
    {
      paymentMethod: "voucher",
      processors: ["payu_oxxo_cash"]
    }
  ]
});





deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "...", // optional
  styleFile: "...", // optional
  callbacks: { /* your callbacks object */ },
  paymentMethods: [
    {
      paymentMethod: "voucher",
      processors: ["daviplata"]
    }
  ]
});







deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "user_token_here", // optional
  styleFile: "style-file-id", // optional
  callbacks: {
    onSuccess: (data) => console.log("Payment success", data),
    onError: (error) => console.error("Payment error", error),
    onClose: () => console.log("Widget closed"),
  },
  paymentMethods: [
    {
      paymentMethod: "voucher",
      processors: ["nequi_push_voucher"],
    },
  ],
});







  1. Si se especifica más de 1 método de pago en el parámetro paymentMethods, el widget solo mostrará esos métodos habilitados los cuales deben estar configurados a nivel comercio.
  • 📘

    For credit card processing there is no need to pass the processors's array since the merchant can have multiple configured for the use of routing.

deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "user_token_here", // optional
  styleFile: "style-file-ids", // optional
  callbacks: {
    onSuccess: (data) => console.log("Payment success", data),
    onError: (error) => console.error("Payment error", error),
    onClose: () => console.log("Widget closed"),
  },
  paymentMethods: [
    {
      paymentMethod: "credit_card",
    },
    {
      paymentMethod: "bnpl",
      processors: ["kueski"],
    },
    {
      paymentMethod: "voucher",
      processors: ["payu_oxxo_cash"],
    },
  ],
});


  1. Si no se especifica una lista de métodos en paymentMethods y no se utilizó el parámetro include_payment_options al crear la orden, el widget mostrará todos los métodos de pago configurados para el comercio.
deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  .
  .
  .
  callbacks: {
    onSuccess: (data) => console.log('Payment success', data),
    onError: (error) => console.error('Payment error', error),
    onClose: () => console.log('Widget closed')
  },
});









2.2. Tabla de Prioridades de Configuración de Métodos de Pago

La siguiente tabla muestra como el payment widget decide que formas de pago debe mostrar cuando no se pasa el parámetro paymentMethods.

PrioridadFuente de ConfiguraciónDescripciónComportamiento en caso de un solo método de pago
1Métodos de pago pasados al ejecutar la función .initPaymentWidget o .buildPaymentWidgetUrlLos métodos de pago se muestran según los que se pasan al iniciar el widget, siempre y cuando estén activados y configurados a nivel comercio.Si solo se pasa un método de pago, el widget abre automáticamente el formulario del método de pago sin mostrar botones.
2Orden e include_payment_optionsSe revisa la orden para verificar si include_payment_options tiene métodos de pago que estén configurados y activados a nivel comercio. Los métodos no configurados no se muestran.Si solo se pasa un método de pago, el widget abre automáticamente el formulario del método de pago sin mostrar botones.
3Métodos configurados a nivel comercio
(API /payment-methods)
Si no se pasa ningún método de pago ni en la función .initPaymentWidget ni en include_payment_options al momento de crear la order, se toman los métodos configurados a nivel comercio.Si solo se pasa un método de pago, el widget abre automáticamente el formulario del método de pago sin mostrar botones.



Paso 3: Escuchar los Eventos del Widget de Pago

Cuando una transacción es exitosa o falla, es importante actualizar tu interfaz para notificar a los usuarios sobre el resultado de la transacción. Esto lo puedes realizar escuchando los eventos del widget de pago mediante los callbacks.

La función initPaymentWidget te permite escuchar los eventos del widget mediante callbacks. Define los callbacks respectivos para actualizar la interfaz de tu app.

3.1. Callbacks

CallbackModalEmbebido¿Cuándo se dispara?
onSuccessSe ejecuta cuando se completó el pago. Este callback contiene un parámetro de tipo Json con la información de la orden.
onErrorSe ejecuta cuando ocurre un error. Este callback contiene un parámetro tipo PaymentsError el cual identifica el tipo de error producido.

Consulta un ejemplo de la respuesta del callback aquí.
onClosed (Opcional)Se ejecuta cuando se cierra el dialogo que contiene el widget de pago.

Este callback contiene un parámetro de tipo enum CloseAction con los siguientes valores:

- userAction: Cuando el widget fue cerrado manualmente por el usuario, presionando en el botón cerrar (X) o en el botón de retroceso en Android sin que la operación se haya completado.

- systemAction: Cuando el widget se cierra debido a la ejecución de la función close. NOTA: Para implementación embebida el callback onClosed no ejecuta.
onCardBinDetected (Opcional)Se ejecuta cuando el widget de pago detecta el BIN de una tarjeta de crédito o débito ingresada o cuando el usuario borra el número de la tarjeta ingresada.

Este callback contiene un parámetro de tipo Json con la información del bin y la marca de la tarjeta ingresada.

NOTA: El parámetro de tipo Json será nulo cuando el usuario elimina el texto ingresado en el campo del número de tarjeta.
onInstallmentSelected (Opcional)Si la orden se puede diferir, este callback se ejecutará cuando el usuario seleccione los meses a diferir.

Este callback contiene un parámetro de tipo Json con la información de los meses a diferir seleccionados por el usuario.

NOTA: El parámetro de tipo Json será nulo cuando el usuario selecciona pago corriente (Sin cuotas).
onPaymentProcessing (Opcional)Este callback se ejecutará cuando el usuario presiona en el botón pagar y se esta procesando el pago.

NOTA: Si existe algún campo incorrecto en el formulario de pago este evento no se ejecutará.
onEventDispatch (Opcional)Se ejecuta en todas los eventos que pueda producir el widget.
Este callback contiene un parámetro de tipo CheckoutEvent y la data asociada a dicho evento

NOTA: Utiliza el callback onError para identificar si el widget no puedo ser mostrado o si ocurrió un error al procesar el pago.

onError: async (error) => {
  // Assuming the error object has a 'type' property similar to PaymentsError.Type
  switch (error.type) {
    // The widget could not be loaded
    case 'INITIALIZATION_FAILED': {
     async deunaSDK.close();
      break;
    }

    // The payment failed
    case 'PAYMENT_ERROR': {
      // YOUR CODE HERE
      console.error('Payment failed:', error);
      break;
    }

    // Default case for other error types
    default: {
      console.warn('Unhandled error type:', error.type);
      break;
    }
  }
}



Paso 4: Cerrar el Widget

La función close libera los recursos del widget de pago y si este fue mostrado en un modal también lo cierra.

Es muy importante liberar los recursos del widget cuando ya no lo necesitas para evitar perdida de memoria.

El siguiente código de ejemplo muestra cómo cerrar el widget cuando un pago es exitoso.

deunaSDK.initPaymentWidget({
  // In React/Web environment, we don't need activity context
  orderToken: "<DEUNA order token>", 
  userToken: "<DEUNA user token>", // optional
  callbacks: {
    onSuccess: async(order: any) => {
      await deunaSDK.close(); // Close payment widget
      // Your additional success handling code
      console.log('Payment successful', order);
      
      // Example: Redirect to success page
    },
  }
});

Cuando el widget de DEUNA se muestra en un Modal, los recursos del mismo son liberados cuando el Modal es cerrado. Sin embargo si el widget se muestra de forma embebida y el usuario abandona tu vista es necesario liberar los recursos del widget.

import { DeunaSDK, DeunaWidget, Mode } from "@deuna/react-native-sdk";

const YourScreen = () => {
  useEffect(() => {
    return () => {
      deunaSDK.close(); // Release the widget resources
    };
  }, []);

  const onShowWidget = () => {
    deunaSDK.initPaymentWidget({
      orderToken: "TOKEN", // optional
      mode: Mode.EMBEDDED,
      callbacks: {...},
    });

    return (
      <View>
        <View style={{ flex: 1, width: "100%" }}>
          <DeunaWidget instance={deunaSDK} />
        </View>
        <Button onPress={onShowWidget} title="Show Payment Widget"></Button>
      </View>
    );
  };
};




Paso 5 (Opcional): Personalizar la apariencia del widget

Si el payment widget esta visible y quieres personalizar la apariencia del mismo utiliza la función setCustomStyle.

Consulta la siguiente documentación para conocer más a detalle todas las opciones de personalización del payment widget.

A continuación se muestra como cambiar los colores y el logo del payment widget mediante setCustomStyle

deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "<DEUNA user token>", // optional
  callbacks: {
    onCardBinDetected: (cardBinMetadata: any) => {
      deunaSDK.setCustomStyle({
        theme: {
          colors: {
            primaryTextColor: "#023047",
            backgroundSecondary: "#8ECAE6",
            backgroundPrimary: "#F2F2F2",
            buttonPrimaryFill: "#FFB703",
            buttonPrimaryHover: "#FFB703",
            buttonPrimaryText: "#000000",
            buttonPrimaryActive: "#FFB703"
          }
        },
        HeaderPattern: {
          overrides: {
            Logo: {
              props: {
                url: "https://images-staging.getduna.com/ema/fc78ef09-ffc7-4d04-aec3-4c2a2023b336/test2.png"
              }
            }
          }
        }
      });
    },
    onSuccess: async(order: any) => {
      await deunaSDK.close();
      // Your success handling
    }
    // ... other callbacks
  }
});



Paso6 (Opcional): Refrescar el widget de pago

La función refetchOrder actualiza el widget de pago y retorna los datos de la orden actualizada.

Esta función es útil, por ejemplo, cuando el comercio ofrece promociones. Un caso común es una "promoción del 20% al pagar con Mastercard". En este escenario, el comercio escucha el evento onCardBinDetected para identificar la franquicia de la tarjeta. Luego, el comercio actualiza la orden en DEUNA (siendo el responsable de calcular las promociones) y, a través de esta función, notifica al widget para que se actualice, ya que el monto de la orden podría haber cambiado.

A continuación se muestra un ejemplo del uso de la función refetchOrder.

deunaSDK.initPaymentWidget({
  orderToken: "<DEUNA order token>",
  userToken: "<DEUNA user token>", // optional
  callbacks: {
    onCardBinDetected: async (metadata) => {
      if (metadata.cardBrand === "Mastercard") {
         const order = await deunaSDK.refetchOrder();
          console.log("Refetched order:", order);
         
      }
    },
  }
});



Ocultar el botón de pago (Widget Embebido)

Cuando se muestra el widget de pago de forma embebida se puede utilizar la propiedad hidePayButton para esconder el botón de pago del DeunaWidget.

deunaSDK.initPaymentWidget({
  orderToken: "YOUR_ORDER_TOKEN",
  mode: Mode.EMBEDDED, // AGREGAR ESTA LINEA
  hidePayButton: true, // AGREGAR ESTA LINEA
  callbacks: {
    onSuccess: async (order) => {
      await deunaSDK.close(); // Libera los recursos del widget de pago.
      // TU CÓDIGO AQUÍ
    },
    onError: (error) => {
      // Manejo de errores
    },
    onClosed: (action) => {
      // Widget cerrado
    },
  },
});

Puedes emplear las siguientes funciones para validar y ejecutar el pago.

MétodoDescripciónRespuesta
.isValid()Valida si la información ingresada es correcta y si el pago puede ser procesado.true si la información es válida, false en caso contrario.
.submit()Ejecuta el proceso de pago, equivalente a presionar el botón de pagar. Realiza las mismas validaciones internas.{ status: "success", message: "Pago procesado exitosamente" } o { status: "error", message: "The submit flow is not available" }

Consideraciones

  • Se recomienda usar isValid() antes de llamar a submit() para evitar errores en el proceso de pago.
  • Si el flujo de pago aún no está disponible, submit() siempre devolverá un error con el mensaje "The submit flow is not available".