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 get this information. When creating the order with DEUNA's API, a redirect URL (redirect_url) should not be defined so that the onSuccess callback runscorrectly. |
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. 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, then the flow without showing cards is 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 (Ooptional) | ✅ | ✅ | UUID provided by DEUNA. This applies if you want to configure a custom custom styles file (change colors, texts, logo, and so on). If a valid value is provided for styleFile , the Payment widget uses 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 is displayed. It should be provided as a valid language code (for example, "es" for Spanish, "en" for English, "pt" for Portuguese). Behavior: - If provided,then the widget uses the language specified in this parameter, regardless of the merchant's configuration. - If not provided, then the widget uses 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 allows 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 two 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. |
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 **itPaymentWidget
function, the paymentMethods
parameter defines the payment methods that the widget shows:
-
If only one method is passed in
paymentMethods
, then 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 one payment method is specified in the paymentMethods
parameter, then the widget only shows the 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, then the widget shows 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, then 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, then 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, then the methods configured at the merchant level are taken. | If only one payment method is passed, then the widget automatically opens the payment method form without showing buttons. |
3. Listen to Payment widget events
When a transaction is successful or fails, you must 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 interface.
Callbacks
Callback | Modal | Embedded | When is it triggered? |
---|---|---|---|
onSuccess | ✅ | ✅ | Runs when the payment is completed. This callback contains a parameter of type [String:Any] with the order information. |
onError | ✅ | ✅ | Runs when an error occurs. This callback contains a parameter of type PaymentsError which identifies the type of error produced. |
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) | ✅ | ✅ | Runs 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, then this callback runs 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 when the user presses the pay button and the payment is being processed. NOTE: If there is any incorrect field in the payment form, then this event will not run. |
onEventDispatch (Optional) | ✅ | ✅ | Runs 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 might 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, then 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, then the pay button is visible, and the payment flow works automatically. - If
hidePayButton
is true, then the pay button is not 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, then
submit{}
will always return an error with the message "The submit flow is not available."