> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://amer.developers.trustly.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://amer.developers.trustly.com/_mcp/server.

# React Native

The Trustly React Native SDK allows you to build a bank authorization workflow in your mobile app. You can use the SDK to initiate a bank authorization flow using either the Select Bank Widget or the Trustly Lightbox.

The SDK handles the complexities of OAuth flows and bank redirection, ensuring a seamless user experience on both iOS and Android.

## Prerequisites

* [Node.js](https://nodejs.org/en/download/): Version 22.23.0 or later
* [React Native](https://reactnative.dev/docs/environment-setup): Version 0.76.9 or later
* [React](https://react.dev/learn/installation): Version 18.3.1 or later
* iOS: [iOS 12+](https://developer.apple.com/ios/) and [Xcode 14+](https://developer.apple.com/xcode/) (for iOS builds)
* Android: [Android Studio](https://developer.android.com/studio) (for Android builds)

## Authentication flow

The following diagram illustrates how the Trustly SDK manages the secure transition between your application, the SDK, and the banking institution.

```mermaid
sequenceDiagram
    participant User
    participant App
    participant SDK as Trustly SDK
    participant System as System / Bank

    User->>App: Start
    App->>SDK: Initialize
    SDK-->>System: Open Auth [A]
    
    Note right of System: Login
    
    System-->>App: Redirect [B]
    App->>SDK: Verify
    SDK-->>App: Callback [C]
    App-->>User: Success
```

| Step  | Description                                                                                                                                                                                                                                                       |
| :---- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **A** | **Authentication session launch** The SDK opens a secure system overlay. On iOS, this is `ASWebAuthenticationSession`. On Android, this uses Custom Tabs or direct App-to-App switching.                                                                          |
| **B** | **App redirection** The bank redirects the user back to your application using the configured Universal Link (iOS) or App Link (Android). For example, `https://yourdomain.com/trustly-return`. The OS verifies the domain and brings your app to the foreground. |
| **C** | **SDK handoff** Your app detects the return and the SDK verifies the transaction status, triggering the `onReturn` function defined in your component.                                                                                                            |

## Add the package

To install the core Trustly SDK, which includes the Lightbox UI components and the native bridges required to communicate with banking apps, go to your project's root directory and run the following command:

```bash
npm install @trustlyinc/trustly-react-native
```

Then install the required peer dependencies:

```bash
npm install react react-native react-native-inappbrowser-reborn react-native-webview @react-native-async-storage/async-storage react-native-get-random-values --legacy-peer-deps
```

## Install iOS CocoaPods

If you are building for iOS, you must install the CocoaPods dependencies to integrate the SDK's native modules. Navigate to your iOS directory and run:

```bash
cd ios && pod install && cd ..
```

## Set up deep links

To handle bank logins securely, the Trustly SDK uses `ASWebAuthenticationSession` on iOS and direct App-to-App interactions on Android. You must configure a Universal Link (iOS) or App Link (Android) to handle redirects back to your app after a user authenticates with their bank. Pass your deep link URL as `metadata.deepLinkUrl` in `establishData`.

If your app does not already have a Universal Link (iOS) or App Link (Android) configured, you must set one up. Without it, users will not be automatically redirected to your app after logging in on a mobile banking app.

### iOS

[Universal Links](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/) use standard HTTPS URLs to return users directly to your app after bank authentication. Unlike custom URL schemes, they are verified against your domain — preventing other apps from intercepting them — and fall back to your website if the app isn't installed.

#### Define the associated domains

Create a JSON file named `apple-app-site-association` and host it at one of the following locations on your server:

* **Root**: `https://yourdomain.com/apple-app-site-association`
* **Subdirectory**: `https://yourdomain.com/.well-known/apple-app-site-association`

Server requirements:

* Served over HTTPS.
* `Content-Type` header set to `application/json`.
* Filename must have no extension.

```json
{
  "applinks": {
    "apps": [
      "ABCDE12345.com.yourcompany.YourApp"
    ],
    "details": [
      {
        "appID": "ABCDE12345.com.yourcompany.YourApp",
        "paths": [
          "/trustly-return",
          "*"
        ]
      }
    ]
  }
}
```

| Key     | Description                                                                                                                  |
| :------ | :--------------------------------------------------------------------------------------------------------------------------- |
| `apps`  | An array of application identifiers. Detailed matching is handled in `details`.                                              |
| `appID` | Your app's unique identifier in the format `<Team ID>.<Bundle Identifier>`. Find your Team ID in the Apple Developer Portal. |
| `paths` | URL paths the app should handle. Use `*` as a catch-all, or `NOT /path` to exclude a path from opening the app.              |

Verify your server returns the correct `Content-Type`:

```bash
curl -I https://yourdomain.com/.well-known/apple-app-site-association
```

#### Add the Associated Domains entitlement

1. Open your project in Xcode and select your application target.
2. Go to **Signing & Capabilities**.
3. Click **+ Capability** and select **Associated Domains**.
4. Add your domains, prefixed with `applinks:`:

```text
applinks:yourdomain.com
applinks:www.yourdomain.com
```

Adding this capability automatically inserts the `com.apple.developer.associated-domains` key into your app's entitlements file.

#### Handle the Universal Link return

Unlike standard deep link routing, the Trustly SDK manages the return from the bank natively. On iOS, the SDK leverages `ASWebAuthenticationSession` via the InAppBrowser module. This securely overlays the bank's login page and intercepts the redirect URL at the OS level once authentication is complete.

Because of this native integration, you do not need to manually intercept the URL in your `AppDelegate` or add React Native `Linking` event listeners.

To ensure the return works seamlessly:

* Make sure your configured Universal Link matches the `deepLinkUrl` value you pass in the `establishData` object.
* Once the bank redirects the user to this URL, the SDK will automatically capture the callback, dismiss the secure browser, and trigger the `onReturn` or `onCancel` functions defined in your `TrustlyLightbox` component.

```javascript
<TrustlyLightbox
  establishData={establishData}
  onReturn={() => {
    // Called automatically when the bank redirects back to your app
  }}
  onCancel={() => {
    // Called automatically if the user cancels
  }}
/>
```

### Android

[Android App Links](https://developer.android.com/training/app-links) use standard HTTPS URLs to return users directly to your app after bank authentication. Unlike custom URL schemes, App Links are verified against your domain via Digital Asset Links, preventing other apps from intercepting them, and fall back to your website if the app isn't installed.

#### Define the digital asset links

Create a JSON file named `assetlinks.json` and host it at: `https://yourdomain.com/.well-known/assetlinks.json`

Server requirements:

* Served over HTTPS.
* `Content-Type` header set to `application/json`.

```json
[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourapp",
      "sha256_cert_fingerprints": [
        "4A:34:B4:72:DE:F7:..."
      ]
    }
  }
]
```

| Key                        | Description                                                                                                                                       |
| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
| `relation`                 | Permissions being granted. Use `delegate_permission/common.handle_all_urls` for deep links.                                                       |
| `namespace`                | The namespace of your application. Typically `android_app`.                                                                                       |
| `package_name`             | The unique application ID defined in your `build.gradle` file.                                                                                    |
| `sha256_cert_fingerprints` | The SHA-256 fingerprint of your app's signing certificate. Retrieve it with `keytool` or via the **signingReport** Gradle task in Android Studio. |

Verify your server returns the correct `Content-Type`:

```bash
curl -I https://yourdomain.com/.well-known/assetlinks.json
```

#### Configure the manifest

Add an intent filter with `autoVerify="true"` to your `AndroidManifest.xml`. Android uses the following attributes to handle App Links:

* **`android:exported="true"`** — Required to allow your activity to be started by external app links.
* **`android:autoVerify="true"`** — Instructs Android to verify your domain ownership by checking `assetlinks.json` at install time.
* **`<data>` elements** — Define the HTTPS scheme, domain, and path that trigger your activity.

```xml
<activity
    android:name="com.trustly.rnsdk.TrustlyRedirectActivity"
    android:exported="true">

    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <!-- App Links -->
        <data android:scheme="https" android:host="yourdomain.com" android:pathPrefix="/trustly-return" />
    </intent-filter>

</activity>
```

## Create the `establishData` object

To initialize the Trustly Lightbox, you must create an `establishData` object. This object acts as the configuration payload for the SDK and includes transaction details and the `requestSignature`, which must be generated by your backend for security. For more information, see [Generate request signatures](/integrate/api-fundamentals/secure-requests-and-signature-validation/generate-request-signatures).

You define the `establishData` object within your main component — for example, `App.tsx` or the screen that handles the payment logic.

```javascript
const establishData = {
  accessId: "YOUR_ACCESS_ID",
  merchantId: "YOUR_MERCHANT_ID",
  requestSignature: "GENERATED_HASH_FROM_BACKEND",
  description: "Transaction description",
  merchantReference: "UNIQUE_TRANSACTION_REF",
  amount: "1.00",
  paymentType: "Retrieval",
  currency: "USD",
  returnUrl: "/returnUrl",
  cancelUrl: "/cancelUrl",
  customer: {
    name: "John Doe",
    address: { country: "US" }
  },
  metadata: {
    deepLinkUrl: Platform.OS === "android" ? 
      "intent://yourdomain.com/trustly-return/#Intent;scheme=https;end" : 
      "https://yourdomain.com/trustly-return",
  },
  env: "sandbox"
};
```

When using the sandbox environment, set the `env` property to `sandbox`. Before publishing your production application, remove the `env` property.

### Required properties

| Property               | Type   | Description                                                                                            |
| :--------------------- | :----- | :----------------------------------------------------------------------------------------------------- |
| `accessId`             | string | The Access ID provided by Trustly.                                                                     |
| `merchantId`           | string | Your Merchant ID.                                                                                      |
| `requestSignature`     | string | A signature generated by your backend to validate the request.                                         |
| `merchantReference`    | string | A unique reference for the transaction.                                                                |
| `amount`               | string | The transaction amount. For example, `10.00`.                                                          |
| `currency`             | string | The currency code. For example, `USD`.                                                                 |
| `paymentType`          | string | The type of payment. For example, `Retrieval` or `Deferred`.                                           |
| `returnUrl`            | string | The URL path to return to on success.                                                                  |
| `cancelUrl`            | string | The URL path to return to on cancel.                                                                   |
| `metadata.deepLinkUrl` | string | Your Universal Link (iOS) or App Link (Android). For example, `https://yourdomain.com/trustly-return`. |

## Add the SDK components

You can integrate the SDK in two ways: displaying the Bank Selection Widget first, or launching the Trustly Lightbox directly.

### Display the Select Bank Widget

Add `TrustlyWidget` to your component's return statement to allow users to select their bank directly within your app's UI.

The `onBankSelected` callback triggers a state change. You must use this state to conditionally render the `TrustlyLightbox` component, as shown in the following example.

```javascript
import React, { useState } from 'react';
import { View } from 'react-native';
import { TrustlyWidget, TrustlyLightbox } from 'trustly-react-native';

// const establishData = { ... } // See "Create the establishData object" above

const PaymentScreen = () => {
  const [lightboxData, setLightboxData] = useState(null);
  const [selectedBankId, setSelectedBankId] = useState(null);
  const [showLightbox, setShowLightbox] = useState(false);

  const handleBankSelected = (bankId, updatedEstablishData) => {
    setSelectedBankId(bankId);
    setLightboxData(updatedEstablishData);
    setShowLightbox(true);
  };

  const handleReturn = () => {
    console.log('Authorization completed');
    setShowLightbox(false);
  };

  const handleCancel = () => {
    console.log('User canceled');
    setShowLightbox(false);
  };

  return (
    <View>
      {/* Show Widget only when Lightbox is NOT active */}
      {!showLightbox && (
        <TrustlyWidget
          establishData={establishData}
          onBankSelected={handleBankSelected}
        />
      )}

      {/* Conditionally render Lightbox based on state */}
      {showLightbox && (
        <TrustlyLightbox
          establishData={lightboxData}
          paymentProviderId={selectedBankId}
          onReturn={handleReturn}
          onCancel={handleCancel}
        />
      )}
    </View>
  );
};
```

### Launch the Trustly Lightbox

Add the following code to your component's return statement to bypass the Select Bank Widget and launch the Trustly Lightbox immediately or from your own custom button:

```javascript
import { TrustlyLightbox } from 'trustly-react-native';

// Inside your component render
<TrustlyLightbox
  establishData={establishData}
  onReturn={handleReturn}
  onCancel={handleCancel}
/>
```

## Handle callbacks

Define the callback functions inside your component to handle the transaction result.

Ensure the state setter (for example, `setShowLightbox`) matches the variable name defined in your component's state.

```javascript
// Place these functions inside your component, before the return statement
const handleReturn = () => {
  console.log('Authorization completed successfully');
  setShowLightbox(false);
  // Navigate to success screen
};

const handleCancel = () => {
  console.log('User canceled the process');
  setShowLightbox(false);
  // Navigate to cancel screen or close modal
};
```

## Troubleshooting

Use the information provided here to resolve configuration and integration issues on iOS and Android platforms.

### iOS

* **App does not return after auth:** Verify that the `metadata.deepLinkUrl` in your `establishData` matches a path covered by your `apple-app-site-association` file and that the Associated Domains entitlement is configured in Xcode.
* **Universal Link opens browser instead of app:** Confirm your server is serving `apple-app-site-association` over HTTPS with `Content-Type: application/json` and no file extension.
* **CocoaPods architecture errors on Apple Silicon:** React Native 0.79+ supports arm64 natively. If you encounter incompatible architecture errors or FFI gem issues during `pod install`, try running the command with the `arch -x86_64` prefix as a fallback:

  ```bash
  cd ios && arch -x86_64 pod install && cd ..
  ```

### Android

* **App Link not working:** Confirm `android:autoVerify="true"` is set on your intent filter and your `assetlinks.json` is reachable at `https://yourdomain.com/.well-known/assetlinks.json` with the correct `Content-Type: application/json`.
* **WebView not loading:** Ensure you have requested internet permissions in `AndroidManifest.xml`:

  ```xml
  <uses-permission android:name="android.permission.INTERNET" />
  ```

### General

* **Signature error:** Ensure the `requestSignature` is being generated correctly by your backend using your access key. See [Generate request signatures](/integrate/api-fundamentals/secure-requests-and-signature-validation/generate-request-signatures).
* **Module not found:** If you encounter resolution errors, try resetting your cache:

  ```bash
  npm start -- --reset-cache
  ```