Integration for Android

Prerequisites

Before implementing Mercado Pago Wallet payments, ensure you have:

  1. Enabled Mercado Pago in the DEUNA Admin Panel.
  2. A generated order token.
  3. Integrated the DEUNA Android SDK in your project.
  4. Reviewed the Payment Widget documentation for Android.

Implementing Custom Tabs Support

For security reasons, Mercado Pago requires that their payment URLs must be loaded in a Custom Tab instead of a WebView.

It's crucial to understand that while a Custom Tab is open, you should avoid updating your UI or performing actions like navigation or showing dialogs. These operations could cause unexpected behavior, including app crashes.

Therefore, you should only execute such actions after the user has closed the Custom Tab. To detect when the Custom Tab closes, implement the following code in your MainActivity:

class MainActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        /**
        * Register for activity result callbacks, 
				* needed to wait until the custom chrome tab is closed
        */
        ExternalUrlHelper.registerForActivityResult(this)
    }
}

Payment Widget Implementation

Modal Implementation (DialogFragment)

Display the payment widget by passing the Mercado Pago configuration in the paymentMethods parameter:

deunaSDK.initPaymentWidget(
    orderToken = "<DEUNA_ORDER_TOKEN>",
    callbacks = PaymentWidgetCallbacks().apply {
        onSuccess = { order ->
            // Close the DEUNA widget
            this.deunaSDK.close {
                // Handle successful payment:
                // - Navigate to confirmation screen
                // - Show success message
                // - Update order status
            }
        }
        onError = { error ->
            if (error.type == PaymentErrorType.PAYMENT_ERROR) {
                // Handle payment errors:
                // - Show error message
                // - Allow retry
                // - Log analytics
            }
        }
    },
    paymentMethods = listOf(
        mapOf(
            "paymentMethod" to "wallet",
            "processors" to listOf("mercadopago_wallet")
        )
    )
)

Embedded Implementation (Jetpack Compose)

For Compose implementations:

@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,
                            orderToken = orderToken,
                            userToken = userToken,
                            paymentMethods = listOf(
                                mapOf(
                                    "paymentMethod" to "wallet",
                                    "processors" to listOf("mercadopago_wallet")
                                )
                            ),
                            callbacks = PaymentWidgetCallbacks().apply {
                                onSuccess = { data ->
                                    deunaWidget.value?.waitUntilExternalUrlIsClosed {
                                        // Handle post-payment flow
                                    }
                                }
                                onError = { error ->
                                    // Handle error
                                }
                            },
                        )

                        this.build() // Render the DEUNA widget
                        // Store reference for later submission
                        deunaWidget.value = this
                    }
                }
            )
        }

        Spacer(modifier = Modifier.height(16.dp))
    }

    // Critical: Clean up DeunaWidget resources when composable leaves composition
    DisposableEffect(Unit) {
        onDispose {
            deunaWidget.value?.destroy()
            Log.d("DeunaWidget", "WebView resources cleaned up")
        }
    }
}

📘

Android-Specific Considerations

⚠️ Mercado Pago Security Requirements

  1. Custom Tabs Requirement:
    • Payment flows must launch in Chrome Custom Tabs.
    • This ensures secure handling of payment credentials.
  2. Close the Custom Tab:
    • The Custom Tab must be manually closed by the user.
    • Programmatic closing is restricted for security. The SDK's close() function won't automatically close it.

✅ Best Practices for Modal Implementation

Always provide a completion handler to the close()function to ensure your code executes only after both:

  1. The user manually closes the Custom Tab (as required by Mercado Pago)
  2. The payment dialog is closed
self.deunaSDK.close {
    // Execution sequence:
    // 1. User closes Custom Tab
    // 2. Payment dialog animates out
    // 3. This block executes

    // Recommended actions:
    // 1. Navigate to order confirmation
    // 2. Display transaction success UI
    // 3. Update application state
}

✅ Best Practices for Embedded Implementation

Use the waitUntilExternalUrlIsClosed to ensure your code executes only after the user manually closes the Custom Tab.

deunaWidget.value?.waitUntilExternalUrlIsClosed {
	// Handle post-payment flow
}

✅ Memory Management

For embedded implementations, always call destroy() when the widget is no longer needed to prevent memory leaks:

@Composable
fun PaymentScreen() {
    DisposableEffect(Unit) {
        onDispose {
            // Always clean up WebView resources
            deunaWidget.destroy()
        }
    }
}