The Payment widget is an integrated tool that allows you to offer multiple payment methods to end customers.
The Payment widget supports:
- Alternative payment methods (APMs): such as OXXO, KueskiPay, Aplazo, among others.
- Credit and debit cards: MasterCard, Visa, American Express, and so on.
Initialize the widget
Before integrating the Payment Widget, complete the First steps - iOS.
1. Show the Widget
Show in a Modal
To show the Payment Widget in a modal (pageSheet
), call the initPaymentWidget
function passing the following data:

deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: "<DEUNA user token>", // optional
styleFile: "<DEUNA theme ID to customize the look and feel of the widget>", // optional
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in
self.deunaSDK.close() // Closes the payment widget
},
onError: { error in
// Error handling
if(error.type == .paymentError){
// YOUR CODE HERE
}
},
onClosed: { action in
// Widget closed
},
onCardBinDetected: { metadata in
guard metadata != nil else {
// Your code here
}
}
),
)
Show the Embedded Widget (SwiftUI)
Use the DeunaWidget
view to show the payment widget embedded in your app with SwiftUI.

import DeunaSDK
import SwiftUI
struct YourView: View {
let deunaSDK: DeunaSDK
var body: some View {
VStack {
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in
// NOTE: Explicitly release widget resources
// when no longer needed
// to prevent memory leaks and ensure proper cleanup.
deunaSDK.dispose()
},
onError: { error in
if(error.type == .paymentError){
// YOUR CODE HERE
}
},
onEventDispatch: { event, data in
}
)
)
)
}
}
}
Parameters
Attributes | Modal | Embedded | Description |
---|---|---|---|
orderToken | ✅ | ✅ | The orderToken is a unique token generated for the payment order. This token is generated through DEUNA's API and you must implement the corresponding endpoint in your backend to obtain this information. IMPORTANT: When creating the order with DEUNA's API, a redirect URL (redirect_url) should not be defined so that the onSuccess callback executes correctly. |
userToken (Optional) | ✅ | ✅ | The bearer token of the DEUNA user. When this is sent, all actions within the widget will be performed on this DEUNA user. Important: for this userToken to be used and for saved cards to be shown to the customer, the email associated with that userToken must be the same as the one sent when creating the order in billing_address.email . If both emails do not match, the flow without showing cards will be used for security. |
callbacks | ✅ | ✅ | Callbacks are callback functions that are responsible for listening to and handling payment widget events. These events allow managing specific actions based on the payment status. The main callbacks include: onSuccess , onError , onClosed , onCanceled , onCardBinDetected , onInstallmentSelected |
styleFile (Optional) | ✅ | ✅ | UUID provided by DEUNA. This applies if you want to configure a custom custom styles file (Change colors, texts, logo, etc). If a valid value is provided for styleFile , the payment widget will use the UI configuration provided by the theme configuration that matches the provided UUID. |
paymentMethods (Optional) | ✅ | ✅ | A list of allowed payment methods. This parameter determines which type of widget should be rendered. |
language (Optional) | ✅ | ✅ | This parameter allows you to specify the language in which the widget interface will be displayed. It should be provided as a valid language code (for example, "es" for Spanish, "en" for English, "pt" for Portuguese). Behavior: - If provided: The widget will use the language specified in this parameter, regardless of the merchant's configuration. - If not provided: The widget will use the language configured by the merchant. |
behavior (Optional) | ✅ | ✅ | Use this parameter to configure the widget's behavior. |
Configuration for voucher downloads in iOS
In iOS, for payment methods that generate the download of a voucher or receipt, additional configuration is required in your application.
Add the NSPhotoLibraryUsageDescription
key to the Info.plist file.
This will allow your app to have the necessary permissions to download and store receipts on the user's device.
Example configuration in Info.plist:
<key>NSPhotoLibraryUsageDescription</key>
<string>The application needs access to the gallery to download and store payment receipts.</string>
behavior
parameter (optional)
behavior
parameter (optional)The initPaymentWidget
and buildPaymentWidgetUrl
methods accept the behavior
parameter which must be an instance of the WidgetBehavior
class that allows you to customize the behavior of the payment widget, including:
- Enable payments with multiple cards.
- Among other configuration options.
Note: These configurations apply to all payment methods enabled in the widget.
Payment methods behavior paymentMethods
paymentMethods
The paymentMethods
parameter within behavior
allows you to configure global behaviors for all payment methods enabled in the widget.
- flowType (Flow Type)
- Values (string):
twoStep
orsingleStep
- Current compatibility: Exclusive to PayPal.
Controls the display flow for specific payment methods that require showing prior information before the payment form.
/// DIALOG
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
behavior: WidgetBehavior(
paymentMethods: [
"flowType" : "twoStep"
]
)
)
/// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
behavior: WidgetBehavior(
paymentMethods: [
"flowType" : "twoStep"
]
)
)
)
Split payment across multiple cards (Split Payments)
The Split Payments feature allows customers to split a purchase payment across multiple credit/debit cards.
Requirements
- The option must be enabled in the merchant configuration.
- Currently only supports splitting between a maximum of 2 cards.
/// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
behavior: WidgetBehavior(
paymentMethods: [
"creditCard": [
"splitPayments": [
"maxCards": 2
]
]
]
)
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
behavior: WidgetBehavior(
paymentMethods: [
"creditCard": [
"splitPayments": [
"maxCards": 2
]
]
]
)
)
)
Auto Purchase Configuration for PayPal
The Auto Purchase feature for PayPal allows processing payments instantly when:
- The customer has previously linked their PayPal account.
- They have authorized quick payments in their account.
/// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...,
paymentMethods: [
[
"paymentMethod": "wallet",
"processors": ["paypal_wallet"],
"configuration": [ "express": true ]
]
]
)
Configuration Parameters
Parameter | Type | Default Value | Description |
---|---|---|---|
express | Boolean | true | When true , processes payment automatically if the customer has PayPal linked. When false , allows selecting/confirming the account. |
IMPORTANT: the express configuration will only work if in the paymentMethods
parameter only the PayPal configuration is passed.
2. Show or hide payment methods
The Payment Widget can display various payment methods available for an order, without the merchant having to individually add each APM button in their Frontend.
When invoking the initPaymentWidget function, the paymentMethods parameter defines the payment methods that the widget will show:
-
If only 1 method is passed in paymentMethods, the widget automatically opens the payment method form without showing buttons for other AMPs.
// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...,
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["payu_oxxo_cash"]
]
]
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in },
onError: { error in },
onEventDispatch: { event, data in }
),
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["payu_oxxo_cash"]
]
]
)
)

// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...,
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["daviplata"]
]
]
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in },
onError: { error in },
onEventDispatch: { event, data in }
),
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["daviplata"]
]
]
)
)

// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...,
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["nequi_push_voucher"]
]
]
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in },
onError: { error in },
onEventDispatch: { event, data in }
),
paymentMethods: [
[
"paymentMethod": "voucher",
"processors": ["nequi_push_voucher"]
]
]
)
)
If more than 1 payment method is specified in the paymentMethods parameter, the widget will only show those enabled methods which must be configured at the merchant level.
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.

// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...,
paymentMethods: [
[
"paymentMethod": "credit_card"
],
[
"paymentMethod": "bnpl",
"processors": ["kueski"]
],
[
"paymentMethod": "voucher",
"processors": ["payu_oxxo_cash"]
]
]
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in },
onError: { error in },
onEventDispatch: { event, data in }
),
paymentMethods: [
[
"paymentMethod": "credit_card"
],
[
"paymentMethod": "bnpl",
"processors": ["kueski"]
],
[
"paymentMethod": "voucher",
"processors": ["payu_oxxo_cash"]
]
]
)
)
If no list of methods is specified in paymentMethods and the include_payment_options parameter was not used when creating the order, the widget will show all payment methods configured for the merchant.

// MODAL
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: ..., // optional
styleFile: ..., // optional
callbacks: ...
)
// EMBEDDED
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in },
onError: { error in },
onEventDispatch: { event, data in }
)
)
)
Payment Methods Configuration Priority Table
The following table shows how the payment widget decides which payment methods to show when the paymentMethods parameter is not passed.
Priority | Configuration Source | Description | Behavior in case of a single payment method |
---|---|---|---|
1 | Payment methods passed when executing the .initPaymentWidget function or when showing the widget embedded with DeunaWidget | Payment methods are shown according to those passed when starting the widget, as long as they are activated and configured at the merchant level. | If only one payment method is passed, the widget automatically opens the payment method form without showing buttons. |
2 | Order and include_payment_options | The order is checked to verify if include_payment_options has payment methods that are configured and activated at the merchant level. Unconfigured methods are not shown. | If only one payment method is passed, the widget automatically opens the payment method form without showing buttons. |
3 | Methods configured at merchant level (API /payment-methods ) | If no payment method is passed either in the .initPaymentWidget function or in include_payment_options when creating the order, the methods configured at the merchant level are taken. | If only one payment method is passed, the widget automatically opens the payment method form without showing buttons. |
3. Listen to Payment Widget events
When a transaction is successful or fails, it's important to update your interface to notify users about the transaction result. You can do this by listening to payment widget events through callbacks.
The instance of the PaymentWidgetCallbacks
class passed to the initPaymentWidget
function allows you to listen to widget events through callbacks. Define the respective callbacks to update your app's interface.
Callbacks
Callback | Modal | Embedded | When is it triggered? |
---|---|---|---|
onSuccess | ✅ | ✅ | Executes when the payment is completed. This callback contains a parameter of type [String:Any] with the order information. |
onError | ✅ | ✅ | Executes when an error occurs. This callback contains a parameter of type PaymentsError which identifies the type of error produced. See an example of the callback response here. |
onClosed (Optional) | ✅ | ❌ | Executes when the payment widget modal is closed. This callback contains a parameter of enum type CloseAction with the following values:- .userAction : When the widget was manually closed by the user (pressing the close button X or swiping the modal down) without the operation being completed.- .systemAction : When the widget closes due to the execution of the close function. |
onCardBinDetected (Optional) | ✅ | ✅ | Executes when the payment widget detects the BIN of an entered credit or debit card or when the user deletes the entered card number. This callback contains a parameter of type [String:Any] with the BIN information and the brand of the entered card. NOTE: The CardBinMetadata type parameter will be null when the user removes the text entered in the card number field. |
onInstallmentSelected (Optional) | ✅ | ✅ | If the order can be deferred, this callback will execute when the user selects the months to defer. This callback contains a parameter of type [String:Any] with the information of the deferral months selected by the user. |
onPaymentProcessing (Optional) | ✅ | ✅ | This callback will execute when the user presses the pay button and the payment is being processed. NOTE: If there is any incorrect field in the payment form this event will not execute. |
onEventDispatch (Optional) | ✅ | ✅ | Executes on all events that the widget can produce. This callback contains a parameter of type CheckoutEvent and the data associated with that event. |
NOTE: Use the onError callback to identify if the widget could not be shown or if an error occurred when processing the payment.
onError: { error in // The widget could not be loaded if error.type == .initializationFailed { self.deunaSDK.close() // close the widget return } // The payment was failed if(error.type == .paymentError){ // YOUR CODE HERE } }
Optional functionalities
In addition to the mandatory steps to operate the widget, you have the following customization options:
Close the Widget
When the widget is shown in a modal, the payment widget only closes when the user presses the widget's close button or when they press the "back" button on iOS.
To close the modal when a payment is successful or when an error occurs, you must call the close
function.
Example
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: "<DEUNA user token>", // optional
callbacks: PaymentWidgetCallbacks(
onSuccess: { order in
self.deunaSDK.close() // Closes the payment widget
// Your additional code
},
onError: nil,
...
)
)
Customize widget appearance
Use the setCustomStyle
function to customize the Widget appearance.
await DeunaSDK.setCustomStyle({...});
For more information, go to Style customization.
Example
// Extension to convert a String to a Dictionary(JSON)
extension String {
func toDictionary() -> [String: Any]? {
guard let data = data(using: .utf8) else {
return nil
}
do {
let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
return dictionary
} catch {
print("Error: \(error.localizedDescription)")
return nil
}
}
}
.
.
.
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: "<DEUNA user token>", // optional
callbacks: PaymentWidgetCallbacks(
onCardBinDetected: { metadata in
self.deunaSDK.setCustomStyle(data: """
{
"theme": {
"colors": {
"primaryTextColor": "#023047",
"backgroundSecondary": "#8ECAE6",
"backgroundPrimary": "#8ECAE6",
"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"
}
}
}
}
}
""".toDictionary() ?? [:]
)
}
...
)
)
Refresh the payment widget
The refetchOrder
function updates the payment widget and returns the updated order data.
This function is useful for offering promotions.
A common case is a "20% promotion when paying with Mastercard".
In this scenario, the merchant listens to the onCardBinDetected event to identify the card franchise. Then, the merchant updates the order in DEUNA (being responsible for calculating promotions) and, through this function, notifies the widget to update, as the order amount may have changed.
Example usage of the refetchOrder
function
deunaSDK.initPaymentWidget(
orderToken: "<DEUNA order token>",
userToken: "<DEUNA user token>", // optional
callbacks: PaymentWidgetCallbacks(
onCardBinDetected: { metadata in
if let cardBrand = metadata["cardBrand"] as? String, cardBrand == "Mastercard" {
self.deunaSDK.refetchOrder { order in
print("ORDER: \(order)")
}
}
}
...
)
)
Check Demo App Example
To better understand the Payment Widget integration, review the example project provided by DEUNA.
This example will help you better understand how to implement the widget in your iOS application.
To access the example project and get more information, Check the iOS Project Example documentation.
Hide the pay button (Embedded Widget)
When the widget is shown embedded with the DeunaWidget
view you can hide the widget's pay button using the parameter hidePayButton: true
.
DeunaWidget(
deunaSDK: deunaSDK,
configuration: PaymentWidgetConfiguration(
orderToken: "YOUR_ORDER_TOKEN",
hidePayButton: true // Set true to hide the pay button
)
)
If the DEUNA widget pay button is hidden you must use the .isValid {}
and .submit{}
functions to complete the payment flow.
Method | Description | Response |
---|---|---|
deunaSdk.isValid{} | Validates if the entered information is correct and if the payment can be processed. | true if the information is valid, false otherwise. |
deunaSdk.submit{} | Executes the payment process, equivalent to pressing the pay button. Performs the same internal validations. | { status: "success", message: "Payment processed successfully" } or { status: "error", message: "The submit flow is not available" } |
Process considerations:
- If
hidePayButton
isfalse or not defined, the pay button will be visible and the payment flow will work automatically. - If
hidePayButton
is true, the pay button will not be displayed and the payment must be managed withsubmit{}
. - Use
isValid{}
before callingsubmit{}
to avoid errors in the payment process. - If the payment flow is not yet available,
submit{}
will always return an error with the message "The submit flow is not available"