Mercado Pago via Widget
Prerequisites
Before implementing Mercado Pago Wallet payments, ensure you have:
- Enabled Mercado Pago in the DEUNA Admin Panel.
- A generated order token.
- Integrated the DEUNA SDK in your project.
- Reviewed the Payment Widget documentation for your platform.
Payment Method configuration
Display the Payment Widget by passing the Mercado Pago configuration in the paymentMethods parameter.
[
{
"paymentMethod": "wallet",
"processors": [ "mercadopago_wallet" ]
}
]
Payment Widget - Web
DeunaSDK.initPaymentWidget({
orderToken: 'YOUR_ORDER_TOKEN',
paymentMethods: [
{
paymentMethod: 'wallet',
processors: ['mercadopago_wallet'],
},
],
callbacks: { ... },
});
Payment Widget - iOS
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA_ORDER_TOKEN>",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in
// Close the DEUNA widget
self.deunaSDK.close {
// Handle successful payment:
// - Navigate to confirmation screen
// - Show success message
// - Update order status
}
},
onError: { error in
if error.type == .paymentError {
// Handle payment errors:
// - Show error message
// - Allow retry
// - Log analytics
}
}
),
paymentMethods: [
[
"paymentMethod": "wallet",
"processors": ["mercadopago_wallet"]
]
]
)
import DeunaSDK
import SwiftUI
struct PaymentView: View {
let deunaSDK: DeunaSDK
var body: some View {
VStack {
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in
deunaSDK.dispose {
// Handle post-payment flow
}
},
onError: { error in
if error.type == .paymentError {
// Implement error handling
}
}
),
paymentMethods: [
[
"paymentMethod": "wallet",
"processors": ["mercadopago_wallet"]
]
]
)
)
}
}
}
iOS-Specific Considerations
⚠️ Mercado Pago Security Requirements
- SafariViewController Requirement:
- Mercado Pago mandates the use of SafariViewController for payment processing.
- This ensures secure handling of payment credentials.
- ViewController Dismissal:
- The SafariViewController must be manually dismissed by the user.
- The SDK's
close()
function won't automatically dismiss it.✅ Best Practices
Always provide a completion handler to the close() function to ensure your code executes only after both:
- The user manually dismisses the SafariViewController (as required by Mercado Pago)
- The payment modal completes its dismissal animation
self.deunaSDK.close { // This executes sequentially after: // 1. User closes SafariViewController // 2. Payment modal fully disappears // Recommended actions: // 1. Navigate to order confirmation // 2. Display transaction success UI // 3. Update application state }
✅ Memory Management
For embedded implementations, always call
dispose()
when the widget is no longer needed to prevent memory leaks:deunaSDK.dispose { // Cleanup resources }
Payment Widget - Android
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)
}
}
Now show the Payment Widget
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")
)
)
)
@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
- Custom Tabs Requirement:
- Payment flows must launch in Chrome Custom Tabs.
- This ensures secure handling of payment credentials.
- 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:
- The user manually closes the Custom Tab (as required by Mercado Pago)
- 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() } } }
Payment Widget - React Native
import React from 'react';
import { View, Button } from 'react-native';
import { DeunaSDK, DeunaWidget } from '@deuna/react-native-sdk';
// Initialize SDK
const deunaSDK = DeunaSDK.initialize({
publicApiKey: 'YOUR_PUBLIC_API_KEY',
environment: 'sandbox', // or "production"
});
const PaymentScreen = () => {
const launchPayment = () => {
deunaSDK.initPaymentWidget({
orderToken: 'YOUR_ORDER_TOKEN',
paymentMethods: [
{
paymentMethod: 'wallet',
processors: ['mercadopago_wallet'],
},
],
callbacks: {
onSuccess: async (order) => {
await deunaSDK.close();
// Handle successful payment
console.log('Payment successful:', order);
},
onError: async (error) => {
// Handle payment errors
console.error('Payment error:', error);
if (...) {
// Always await the close operation
await deunaSDK.close();
// Handle UI updates
console.log('Payment widget closed due to error.');
}
},
},
});
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<DeunaWidget instance={deunaSDK} />
<Button title="Pay with Mercado Pago" onPress={launchPayment} />
</View>
);
};
Specific Considerations for Mercado Pago
For secure payment processing and adherence to Mercado Pago's security policies, the SDK employs:
- iOS: SFSafariViewController.
- Android: Chrome Custom Tabs.
✅ These components are crucial for:
- Providing secure payment processing.
- Maintaining session continuity.
- Complying with Mercado Pago's security policies.
✅ SafariViewController or Custom Tab Dismissal
It's important to note that the SafariViewController or Custom Tab must be manually closed by the user. The SDK's close() function will not automatically close them.
✅ Best Practices
The close() function returns a Promise. To ensure your code executes only after both the user manually dismisses the SafariViewController/Custom Tab and the payment modal completes its dismissal animation, make sure to await this Promise.
callbacks: { onSuccess: async (order) => { // Always await the close operation await deunaSDK.close(); // Safe to execute after: // 1. User closes browser component // 2. Widget completes dismissal } }
✅ Memory Management
Always call
deunaSDK.close()
when you no longer need the DEUNA widget to:
- Free up allocated resources
- Prevent memory leaks
- Ensure proper cleanup of payment sessions
useEffect(() => { return () => { // Clean up when component unmounts deunaSDK.close(); }; }, []);
Updated 17 days ago