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.
Inicializa el Widget
Antes de integrar el Payment Widget, completa los Primeros Pasos - Android.
1. Muestra el Widget
Muestra el Widget dentro de tu aplicativo con dos opciones:
- Mostrar el Widget en un Doalogo de
DialogFragment
- Mostrar el Widget embebido en Jetpack Compose
Mostrar el Widget en un Dialogo
Para mostrar el Payment Widget en un DialogFragment, llama a la función initPaymentWidget pasando los siguientes datos:

let deunaSDK: DeunaSDK = ....
deunaSDK.initPaymentWidget(
context = YOUR_ACTIVITY_CONTEXT,
orderToken = "<DEUNA order token>",
userToken = "<DEUNA user token>", // Opcional
styleFile: "<DEUNA theme ID to customize the look and feel of the widget>", // Opcional
callbacks = PaymentWidgetCallbacks().apply {
onSuccess = { data ->
deunaSDK.close() // Cierra el DialogFragment del widget de pago
// Tu código adicional
}
onCardBinDetected = { metadata, refetchOrder ->
if (metadata != null) {
// Tu código aquí
}
}
onInstallmentSelected = { metadata, refetchOrder ->
// Tu código aquí
}
onError = { error ->
// Manejo de errores
when (error.type) {
PaymentsError.Type.PAYMENT_ERROR -> {
// YOUR CODE HERE
}
else -> {}
}
}
onClosed = { action ->
// Widget cerrado
}
},
)
Mostrar el Widget de forma embebida (Jetpack Compose)
Para mostrar el widget de pago de forma embebida en tu aplicación con Compose, usa el composable AndroidView
y el view DeunaWidget
.
DeunaWiget
no admite dimensiones dinámicas por lo que el contenedor delAndroidView
debe definir las dimensiones que utilizará el widget de DEUNA.

@Composable
fun YourScreen(
deunaSDK: DeunaSDK,
orderToken: String,
userToken: String,
) {
// Maintains the Deuna WebView instance across recompositions
val deunaWidget = remember { mutableStateOf<DeunaWidget?>(null) }
Column(modifier = Modifier.padding(16.dp)) {
// Container for the embedded payment widget
// MUST specify dimensions (e.g., .height(400.dp) or .weight(1f) in parent Column)
Box(
modifier = yourModifier
) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
DeunaWidget(context).apply {
this.widgetConfiguration = PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
hidePayButton = true, // (Optional) Hide the pay button in the embedded widget
orderToken = orderToken,
userToken = userToken,
callbacks = PaymentWidgetCallbacks().apply {
onEventDispatch = { event, data ->
// Handle event dispatch
}
onSuccess = { data ->
// Handle success
}
onError = { error ->
// Handle error
}
},
)
this.build() // Render the DEUNA widget
// Store reference for later submission
deunaWidget.value = this
}
}
)
}
Spacer(modifier = Modifier.height(16.dp))
// Custom payment submission button
// Only needed when hidePayButton = true
Button(
onClick = {
// Programmatically submit payment details to Deuna
// Required when hidePayButton = true
deunaWidget.value?.submit { result ->
// Handle submission result
// result.status: "success"|"error"
// result.message: Detailed status message
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Complete Payment")
}
}
// Critical: Clean up DeunaWidget resources when composable leaves composition
DisposableEffect(Unit) {
onDispose {
deunaWidget.value?.destroy()
Log.d("DeunaWidget", "WebView resources cleaned up")
}
}
}
Parámetros
Dependiendo de la forma que como muestres el widget de pago deberas usar una de las siguientes opciones:
- Dialog: Mediante la llamada a la función
initPaymentWidget
. - Embebido: Mediante una instancia de la clase
PaymentWidgetConfiguration
Atributos | Dialogo | Embebido | Descripción |
---|---|---|---|
| ✅ | ❌ | El context es el contexto de tu Activity en Android. Proporciona acceso a recursos específicos de la aplicación y a la información del entorno en el que se está ejecutando la aplicación. |
| ✅ | ✅ | El 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.
|
| ✅ | ✅ | 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: Si le widget de DEUNA se muestra de forma embebida se debe pasar los callbacks dentro de una instancia de la clase |
| ✅ | ✅ | El bearer token del usuario de DEUNA. Cuando este es enviado, todas las acciones dentro del widget van a hacer sobre este usuario de DEUNA.
|
| ✅ | ✅ | UUID proporcionado por DEUNA. Esto aplica si quieres configurar un archivo |
| ✅ | ✅ | Una lista métodos de pago permitidos. Este parámetro determina qué tipo de widget se debe renderizar. |
| ✅ | ✅ | Este 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:
|
| ✅ | ✅ | Utiliza este parámetro para configurar el comportamiento del widget. |
Parámetro behavior
(Opcional)
behavior
(Opcional)En la función initPaymentWidget
y la clasePaymentWidgetConfiguration
aceptan el parámetro behavior
que debe ser una instancia de la clase WidgetBehavior
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.
2. Configura métodos de pago
Comportamiento de los métodos de pago paymentMethods
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
osingleStep
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.
/// DIALOG deunaSDK.initPaymentWidget( orderToken = "TOKEN", ... behavior = WidgetBehavior( paymentMethods = mapOf( "flowType" to "twoStep" ) ) ); /// EMBEDDED PaymentWidgetConfiguration( sdkInstance = deunaSDK, orderToken = "TOKEN", ... behavior = WidgetBehavior( paymentMethods = mapOf( "flowType" to "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.
/// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "TOKEN",
...
behavior = WidgetBehavior(
paymentMethods = mapOf(
"creditCard" to mapOf(
"splitPayments" to mapOf(
"maxCards" to 2 // Número máximo de tarjetas permitidas
)
)
)
)
);
/// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken = "TOKEN",
...
behavior = WidgetBehavior(
paymentMethods = mapOf(
"creditCard" to mapOf(
"splitPayments" to mapOf(
"maxCards" to 2
)
)
)
)
);
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 =
listOf(
mapOf(
"paymentMethod" to "wallet",
"processors" to listOf("paypal_wallet"),
"configuration" to mapOf("express" to true)
)
)
)
Parámetros de Configuración
Parámetro | Tipo | Valor por Defecto | Descripción |
---|---|---|---|
express | Boolean | true | Cuando es true , procesa el pago automáticamente si el cliente tiene PayPal vinculado. Cuando es false , permite seleccionar/confirmar la cuenta. |
La configuración de express solo funcionará si en el parámetropaymentMethods
se le pasa únicamente la configuración para PayPal.
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á:
-
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.
// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "<DEUNA order token>",
userToken = ..., // optional
styleFile = ..., // optional
callbacks = ...,
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("payu_oxxo_cash")
)
)
)
// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken ="<DEUNA order token>",
userToken = ..., // optional
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("payu_oxxo_cash")
)
)
)

// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "<DEUNA order token>",
userToken = ..., // optional
styleFile = ..., // optional
callbacks = ...,
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("daviplata")
)
)
)
// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken ="<DEUNA order token>",
userToken = ..., // optional
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("daviplata")
)
)
)

// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "<DEUNA order token>",
userToken = ..., // optional
styleFile = ..., // optional
callbacks = ...,
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("nequi_push_voucher")
)
)
)
// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken ="<DEUNA order token>",
userToken = ..., // optional
paymentMethods = listOf(
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("nequi_push_voucher")
)
)
)
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.

// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "<DEUNA order token>",
userToken = ..., // optional
styleFile = ..., // optional
callbacks = ...,
paymentMethods = listOf(
mapOf(
"paymentMethod" to "credit_card"
),
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("payu_oxxo_cash")
),
mapOf(
"paymentMethod" to "bnpl",
"processors" to listOf("kueski")
)
)
)
// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken ="<DEUNA order token>",
userToken = ..., // optional
paymentMethods = listOf(
mapOf(
"paymentMethod" to "credit_card"
),
mapOf(
"paymentMethod" to "voucher",
"processors" to listOf("payu_oxxo_cash")
),
mapOf(
"paymentMethod" to "bnpl",
"processors" to listOf("kueski")
)
)
)
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.

// DIALOG
deunaSDK.initPaymentWidget(
orderToken = "<DEUNA order token>",
userToken = ..., // optional
styleFile = ..., // optional
callbacks = ...,
paymentMethods = null
)
// EMBEDDED
PaymentWidgetConfiguration(
sdkInstance = deunaSDK,
orderToken ="<DEUNA order token>",
userToken = ..., // optional
callbacks = ...,
paymentMethods = null
)
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.
Prioridad | Fuente de Configuración | Descripción | Comportamiento en caso de un solo método de pago |
---|---|---|---|
1 | Métodos de pago pasados al ejecutar la función | Los 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. |
2 | Orden e | Se revisa la orden para verificar si | Si solo se pasa un método de pago, el widget abre automáticamente el formulario del método de pago sin mostrar botones. |
3 | Métodos configurados a nivel comercio | Si no se pasa ningún método de pago ni en la función | Si solo se pasa un método de pago, el widget abre automáticamente el formulario del método de pago sin mostrar botones. |
3. Escucha eventos del Widget
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 instancia de la clase PaymentWidgetCallbacks
pasada a la función initPaymentWidget
o a la clase PaymentWidgetConfiguration
te permite escuchar los eventos del widget mediante callbacks.
Define los callbacks respectivos para actualizar la interfaz de tu app.
Callbacks
Callback | Modal | Embebido | ¿Cuándo se dispara? |
---|---|---|---|
| ✅ | ✅ | Se ejecuta cuando se completó el pago. Este callback contiene un parámetro de tipo Map<String,Any> con la información de la orden. |
| ✅ | ✅ | Se 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í. |
| ❌ | Se ejecuta cuando se cierra el dialogo que contiene el widget de pago. Este callback contiene un parámetro de tipo enum
| |
| ✅ | ✅ | 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 **Map<String,Any> ** con la información del bin y la marca de la tarjeta ingresada. NOTA: El parámetro de tipo |
| ✅ | ✅ | 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 **Map<String,Any> ** con la información de los meses a diferir seleccionados por el usuario. NOTA: El parámetro de tipo |
| ✅ | ✅ | 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á. |
| ✅ | ✅ | Se ejecuta en todas los eventos que pueda producir el widget. |
Utiliza el callback onError para identificar si el widget no puedo ser mostrado o si ocurrió un error al procesar el pago.
onError = { error ->
when (error.type) {
// The widget could not be loaded
PaymentsError.Type.INITIALIZATION_FAILED -> {
deunaSDK.close()
}
// The payment was failed
PaymentsError.Type.PAYMENT_ERROR -> {
// YOUR CODE HERE
}
else -> {}
}
}
4. Cierra el Widget
Por defecto cuando el widget se muestra en un dialog a través de la función initPaymentWidget
, el widget de pago solo se cierra cuando el usuario presiona el botón de cerrar del widget o cuando presiona el botón de "retroceso" en Android.
Para cerrar el dialogo cuando un pago es exitoso o cuando ocurre un error, debes llamar a la función close
.
El siguiente código de ejemplo muestra cómo cerrar el widget cuando un pago es exitoso.
deunaSDK.initPaymentWidget(
context = YOUR_ACTIVITY_CONTEXT,
orderToken = "<DEUNA order token>",
userToken = "<DEUNA user token>", // optional
callbacks = PaymentWidgetCallbacks().apply {
onSuccess = { order ->
deunaSDK.close() // Cierra el widget de pago
// Tu código adicional
}
}
)
5. Liberar los recursos del widget
Si el widget de DEUNA se muestra de forma embebida, es importante liberar los recursos del widget cuando este ya no se necesite.
Ejemplo
val deunaWidget = remember { mutableStateOf<DeunaWidget?>(null) }
.
.
.
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
DeunaWidget(context).apply {
.
.
.
// Store reference for later submission
deunaWidget.value = this
}
}
)
DisposableEffect(Unit) {
onDispose {
deunaWidget.value?.destroy()
}
}
Funcionalidades opcionales
Además de los pasos obligatorios para operar el widget, tienes las siguientes opciones de personalización:
Personalizar la apariencia del widget
Utiliza la función setCustomStyle
para personalizar la apariencia del Widget.
await DeunaSDK.setCustomStyle({...});
Para más información, ve a Personalización de estilos.
Ejemplo
// DIALOG
deunaSDK.initPaymentWidget(
context = YOUR_ACTIVITY_CONTEXT,
orderToken = "<DEUNA order token>",
userToken = "<DEUNA user token>", // optional
callbacks = PaymentWidgetCallbacks().apply {
onCardBinDetected = { cardBinMetadata ->
this.deunaSDK.setCustomStyle(
data = JSONObject(
"""
{
"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"
}
}
}
}
}
"""
).toMap()
)
}
...
}
)
// EMBEDDED JETPACK COMPOSE
// Maintains the Deuna WebView instance across recompositions
val deunaWidget = remember { mutableStateOf<DeunaWidget?>(null) }
.
.
.
deunaWidget.value.setCustomStyle(data = ... )
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
.
// DIALOG
deunaSDK.initPaymentWidget(
context = YOUR_ACTIVITY_CONTEXT,
orderToken = "<DEUNA order token>",
userToken = "<DEUNA user token>", // optional
callbacks = PaymentWidgetCallbacks().apply {
onCardBinDetected = { metadata ->
val cardBrand = metadata["cardBrand"] as String?
if (cardBrand != null && cardBrand == "Mastercard"){
deunaSDK.refetchOrder { order ->
print("ORDER: $order")
}
}
}
...
}
)
// EMBEDDED JETPACK COMPOSE
// Maintains the Deuna WebView instance across recompositions
val deunaWidget = remember { mutableStateOf<DeunaWidget?>(null) }
.
.
.
deunaWidget.value.refetchOrder { order ->
}
Ocultar el botón de pago (Widget Embebido)
Cuando se muestra el widget de pago de forma embebida se puede utilizar el parámetro hidePayButton
para esconder el botón de pago del DeunaWidget
.
// Maintains the Deuna WebView instance across recompositions
val deunaWidget = remember { mutableStateOf<DeunaWidget?>(null) }
.
.
.
DeunaWidget(context).apply {
// Set true to handle payment submission manually via submit()
// Set false to use Deuna's built-in payment button
this.widgetConfiguration = PaymentWidgetConfiguration(
.
.
.
hidePayButton = true,
.
.
.
)
.
.
.
}
deunaWidget.value?.isValid { it -> }
deunaWidget.value?.submit {result -> }
Puedes emplear las siguientes funciones para validar y ejecutar el pago.
Método | Descripción | Respuesta |
---|---|---|
.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 asubmit{}
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"