Skip to main content
On this page

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 24 or 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-sdk

Add the required react-native-webview (≥ 13.15.0) peer dependency:

yarn add react-native-webview

2. 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:

Shell
cd ios && pod install

Expo (managed workflow)

Add the config plugin to app.json. Supply only the wallet entitlements and fraud providers your application uses:

app.jsonJSON
{
  "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:

Shell
npx expo prebuild

3. 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.

JavaScript
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:

JavaScript
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

AttributeTypeModalEmbeddedDescription
orderTokenstring✅✅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.
callbacksobject✅✅Required. Callback functions handling widget events (onSuccess, onError, onClosed, onCardBinDetected, onInstallmentSelected, onPaymentProcessing, onDownloadFile, onEventDispatch).
modeMode✅✅Mode.MODAL (default) or Mode.EMBEDDED.
userTokenstring✅✅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.
styleFilestring✅✅Optional. UUID provided by DEUNA to apply custom theme branding (colors, typography, logo).
paymentMethodsobject[]✅✅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.
languagestring✅✅Optional. Language code ("es", "en", "pt"). Overrides merchant default configuration.
behaviorobject✅✅Optional. Flow customization, such as behavior: { paymentMethods: { flowType: "twoStep" } } for PayPal.
fraudCredentialsobject✅✅Optional. Fraud provider credentials (e.g. { RISKIFIED: { storeDomain: 'example.com' } }).
domainstring✅✅Optional. Overrides default base host (https://pay.deuna.io).
hidePayButtonboolean❌✅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.

JavaScript
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:

JavaScript
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);
  }
};
MethodDescriptionReturn 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.

JavaScript
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:

PrioritySourceBehavior
1 (Highest)paymentMethods in .initPaymentWidget()Direct forms flow: Opens immediately into the single specified payment method form (e.g., Kueski), bypassing the method selector.
2include_payment_options in order creationMulti-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:

JavaScript
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:

JavaScript
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):

JavaScript
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:

JavaScript
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#

ExperienceMethodImportant input
Payment WidgetinitPaymentWidget(...)orderToken, callbacks, optional mode
Payment VaultinitElements(...)callbacks; types may be omitted
Click to PayinitElements(...)types: [{name: 'click_to_pay'}]
Next ActioninitNextAction(...)order token, callbacks, and mode
VoucherinitVoucherWidget(...)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:

JavaScript
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:

JavaScript
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

ParameterTypeDescription
callbacksobjectRequired. Event listeners for onSuccess, onError, onClosed, onCardBinDetected, and onEventDispatch.
userInfoobjectCustomer information. email is required (even when passing a userToken). firstName and lastName are optional if userToken is provided.
userTokenstringOptional. 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.
orderTokenstringOptional. Order token when tokenizing in the context of an existing checkout order.
typesobject[]Optional. Widget experience to render: [{ name: 'vault' }] (default) or [{ name: 'click_to_pay' }]. If omitted, defaults to Vault.
modeModeMode.MODAL (default) or Mode.EMBEDDED.
widgetExperienceobjectOptional. Overrides merchant settings: userExperience.showSavedCardFlow (shows card saving toggle) and userExperience.defaultCardFlow (shows save card as default toggle).
styleFilestringOptional. UUID of a custom stylesheet/theme configured in DEUNA.
languagestringOptional. Language code ("es", "en", "pt"). Overrides merchant default.
hidePayButtonbooleanOptional. When embedded, hides the action button for manual execution with isValid() and submit().
domainstringOptional. 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' }].

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
await deunaSDK.initNextAction({
  orderToken,
  mode: Mode.MODAL,
  callbacks: {
    onSuccess: (order) => showConfirmation(order),
    onError: (error) => showRetry(error),
    onClosed: () => showPaymentMethods(),
  },
});

Open a voucher#

JavaScript
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:

JavaScript
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:

Shell
npm install react-native-inappbrowser-reborn
# or
yarn add react-native-inappbrowser-reborn

Implement the adapter:

JavaScript
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

ProviderDescriptionRequired Parameters
MERCADOPAGOMercado Pago device behavior profiling(None required)
RISKIFIEDRiskified Beacon beaconingstoreDomain
CYBERSOURCECyberSource ThreatMetrix profilingorgId, merchantId (Optional: fpServer)
SIGNIFYDSignifyd ThreatMetrix profilingorgId (Optional: fpServer)
ACCERTIFYAccertify InMobile profilingConfig files in assets/bundle (Optional: locationConsent, phoneConsent)
KOUNTKount Data CollectormerchantId, clientId, dataCollectorUrl, environment
SIFTSift Science device profilingaccountId, 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:

app.jsonJSON
{
  "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:

Shell
npx expo prebuild --clean

B. 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
  1. Mercado Pago Maven repository: If using Mercado Pago, ensure the Mercado Libre Maven repository is included in android/build.gradle (or settings.gradle):
GROOVY
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://artifacts.mercadolibre.com/repository/android-releases/" }
    }
}
  1. 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 inmobile and inmobile-common AAR files in android/app/libs/accertify/.
  2. Dependencies: Add only the providers you require inside android/app/build.gradle under dependencies { ... }:

android/app/build.gradleGROOVY
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")
}
  1. Accertify assets:
    • Android: Place server_keys_message_hosted.json and inmobile.properties in android/app/src/main/assets/.
    • iOS: Add server_keys_message_hosted.json and InMobile.plist to your Xcode project under your app's Copy Bundle Resources target.
iOS configuration (ios/Podfile)

Inside your target 'YourApp' do block, add only the subspecs for the providers you need:

ios/PodfileRuby
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'
end

Then run:

Shell
cd ios && pod install

Provider 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.json and endpoint properties (inmobile.properties in Android src/main/assets/ and InMobile.plist in 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 dependency com.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:

JavaScript
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:

JavaScript
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:

JavaScript
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, generateFraudId resolves to null.

Callback and lifecycle reference#

The initPaymentWidget and Elements initializers accept typed callbacks to observe payment progress, update UI, and release resources.

CallbackModalEmbeddedWhen 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:

JavaScript
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:

JavaScript
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.

In this section