React Native SDK
Install the DEUNA React Native SDK and integrate widgets, native wallets, redirects, and fraud-device flows.
On this page
- Requirements
- Install the SDK
- Initialize and mount the renderer
- Open Payment Widget
- Embed a widget
- Filter and configure payment methods
- Dynamic styling and order refresh
- Available experiences
- Payment Vault and Click to Pay
- Native Apple Pay and Google Pay
- Continue a pending action
- Open a voucher
- Handle external redirects
- Fraud-device data and fingerprinting
- Callback and lifecycle reference
- Official examples and source
Use the DEUNA React Native SDK to share Payment Widget, Payment Vault, Click to Pay, next-action, voucher, and native-wallet integrations across iOS and Android applications.
Requirements#
To use DEUNA's React Native SDK, you need:
- Node.js: 20 or higher
- Package manager: Yarn or npm
- React: 18.2.0 or higher
- React Native: 0.73.0 or higher
- React Native WebView: 13.15.0 or higher
- Expo SDK: 50 or higher (for apps with Expo)
- DEUNA credentials: An active account and valid DEUNA credentials (contact support@deuna.com if you experience any delays)
- Android project:
minSdkVersion 24or higher - Huawei devices: HarmonyOS 2.0 or higher
Install the SDK#
Begin the initialization of the SDK.
1. Add dependencies
Add the DEUNA SDK to your project:
yarn add @deuna/react-native-sdknpm i @deuna/react-native-sdkpnpm add @deuna/react-native-sdkAdd the required react-native-webview (≥ 13.15.0) peer dependency:
yarn add react-native-webviewnpm i react-native-webviewpnpm add react-native-webview2. Linking
Starting from version 2.1.0, the SDK includes a native module that enables native Google Pay and Apple Pay payment sheets.
React Native CLI
Run pod install to link the native module on iOS:
cd ios && pod installExpo (managed workflow)
Add the config plugin to app.json. Supply only the wallet entitlements and fraud providers your application uses:
{
"expo": {
"plugins": [
[
"@deuna/react-native-sdk",
{
"merchantIdentifiers": ["merchant.com.example"],
"googlePay": true,
"fraudProviders": []
}
]
]
}
}Run prebuild to regenerate the native ios/ and android/ directories with the module registered:
npx expo prebuild3. Migration Guide from 1.x to 2.x.x
Initialize and mount the renderer#
Create one SDK instance and retain it across renders. Mount exactly one DeunaWidget for that instance. The component renders both modal and embedded WebView flows; calling an initializer only configures its controller.
import {useMemo} from 'react';
import {DeunaSDK, DeunaWidget} from '@deuna/react-native-sdk';
export function CheckoutScreen() {
const deunaSDK = useMemo(
() =>
DeunaSDK.initialize({
publicApiKey: 'YOUR_PUBLIC_API_KEY',
environment: 'sandbox',
}),
[]
);
return (
<>
<CheckoutContent deunaSDK={deunaSDK} />
<DeunaWidget instance={deunaSDK} />
</>
);
}Open Payment Widget#
The Payment Widget is an integrated component that allows you to offer multiple payment methods to customers:
- Credit and debit cards: Visa, Mastercard, American Express, and co-branded cards.
- Alternative payment methods (APMs): OXXO, KueskiPay, Aplazo, Daviplata, Nequi, and more.
- Digital wallets: PayPal, Apple Pay, and Google Pay.
Display in a Modal
Use Mode.MODAL (default) for an SDK-managed modal sheet. The mounted <DeunaWidget instance={deunaSDK} /> automatically presents over your screen:
import {Mode} from '@deuna/react-native-sdk';
const onShowPaymentWidget = async () => {
await deunaSDK.initPaymentWidget({
orderToken: 'YOUR_ORDER_TOKEN',
mode: Mode.MODAL,
language: 'en',
callbacks: {
onSuccess: async (order) => {
await deunaSDK.close(); // Closes modal and releases widget resources
navigation.navigate('PaymentSuccess', {order});
},
onError: (error) => showRetry(error),
onClosed: (action) => navigation.goBack(),
onPaymentProcessing: () => disablePayButton(),
onEventDispatch: (event, payload) => {
analytics.track(event, payload);
},
},
});
};Parameter Reference
| Attribute | Type | Modal | Embedded | Description |
|---|---|---|---|---|
orderToken | string | ✅ | ✅ | Required. Unique token for the order created via DEUNA API. Important: When creating the order via DEUNA API, do not define a redirect_url if you want the onSuccess callback to run inside the mobile app. |
callbacks | object | ✅ | ✅ | Required. Callback functions handling widget events (onSuccess, onError, onClosed, onCardBinDetected, onInstallmentSelected, onPaymentProcessing, onDownloadFile, onEventDispatch). |
mode | Mode | ✅ | ✅ | Mode.MODAL (default) or Mode.EMBEDDED. |
userToken | string | ✅ | ✅ | Optional. Customer's DEUNA bearer token. Important: For saved cards to appear, the email linked to userToken must match the billing_address.email passed during order creation. |
styleFile | string | ✅ | ✅ | Optional. UUID provided by DEUNA to apply custom theme branding (colors, typography, logo). |
paymentMethods | object[] | ✅ | ✅ | Optional. Array defining a single payment method to launch directly into the forms flow (e.g. [{ paymentMethod: 'bnpl', processors: ['kueski'] }]). The widget opens directly into that method's form without displaying the method selector. Note: internal checkout systems currently support one element. |
language | string | ✅ | ✅ | Optional. Language code ("es", "en", "pt"). Overrides merchant default configuration. |
behavior | object | ✅ | ✅ | Optional. Flow customization, such as behavior: { paymentMethods: { flowType: "twoStep" } } for PayPal. |
fraudCredentials | object | ✅ | ✅ | Optional. Fraud provider credentials (e.g. { RISKIFIED: { storeDomain: 'example.com' } }). |
domain | string | ✅ | ✅ | Optional. Overrides default base host (https://pay.deuna.io). |
hidePayButton | boolean | ❌ | ✅ | Optional. Hides the built-in pay button in embedded mode when your application renders a native pay button. |
Embed a widget#
To display the widget directly inside your screen layout, initialize with mode: Mode.EMBEDDED.
import { useEffect } from 'react';
import { View, Button } from 'react-native';
import { DeunaSDK, DeunaWidget, Mode } from '@deuna/react-native-sdk';
export function EmbeddedCheckoutScreen({ deunaSDK, orderToken }) {
// Release widget resources on unmount
useEffect(() => {
return () => {
deunaSDK.close();
};
}, [deunaSDK]);
const handleOpenEmbedded = async () => {
await deunaSDK.initPaymentWidget({
orderToken,
mode: Mode.EMBEDDED,
callbacks: {
onSuccess: async (order) => {
await deunaSDK.close();
navigateToReceipt(order);
},
onError: (error) => handlePaymentError(error),
},
});
};
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1, width: '100%' }}>
<DeunaWidget instance={deunaSDK} />
</View>
<Button title="Load Payment Form" onPress={handleOpenEmbedded} />
</View>
);
}Custom Pay Button (hidePayButton)
If your app renders its own pay button, pass hidePayButton: true and execute validations and submission manually:
await deunaSDK.initPaymentWidget({
orderToken,
mode: Mode.EMBEDDED,
hidePayButton: true,
callbacks,
});
const handleCustomSubmit = async () => {
// 1. Validate entered card/payment fields
const isValid = await deunaSDK.isValid();
if (!isValid) return;
// 2. Submit payment
const result = await deunaSDK.submit();
if (result.status === 'error') {
console.error('Submit failed:', result.message);
}
};| Method | Description | Return Value |
|---|---|---|
deunaSDK.isValid() | Validates user input in active payment form. | Promise<boolean> |
deunaSDK.submit() | Submits payment equivalent to clicking the widget button. | Promise<{ status: 'success' | 'error', message?: string }> |
Filter and configure payment methods#
Direct forms flow (paymentMethods)
The DEUNA checkout architectures (checkout-base and modular-checkout) support launching directly into an individual payment form—a pattern known as the forms flow.
While the SDK parameter paymentMethods is typed as an array, the internal checkout engines currently support only one element in this list. Supplying a single payment method opens the widget directly into that specific payment method form (e.g., Kueski Pay BNPL), bypassing the payment method selection screen.
await deunaSDK.initPaymentWidget({
orderToken,
paymentMethods: [
{ paymentMethod: 'bnpl', processors: ['kueski'] },
],
callbacks,
});Configuration priority resolution
When determining which payment methods and forms to display, DEUNA resolves configurations in the following order:
| Priority | Source | Behavior |
|---|---|---|
| 1 (Highest) | paymentMethods in .initPaymentWidget() | Direct forms flow: Opens immediately into the single specified payment method form (e.g., Kueski), bypassing the method selector. |
| 2 | include_payment_options in order creation | Multi-method selection: Displays only the payment methods specified during order creation that are active on your account. |
| 3 (Fallback) | Merchant-level configuration (/payment-methods) | Full selector: If omitted in both SDK call and order payload, displays all active payment methods configured for your merchant account. |
PayPal configurations
Flow Type (flowType)
Control whether PayPal displays an order summary before payment or goes directly to authorization:
await deunaSDK.initPaymentWidget({
orderToken,
behavior: {
paymentMethods: {
flowType: 'twoStep', // 'twoStep' (order summary first) or 'singleStep' (direct payment)
},
},
callbacks,
});Auto Purchase (express)
When customers have previously authorized quick payments with PayPal, enable immediate processing:
await deunaSDK.initPaymentWidget({
orderToken,
paymentMethods: [
{
paymentMethod: 'wallet',
processors: ['paypal_wallet'],
configuration: {
express: true, // Default: true. Set false to prompt user account confirmation.
},
},
],
callbacks,
});Dynamic styling and order refresh#
Dynamically customize appearance (setCustomStyle)
Update widget colors, logos, or fonts dynamically (e.g., changing header branding upon card BIN detection):
await deunaSDK.setCustomStyle({
theme: {
colors: {
primaryTextColor: '#023047',
buttonPrimaryFill: '#FFB703',
buttonPrimaryText: '#000000',
},
},
});Refresh order totals (refetchOrder)
When offering franchise-specific promotions (e.g., "15% off with Mastercard"), listen to onCardBinDetected, recalculate order totals on your backend, and notify the widget:
callbacks: {
onCardBinDetected: async (metadata) => {
if (metadata?.cardBrand === 'Mastercard') {
// 1. Notify your backend to apply discount to order
await applyDiscountOnBackend(orderId, 'MASTERCARD_15');
// 2. Refresh widget to display updated totals
const updatedOrder = await deunaSDK.refetchOrder();
console.log('Order refreshed with discount:', updatedOrder);
}
},
}Available experiences#
| Experience | Method | Important input |
|---|---|---|
| Payment Widget | initPaymentWidget(...) | orderToken, callbacks, optional mode |
| Payment Vault | initElements(...) | callbacks; types may be omitted |
| Click to Pay | initElements(...) | types: [{name: 'click_to_pay'}] |
| Next Action | initNextAction(...) | order token, callbacks, and mode |
| Voucher | initVoucherWidget(...) | order token, callbacks, and mode |
The SDK also exposes setCustomStyle(...), refetchOrder(), getWidgetState(), isValid(), submit(), and close() for the active controller.
Payment Vault and Click to Pay#
The Card Vault and Elements solution integrates card tokenization, customer wallet management, and Click to Pay without requiring an immediate payment transaction:
- Card Vault: Allows customers to securely add, store, and manage credit and debit cards for future purchases.
- Click to Pay: Offers a frictionless checkout experience leveraging global network tokens (Visa, Mastercard, etc.).
Display in a Modal
Follow these steps to display the Card Vault or Elements widget in a modal:
import { DeunaSDK, DeunaWidget, Mode } from '@deuna/react-native-sdk';
const onShowVaultModal = async () => {
await deunaSDK.initElements({
userToken: '<DEUNA_USER_TOKEN>', // Optional
userInfo: {
firstName: 'Ada',
lastName: 'Lovelace',
email: 'ada@example.com', // Required
},
types: [{ name: 'vault' }], // Default experience
mode: Mode.MODAL,
callbacks: {
onSuccess: async (data) => {
await deunaSDK.close(); // Close modal and release resources
console.log('Card tokenized successfully:', data);
},
onError: async (error) => {
console.error('Vault error:', error);
await deunaSDK.close();
},
onClosed: (action) => {
console.log('Vault closed:', action);
},
onEventDispatch: (type, response) => {
console.log('Vault event:', type, response);
},
},
widgetExperience: {
userExperience: {
showSavedCardFlow: true, // Optional: toggle for saving card
defaultCardFlow: false, // Optional: toggle for saving card as default
},
},
});
};Display in Embedded Mode
To embed the Vault or Click to Pay form inside your screen layout, pass mode: Mode.EMBEDDED. Ensure <DeunaWidget /> is wrapped in a container that defines its dimensions:
import { useEffect } from 'react';
import { View, Button } from 'react-native';
import { DeunaSDK, DeunaWidget, Mode } from '@deuna/react-native-sdk';
export function EmbeddedVaultScreen({ deunaSDK }) {
useEffect(() => {
return () => {
deunaSDK.close(); // Release resources on screen unmount
};
}, [deunaSDK]);
const handleOpenEmbeddedVault = async () => {
await deunaSDK.initElements({
mode: Mode.EMBEDDED,
userInfo: {
email: 'ada@example.com',
},
callbacks: {
onSuccess: async (data) => {
await deunaSDK.close();
console.log('Tokenization successful:', data);
},
onError: (error) => console.error(error),
},
});
};
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1, width: '100%' }}>
<DeunaWidget instance={deunaSDK} />
</View>
<Button title="Add Saved Card" onPress={handleOpenEmbeddedVault} />
</View>
);
}Parameter Reference for initElements
| Parameter | Type | Description |
|---|---|---|
callbacks | object | Required. Event listeners for onSuccess, onError, onClosed, onCardBinDetected, and onEventDispatch. |
userInfo | object | Customer information. email is required (even when passing a userToken). firstName and lastName are optional if userToken is provided. |
userToken | string | Optional. DEUNA user bearer token. Important: When passing both userToken and orderToken, the email linked to userToken must match the billing_address.email of the order to display saved cards for security reasons. |
orderToken | string | Optional. Order token when tokenizing in the context of an existing checkout order. |
types | object[] | Optional. Widget experience to render: [{ name: 'vault' }] (default) or [{ name: 'click_to_pay' }]. If omitted, defaults to Vault. |
mode | Mode | Mode.MODAL (default) or Mode.EMBEDDED. |
widgetExperience | object | Optional. Overrides merchant settings: userExperience.showSavedCardFlow (shows card saving toggle) and userExperience.defaultCardFlow (shows save card as default toggle). |
styleFile | string | Optional. UUID of a custom stylesheet/theme configured in DEUNA. |
language | string | Optional. Language code ("es", "en", "pt"). Overrides merchant default. |
hidePayButton | boolean | Optional. When embedded, hides the action button for manual execution with isValid() and submit(). |
domain | string | Optional. Overrides base host (replaces https://elements.deuna.com/vault with your custom domain). |
Click to Pay integration
To render the Click to Pay widget, set types: [{ name: 'click_to_pay' }].
await deunaSDK.initElements({
userInfo: {
firstName: 'Ada',
lastName: 'Lovelace',
email: 'ada@example.com', // Required for Click to Pay
},
types: [
{
name: 'click_to_pay',
},
],
callbacks: {
onSuccess: async (data) => {
await deunaSDK.close();
console.log('Click to Pay success:', data);
},
onError: (error) => console.error('Click to Pay error:', error),
onClosed: (action) => console.log('Widget closed:', action),
},
});Native Apple Pay and Google Pay#
Check merchant configuration and device support before rendering a wallet button. The returned values are apple_pay and google_pay.
const userInfo = {email: 'ada@example.com'};
const wallets = await deunaSDK.getWalletsAvailable({
orderToken,
userInfo,
});
setShowApplePay(wallets.includes('apple_pay'));
setShowGooglePay(wallets.includes('google_pay'));After availability succeeds, launch the native sheet from the button press. For Apple Pay, keep this call directly in the user gesture.
const launchWallet = (provider: 'apple_pay' | 'google_pay') => {
const type = provider === 'apple_pay' ? 'APPLE_PAY' : 'GOOGLE_PAY';
deunaSDK.initElements({
orderToken,
userInfo,
types: [{name: type}],
callbacks: {
onSuccess: (credential) => useSavedCredential(credential),
onError: (error) => showRetry(error),
onClosed: () => showPaymentMethods(),
},
});
};Apple Pay requires the iOS entitlement and merchant identifier. Google Pay requires merchant enablement and an eligible Android device. See Apple Pay and Google Pay.
Continue a pending action#
Use the same order token when DEUNA reports that the payment requires a supported 3DS challenge or redirect.
await deunaSDK.initNextAction({
orderToken,
mode: Mode.MODAL,
callbacks: {
onSuccess: (order) => showConfirmation(order),
onError: (error) => showRetry(error),
onClosed: () => showPaymentMethods(),
},
});Open a voucher#
await deunaSDK.initVoucherWidget({
orderToken,
mode: Mode.MODAL,
callbacks: {
onSuccess: (order) => showVoucherInstructions(order),
onError: (error) => showRetry(error),
onClosed: () => showPaymentMethods(),
onDownloadFile: (file) => saveVoucher(file),
},
});Handle external redirects#
Starting from v2.0.0, payment methods that require native browser functionality (such as Mercado Pago Wallet) must open in Custom Tabs (Android) or Safari View Controller (iOS) to ensure secure session handling and cookie persistence. To support this, supply an inAppBrowserAdapter implementing the InAppBrowserAdapter interface during DeunaSDK.initialize.
Option A: Using @deuna/react-native-in-app-browser (Metro / Expo)
If using Metro or Expo, install @deuna/react-native-in-app-browser and pass ExpoWebBrowserAdapter:
import { DeunaSDK } from '@deuna/react-native-sdk';
import { ExpoWebBrowserAdapter } from '@deuna/react-native-in-app-browser';
const deunaSDK = DeunaSDK.initialize({
publicApiKey: 'YOUR_PUBLIC_API_KEY',
environment: 'sandbox',
inAppBrowserAdapter: new ExpoWebBrowserAdapter(),
});Option B: Using react-native-inappbrowser-reborn (Webpack / Custom)
If using Webpack or custom bundlers incompatible with Expo modules, use react-native-inappbrowser-reborn:
npm install react-native-inappbrowser-reborn
# or
yarn add react-native-inappbrowser-rebornImplement the adapter:
import { Linking } from 'react-native';
import InAppBrowser from 'react-native-inappbrowser-reborn';
import { InAppBrowserAdapter, DeunaSDK } from '@deuna/react-native-sdk';
class InAppBrowserRebornAdapter implements InAppBrowserAdapter {
async openUrl(url: string): Promise<void> {
try {
if (await InAppBrowser.isAvailable()) {
await InAppBrowser.open(url, {
dismissButtonStyle: 'cancel',
preferredBarTintColor: '#023047',
preferredControlTintColor: '#FFFFFF',
readerMode: false,
animated: true,
});
} else {
// Fallback to external browser
await Linking.openURL(url);
}
} catch (error) {
console.error('Error opening in-app browser:', error);
throw error;
}
}
}
const deunaSDK = DeunaSDK.initialize({
publicApiKey: 'YOUR_PUBLIC_API_KEY',
environment: 'sandbox',
inAppBrowserAdapter: new InAppBrowserRebornAdapter(),
});Fraud-device data and fingerprinting#
Use the DeunaSDK.initializeFraudProviders(...) static function to warm up providers in the background, and the deunaSDK.generateFraudId(...) instance method (or the fraudCredentials parameter in widget initializers) to generate a unified DEUNA fraud payload.
The DEUNA React Native SDK uses a modular, reflection-based architecture (zero-bloat): it does not bundle third-party fraud SDKs by default. Only add the specific provider(s) you plan to use. If you do not use a provider, you pay zero app size penalty.
Supported providers
| Provider | Description | Required Parameters |
|---|---|---|
MERCADOPAGO | Mercado Pago device behavior profiling | (None required) |
RISKIFIED | Riskified Beacon beaconing | storeDomain |
CYBERSOURCE | CyberSource ThreatMetrix profiling | orgId, merchantId (Optional: fpServer) |
SIGNIFYD | Signifyd ThreatMetrix profiling | orgId (Optional: fpServer) |
ACCERTIFY | Accertify InMobile profiling | Config files in assets/bundle (Optional: locationConsent, phoneConsent) |
KOUNT | Kount Data Collector | merchantId, clientId, dataCollectorUrl, environment |
SIFT | Sift Science device profiling | accountId, beaconKey |
Dependency linking
Follow the setup corresponding to your project environment (Expo or React Native CLI).
A. Expo (Config Plugin)
In Expo projects using Continuous Native Generation (CNG) or EAS Build, enable only the providers you have contracted by listing them in the fraudProviders array in app.json:
{
"expo": {
"plugins": [
[
"@deuna/react-native-sdk",
{
"fraudProviders": [
"mercadopago",
"riskified"
]
}
]
]
}
}Supported keys in fraudProviders: "mercadopago", "riskified", "cybersource", "signifyd", "accertify", "kount", "sift".
After updating app.json, regenerate the native projects:
npx expo prebuild --cleanB. React Native CLI
In standard React Native CLI projects, add native dependencies only for the providers you need in your host application files:
Android configuration
- Mercado Pago Maven repository: If using Mercado Pago, ensure the Mercado Libre Maven repository is included in
android/build.gradle(orsettings.gradle):
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://artifacts.mercadolibre.com/repository/android-releases/" }
}
}-
Vendor AAR placement: If using Cybersource, Signifyd, or Accertify, download the vendor AARs and place them in
android/app/libs/:- Cybersource: Place AAR files in
android/app/libs/cybersource/. - Signifyd: Place AAR files in
android/app/libs/signifyd/. - Accertify: Place
inmobileandinmobile-commonAAR files inandroid/app/libs/accertify/.
- Cybersource: Place AAR files in
-
Dependencies: Add only the providers you require inside
android/app/build.gradleunderdependencies { ... }:
dependencies {
// --- Add ONLY the providers you use ---
// Mercado Pago
implementation("com.mercadolibre.android.device:sdk:4.0.1")
// Riskified
implementation("com.riskified:android-sdk:1.11.0:nogoogle@aar")
// CyberSource (AARs placed in app/libs/cybersource)
implementation(fileTree(dir: "libs/cybersource", include: ["*.aar"]))
// Signifyd (AARs placed in app/libs/signifyd)
implementation(fileTree(dir: "libs/signifyd", include: ["*.aar"]))
// Accertify (AARs placed in app/libs/accertify + required transitive dependencies)
implementation(fileTree(dir: "libs/accertify", include: ["*.aar"]))
implementation("io.insert-koin:koin-core:3.5.6")
implementation("androidx.biometric:biometric:1.1.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
// Kount
implementation("com.kount:kount-data-collector:4.1.0")
// Sift
implementation("sift-android:sift-android:0.14.0")
}- Accertify assets:
- Android: Place
server_keys_message_hosted.jsonandinmobile.propertiesinandroid/app/src/main/assets/. - iOS: Add
server_keys_message_hosted.jsonandInMobile.plistto your Xcode project under your app's Copy Bundle Resources target.
- Android: Place
iOS configuration (ios/Podfile)
Inside your target 'YourApp' do block, add only the subspecs for the providers you need:
target 'YourApp' do
# Add ONLY the subspecs you need:
pod 'DeunaSDK/MercadoPago'
pod 'DeunaSDK/Riskified'
pod 'DeunaSDK/Cybersource'
pod 'DeunaSDK/Signifyd'
pod 'DeunaSDK/Kount'
pod 'DeunaSDK/Sift'
pod 'DeunaSDK/Accertify'
endThen run:
cd ios && pod installProvider details and constraints
RISKIFIED
- Required:
storeDomain - Configuration example:
JSON
{ "RISKIFIED": { "storeDomain": "yourdomain.com" } }
CYBERSOURCE
- Required:
orgId,merchantId - Optional:
fpServer(default:h.online-metrix.net) - Configuration example:
JSON
{ "CYBERSOURCE": { "orgId": "your_org_id", "merchantId": "your_merchant_id" } }
SIGNIFYD
- Required:
orgId - Optional:
fpServer(default:imgs.signifyd.com) - Configuration example:
JSON
{ "SIGNIFYD": { "orgId": "your_org_id" } }
ACCERTIFY
- Optional:
locationConsent(default:true),phoneConsent(default:true) - Required Files: The public encryption key file
server_keys_message_hosted.jsonand endpoint properties (inmobile.propertiesin Androidsrc/main/assets/andInMobile.plistin the iOS bundle resources) must be obtained from Accertify during merchant onboarding. - Transitive Dependencies (Android): The Accertify AAR requires Koin DI (
io.insert-koin:koin-core:3.5.6), AndroidX Biometric (androidx.biometric:biometric:1.1.0), and Kotlinx Serialization (org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3). - Configuration example:
JSON
{ "ACCERTIFY": { "locationConsent": true, "phoneConsent": false } }
KOUNT
- Required:
merchantId,clientId,dataCollectorUrl,environment - Configuration example:
JSON
{ "KOUNT": { "merchantId": "your_merchant_id", "clientId": "your_client_id", "dataCollectorUrl": "https://...", "environment": "TEST" } }
SIFT
- Required:
accountId,beaconKey - Configuration example:
JSON
{ "SIFT": { "accountId": "your_account_id", "beaconKey": "your_beacon_key" } }
MERCADOPAGO
- Required: None (
{}) - Android: Requires the Mercado Libre Maven repository (
https://artifacts.mercadolibre.com/repository/android-releases/) and dependencycom.mercadolibre.android.device:sdk:4.0.1. - iOS: Linked via
pod 'DeunaSDK/MercadoPago'. - Configuration example:
JSON
{ "MERCADOPAGO": {} }
Early background initialization
Because initializeFraudProviders is a static method, it can be executed in the background before instantiating or launching any checkout widget:
import { useEffect } from 'react';
import { DeunaSDK } from '@deuna/react-native-sdk';
const CartScreen = () => {
useEffect(() => {
// Pre-warm fraud SDKs as soon as the cart or store view mounts
DeunaSDK.initializeFraudProviders({
MERCADOPAGO: {},
});
}, []);
return (
// Your cart UI components
);
};Implementation options
Option 1: Direct call via instance method (generateFraudId)
generateFraudId is an instance method on your initialized DeunaSDK instance. It automatically uses your configured publicApiKey and environment:
import { DeunaSDK } from '@deuna/react-native-sdk';
const deunaSDK = DeunaSDK.initialize({
publicApiKey: 'YOUR_PUBLIC_API_KEY',
environment: 'production',
});
async function getFraudToken() {
const fraudId = await deunaSDK.generateFraudId({
RISKIFIED: {
storeDomain: 'yourdomain.com',
},
MERCADOPAGO: {},
});
// Returns a Base64-encoded JSON string or null
console.log('Base64 Fraud ID:', fraudId);
if (fraudId) {
const decoded = atob(fraudId);
console.log('Decoded Tokens:', JSON.parse(decoded));
}
}Option 2: Pass fraudCredentials directly to widgets
When using DEUNA payment widgets (initPaymentWidget, initElements, initNextAction, initVoucherWidget), pass your provider configuration directly via fraudCredentials. The SDK calls generateFraudId internally at purchase time:
await deunaSDK.initPaymentWidget({
orderToken: 'YOUR_ORDER_TOKEN',
mode: Mode.MODAL,
callbacks: {
onSuccess: (data) => console.log('Payment success:', data),
onError: (error) => console.error('Payment error:', error),
onClosed: () => console.log('Widget closed'),
},
fraudCredentials: {
RISKIFIED: {
storeDomain: 'yourdomain.com',
},
MERCADOPAGO: {},
},
});Behavior and runtime characteristics
- Zero-bloat & Crash-safe: If a provider is included in the JSON request but its native dependency was not added to the app, the native reflection safely ignores it without throwing an unhandled exception or crashing.
- Parallel Execution: All requested providers profile concurrently on background threads.
- Fallback Behavior: If no valid providers are configured or available on the device,
generateFraudIdresolves tonull.
Callback and lifecycle reference#
The initPaymentWidget and Elements initializers accept typed callbacks to observe payment progress, update UI, and release resources.
| Callback | Modal | Embedded | When is it triggered? |
|---|---|---|---|
onSuccess | ✅ | ✅ | Payment succeeded. Contains order data JSON. Always call await deunaSDK.close() to release widget resources. |
onError | ✅ | ✅ | Error occurred during loading or checkout processing. Receives a typed PaymentsError. |
onClosed | ✅ | ❌ | Modal closed. Receives userAction (dismissed by customer) or systemAction (closed via close()). Note: not triggered in embedded mode. |
onPaymentProcessing | ✅ | ✅ | Customer clicked the pay button and processing has begun. Use this to disable buttons and show progress indicators. |
onCardBinDetected | ✅ | ✅ | Triggered when a card BIN is detected or cleared (returns null when user deletes the number). Use for franchise discounts. |
onInstallmentSelected | ✅ | ✅ | Triggered when customer selects an installment plan (returns null on full one-time payment). |
onDownloadFile | ✅ | ✅ | Triggered when customer requests voucher or receipt download. |
onEventDispatch | ✅ | ✅ | Emits all underlying CheckoutEvent occurrences for custom logging and analytics. |
Handling errors (onError)
Inspect the error type to distinguish initialization failures from payment declines:
onError: async (error) => {
switch (error.type) {
case 'INITIALIZATION_FAILED':
// Widget could not load; release controller and retry or notify user
await deunaSDK.close();
showToast('Payment system currently unavailable');
break;
case 'PAYMENT_ERROR':
// Card declined or processor rejection
showRetryScreen(error.message);
break;
default:
console.warn('Unhandled payment error:', error);
break;
}
}Handling file downloads (onDownloadFile)
When voucher or cash payment instructions offer receipts or barcodes:
callbacks: {
onDownloadFile: (file) => {
const { type, data } = file;
if (type === 'url') {
// Download or open external receipt URL
Linking.openURL(data);
} else if (type === 'base64') {
// Save base64 image or PDF to local file system
saveBase64ToGallery(data);
}
},
}Releasing resources (close)
The close() method releases internal WebView controllers and clears event subscriptions. Always call deunaSDK.close() in onSuccess and in screen unmount hooks (useEffect cleanup) for embedded widgets.
Official examples and source#
See ModalScreen.tsx, EmbeddedScreen.tsx, and WalletsScreen.tsx for complete runnable flows.