> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/nfc-payment/integrate-d1-sdk/getting-started/multiplatform-technology/integrate-d1-sdk-with-react-native.md).

# Integrate D1 SDK with React Native

### Overview

React Native combines native Android and iOS application development with the JavaScript library React to build user interfaces.

To integrate D1 SDK using React Native, refer to the following sections:

* [Integrating a Native SDK to a React Native Application](#integrating-a-native-sdk-to-a-react-native-application)
* [Integrating D1 SDK to a React Native Application](#integrating-d1-sdk-to-a-react-native-application)
  * [Android platform](#android-platform)
  * [iOS platform](#ios-platform)
  * [Calling the D1 Plugin API from the React Native Project](#calling-the-d1-plugin-api-from-the-react-native-project)
* [References](#references)

### Integrating a Native SDK to a React Native Application

In React Native, the Android and iOS platforms are generated only once.

There are two ways to integrate third party SDKs and native components:

* A simple approach: Update the generated native projects directly.
* A more modular and universal approach: Create a standalone module, which is then linked and added to the React Native application.

For simplicity, this guide will focus only on the first option.

### Integrating D1 SDK to a React Native Application

This guide was tested with:

* D1 SDK
  * 4.0.0 Android
  * 4.0.0 iOS
* React
  * React 18.2.0
  * React Native 0.72.1
* Xcode 14.3.1
* Android Studio: 2022.1

To create a new React Native application:

{% code title="Terminal" %}

```
>> npx react-native init D1Sample
```

{% endcode %}

#### Android platform

{% stepper %}
{% step %}

### Add the D1 SDK binaries to the generated Android project.

```
android
└── app
    └── libs
        ├── d1-debug.aar
        └── d1-release.aar
```

{% code title="android/build.gradle" %}

```
allprojects {
    repositories {
        flatDir{
            dirs 'libs'
        }
    }
}
```

{% endcode %}

{% code title="android/app/build.gradle" %}

```
dependencies {

    releaseImplementation(name: 'd1-release', ext: 'aar')
    debugImplementation(name: 'd1-debug', ext: 'aar')

    // add other dependecies
    implementation "com.google.code.gson:gson:2.8.9"
    implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
    implementation "net.java.dev.jna:jna:5.5.0@aar"
    implementation "com.google.code.gson:gson:2.8.9"
    implementation "com.google.android.gms:play-services-base:18.0.1"
    implementation "androidx.appcompat:appcompat:[1.0.0,1.3.1]"
    implementation platform('com.google.firebase:firebase-bom:30.1.0')
    implementation 'com.google.firebase:firebase-messaging'
}
```

{% endcode %}
{% endstep %}

{% step %}

### Update the minimum SDK version supported to 24.

{% code title="android/build.gradle" %}

```
buildscript {
    ext {
        minSdkVersion = 24
    }
}
```

{% endcode %}
{% endstep %}

{% step %}

### Add the NFC Wallet configuration files.

For more information, see [NFC Wallet configuration files](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/2.-onboarding/nfc-payment/set-up-nfc-payment-on-application).

```
android/app/src/main/assets/
├── gemcbp.properties
├── mobilegateway.properties
└── rages.properties
```

{% endstep %}

{% step %}

### Update application signing keystore.

To run the NFC Wallet use case on the Android platform, the application needs to be signed with a specific [keystore](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/2.-onboarding/nfc-payment/onboard-nfc-payment).

```
android/app/
└── keystore
```

{% code title="android/app/build.gradle" %}

```
android {

    signingConfigs {
        debug {
            storeFile file('keystore')
            storePassword ''
            keyAlias ''
            keyPassword ''
        }
    }
}
```

{% endcode %}
{% endstep %}

{% step %}

### Update the Android package ID.

To be able to configure the D1 SDK on the Android platform for the Push Provisioning use case, the application ID needs to be registered for [Google Pay](https://thales-dis-dbp.stoplight.io/docs/d1-developer-portal/tpa40s3iff528-d1-push-onboarding-for-wallets#google-pay). Use the application ID `com.thalesgroup.gemalto.d1.validation` for the [sandbox environment](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/environments).
{% endstep %}

{% step %}

### Add Firebase Cloud Messaging.

As NFC Wallet on the Android platform uses Firebase Cloud Messaging (FCM), Google Messaging Services has to be enabled in the Android application and the `google-services.json` file needs to be added.

{% code title="android/build.gradle" %}

```
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.15'
    }
}
```

{% endcode %}

{% code title="android/app/build.gradle" %}

```
apply plugin: "com.google.gms.google-services"
```

{% endcode %}

Add the `google-services.json` to the Android project: `D1ReactExample/android/app/google-services.json`.
{% endstep %}

{% step %}

### Add the Host APDU Service Resource File.

For more information, see [Host APDU service resource file](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/2.-onboarding/nfc-payment/set-up-nfc-payment-on-application#host-apdu-service-resource-file).

{% code title="android/app/src/main/res/xml/apduservice.xml" %}

```
<?xml version="1.0" encoding="utf-8"?>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:apduServiceBanner="@mipmap/ic_launcher"
    android:description="@string/label_payment">
    <aid-group
        android:category="payment"
        android:description="@string/label_d1_pay_description">

        <!-- region MasterCard -->
        <!--Standard MasterCard-->
        <aid-filter android:name="A0000000041010" />

        <!--Mastercard Maestro (Debit)-->
        <aid-filter android:name="A0000000043060" />
        <aid-filter android:name="A0000000042203" />
        <aid-filter android:name="A0000000041010D056" />
        <!-- endregion -->

        <!-- region VISA -->
        <!--VISA Electron-->
        <aid-filter android:name="A0000000032010" />
        <!--VISA Debit/Credit (Classic)-->
        <aid-filter android:name="A0000000031010" />
        <!--Visa payWave for Mobile-->
        <aid-filter android:name="325041592E5359532E4444463031" />
        <!-- endregion -->
    </aid-group>
</host-apdu-service>
```

{% endcode %}

{% code title="android/app/src/main/res/values/strings.xml" %}

```
<resources>
    <string name="label_payment">payment</string>
    <string name="label_d1_pay_description">payment description</string>
</resources>
```

{% endcode %}
{% endstep %}

{% step %}

### Update the [Android manifest file](https://docs.payments.thalescloud.io/nfc-payment/integrate-d1-sdk/getting-started/multiplatform-technology/spaces/62lLFDcmLCeqqwmy4Fee/pages/szgSW0iOBkDj62qiSupT#androidmanifest.xml).

{% code title="android/app/src/main/AndroidManifest.xml" overflow="wrap" expandable="true" %}

```
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.thalesgroup.gemalto.d1.validation">

    <application
        android:allowBackup="false">

        <service
            android:name="com.thalesgroup.gemalto.d1.d1pay.D1HCEService"
            android:exported="true"
            android:permission="android.permission.BIND_NFC_SERVICE">
            <intent-filter>
                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

            <meta-data
                android:name="android.nfc.cardemulation.host_apdu_service"
                android:resource="@xml/apduservice" />
        </service>

    </application>

    <uses-feature android:name="android.hardware.nfc.hce" />
    <uses-feature android:name="android.hardware.nfc" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.NFC" />
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />

</manifest>
```

{% endcode %}
{% endstep %}

{% step %}

### Implement the Native Android entry point of the plugin by extending the React Java class `ReactContextBaseJavaModule`.

The following code snippet shows how to implement the methods for the [Login ](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/5.-authentication/sdk-login)and [Configure ](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/3.-initialization/android-initialization)use cases.

{% code title="android/app/src/main/java/com/d1sample/D1Plugin.java" overflow="wrap" expandable="true" %}

```
package com.d1sample;

import android.app.Activity;
import android.content.Context;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.google.gson.GsonBuilder;
import com.thalesgroup.gemalto.d1.ConfigParams;
import com.thalesgroup.gemalto.d1.D1Exception;
import com.thalesgroup.gemalto.d1.D1Params;
import com.thalesgroup.gemalto.d1.D1Task;
import com.thalesgroup.gemalto.d1.card.OEMPayType;
import com.thalesgroup.gemalto.d1.d1pay.ContactlessTransactionListener;
import com.thalesgroup.gemalto.d1.d1pay.D1PayConfigParams;
import com.thalesgroup.gemalto.d1.d1pay.VerificationMethod;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import androidx.annotation.NonNull;

/**
 * D1 React Native entry point.
 */
public class D1Plugin extends ReactContextBaseJavaModule {
    private D1Task mD1Task;
    private final ReactApplicationContext mReactApplicationContext;

    D1Plugin(final ReactApplicationContext context) {
        super(context);

        mReactApplicationContext = context;
    }

    @NonNull
    @Override
    public String getName() {
        return "D1Plugin";
    }

    /**
     * Configures the D1 SDK.
     *
     * @param serviceUrl     D1 Service URL.
     * @param issuerId       Issuer ID.
     * @param exponent       Public key exponent.
     * @param modulus        Public key modulus.
     * @param digitalCardUrl D1 Digital URL.
     * @param consumerId     Consumer ID.
     * @param callback       Callback.
     */
    @ReactMethod
    public void configure(@NonNull final String serviceUrl,
                          @NonNull final String issuerId,
                          @NonNull final ReadableArray exponent,
                          @NonNull final ReadableArray modulus,
                          @NonNull final String digitalCardUrl,
                          @NonNull final String consumerId,
                          @NotNull final String visaClientId,
                          @NotNull final String samsungServiceId,
                          @NonNull final Callback callback) {
        final Double[] exponentArray = exponent.toArrayList().toArray(new Double[0]);
        final Double[] modulusArray = modulus.toArrayList().toArray(new Double[0]);

        final byte[] exponentBytes = new byte[exponentArray.length];
        for (int i = 0; i < exponentArray.length; i++) {
            exponentBytes[i] = exponentArray[i].byteValue();
        }

        final byte[] modulusBytes = new byte[modulusArray.length];
        for (int i = 0; i < modulusArray.length; i++) {
            modulusBytes[i] = modulusArray[i].byteValue();
        }

        configure(mReactApplicationContext,
                  Objects.requireNonNull(getCurrentActivity()),
                  serviceUrl,
                  issuerId,
                  exponentBytes,
                  modulusBytes,
                  digitalCardUrl,
                  consumerId,
                  visaClientId,
                  samsungServiceId,
                  callback);
    }

    /**
     * Logs in.
     *
     * @param issuerToken Issuer token.
     * @param callback    Callback.
     */
    @ReactMethod
    public void login(@NonNull final String issuerToken, @NonNull final Callback callback) {
        login(issuerToken.getBytes(StandardCharsets.UTF_8), callback);
    }

    /**
     * Configures the D1 SDK.
     *
     * @param context        Application context.
     * @param activity       Enclosing Activity.
     * @param serviceUrl     Service URL.
     * @param issuerId       Issuer ID.
     * @param exponent       Public key exponent.
     * @param modulus        Public key modulus.
     * @param digitalCardUrl Digital Card URL.
     * @param consumerId     Customer ID>
     * @param callback       Callback.
     */
    private void configure(@NonNull final Context context,
                           @NonNull final Activity activity,
                           @NonNull final String serviceUrl,
                           @NonNull final String issuerId,
                           @NonNull final byte[] exponent,
                           @NonNull final byte[] modulus,
                           @NonNull final String digitalCardUrl,
                           @NonNull final String consumerId,
                           @NotNull final String visaClientId,
                           @NotNull final String samsungServiceId,
                           @NonNull final Callback callback) {


        mD1Task = new D1Task.Builder().setContext(context).setD1ServiceURL(serviceUrl).setIssuerID(issuerId)
                                      .setD1ServiceRSAExponent(exponent).setD1ServiceRSAModulus(modulus)
                                      .setDigitalCardURL(digitalCardUrl).build();

        final D1Params coreConfig = ConfigParams.buildConfigCore(consumerId);
        final D1Params cardConfig = ConfigParams.buildConfigCard(activity, OEMPayType.GOOGLE_PAY, visaClientId, samsungServiceId);

        // D1Pay config.
        final D1PayConfigParams d1PayConfigParams = D1PayConfigParams.getInstance();
        d1PayConfigParams.setContactlessTransactionListener(new D1PayTransactionListener());
        d1PayConfigParams.setReplenishAuthenticationUIStrings("Replenishment Title",
                                                              "Replenishment Subtitle",
                                                              "Replenishment Description",
                                                              "Cancel");

        mD1Task.configure(new D1Task.ConfigCallback<Void>() {
            @Override
            public void onSuccess(final Void data) {
                callback.invoke((Object) null);
            }

            @Override
            public void onError(@NonNull final List<D1Exception> exceptions) {
                callback.invoke(createJsonError(exceptions));
            }
        }, coreConfig, cardConfig, d1PayConfigParams);
    }

    /**
     * Logs in.
     *
     * @param issuerToken Issuer token.
     * @param callback    Callback.
     */
    private void login(@NonNull final byte[] issuerToken, @NonNull final Callback callback) {
        getD1Task().login(issuerToken, new D1Task.Callback<>() {
            @Override
            public void onSuccess(final Void data) {
                callback.invoke((Object) null);
            }

            @Override
            public void onError(@NonNull final D1Exception exception) {
                callback.invoke(createJsonError(Collections.singletonList(exception)));
            }
        });
    }

    /**
     * Retrieves the {@code D1Task} instance.
     *
     * @return {@code D1Task} instance.
     */
    @NonNull
    private D1Task getD1Task() {
        if (mD1Task == null) {
            throw new IllegalStateException("Need to configure D1 SDK first.");
        }

        return mD1Task;
    }

    /**
     * Creates a JSON representation of the {@code D1Exception}
     *
     * @param exceptions Exception.
     * @return JSON representation.
     */
    private String createJsonError(final List<D1Exception> exceptions) {
        final List<Map<String, Object>> json = new ArrayList<>();
        for (final D1Exception exception : exceptions) {
            final Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("message", exception.getLocalizedMessage());
            jsonMap.put("code", exception.getErrorCode().getCode());
            json.add(jsonMap);
        }

        if (json.size() > 1) {
            return new GsonBuilder().setPrettyPrinting().create().toJson(json);
        } else if (json.size() == 1) {
            return new GsonBuilder().setPrettyPrinting().create().toJson(json.get(0));
        }

        return new GsonBuilder().setPrettyPrinting().create().toJson(json);
    }

    /**
     * {@code ContactlessTransactionListener} implementation.
     */
    private static class D1PayTransactionListener extends ContactlessTransactionListener {
        @Override
        public void onTransactionStarted() {
            // TODO
        }

        @Override
        public void onAuthenticationRequired(@NonNull final VerificationMethod verificationMethod) {
            // TODO
        }

        @Override
        public void onReadyToTap() {
            // TODO
        }

        @Override
        public void onTransactionCompleted() {
            // TODO
        }

        @Override
        public void onError(@NonNull final D1Exception e) {
            // TODO
        }
    }
}
```

{% endcode %}
{% endstep %}

{% step %}

### Register the native module.

{% code title="android/app/src/main/java/com/d1sample/D1Package.java" overflow="wrap" expandable="true" %}

```
package com.d1sample;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import androidx.annotation.NonNull;

/**
 * D1 Package to register the D1 React wrapper.
 */
public class D1Package implements ReactPackage {

    @NonNull
    @Override
    public List<NativeModule> createNativeModules(@NonNull final ReactApplicationContext reactContext) {
        final List<NativeModule> modules = new ArrayList<>();
        modules.add(new D1Plugin(reactContext));

        return modules;
    }

    @NonNull
    @Override
    public List<ViewManager> createViewManagers(@NonNull final ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}
```

{% endcode %}

{% code title="android/app/src/main/java/com/d1sample/MainApplication.java" overflow="wrap" expandable="true" %}

```
public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages();
            // Packages that cannot be autolinked yet can be added manually here, for example:
            // packages.add(new MyReactNativePackage());
            packages.add(new D1Package());
            return packages;
        }
    };
}
```

{% endcode %}
{% endstep %}
{% endstepper %}

#### iOS platform

{% stepper %}
{% step %}

### Add the D1 SDK binaries to the generated iOS project.

a. Copy the SDK folder to the generated iOS project.

{% code overflow="wrap" %}

```
ios/D1/
├── Debug
│   ├── d1-libs-debug.podspec
│   ├── package.json
│   ├── D1.xcframework
│   └── SecureLogAPI.xcframework
└── Release
    ├── d1-libs-release.podspec
    ├── package.json
    ├── D1.xcframework
    └── SecureLogAPI.xcframework
```

{% endcode %}

The most simple way to differentiate between the [Debug and Release version of D1 SDK](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/1.-binary-integration/ios) on the iOS platform, is to wrap the D1 SDK binaries in to [Pods](https://cocoapods.org/).

**Debug Podspec**

{% code title="src/ios/D1/Debug/d1-libs-debug.podspec" overflow="wrap" %}

```
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))

Pod::Spec.new do |s|
  s.name         = "d1-libs-debug"
  s.version      = package["version"]
  s.summary      = package["description"]
  s.homepage     = package["homepage"]
  s.license      = package["license"]
  s.authors      = package["author"]
  s.platforms    = { :ios => "10.0" }
  s.source       = { :git => "https://www.thalesgroup.com.git", :tag => "#{s.version}" }

  s.ios.vendored_framework = 'D1.xcframework', 'SecureLogAPI.xcframework'
end
```

{% endcode %}

{% code title="ios/D1/Debug/package.json" overflow="wrap" %}

```
{
  "name": "d1-libs-debug",
  "version": "3.0.0",
  "homepage": "https://www.thalesgroup.com",
  "description": "D1 framework plugin",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "dynamic-linking"
  ],
  "author": "vladislav.vesely@external.thalesgroup.com",
  "license": "MIT"
}
```

{% endcode %}

**Release Podspec**

{% code title="ios/D1/Release/d1-libs-release.podspec" overflow="wrap" %}

```
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))

Pod::Spec.new do |s|
  s.name         = "d1-libs-release"
  s.version      = package["version"]
  s.summary      = package["description"]
  s.homepage     = package["homepage"]
  s.license      = package["license"]
  s.authors      = package["author"]
  s.platforms    = { :ios => "10.0" }
  s.source       = { :git => "https://www.thalesgroup.com.git", :tag => "#{s.version}" }

  s.ios.vendored_framework = 'D1.xcframework', 'SecureLogAPI.xcframework'
end
```

{% endcode %}

{% code title="ios/D1/Release/package.json" %}

```
{
  "name": "d1-libs-release",
  "version": "3.0.0",
  "homepage": "https://www.thalesgroup.com",
  "description": "D1 framework plugin",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "dynamic-linking"
  ],
  "author": "vladislav.vesely@external.thalesgroup.com",
  "license": "MIT"
}
```

{% endcode %}

b. Update the application `Podfile`.

{% code title="ios/Podfile" %}

```
target 'D1Sample' do
  config = use_native_modules!

  # Flags change depending on the env values.
  flags = get_default_flags()

  pod 'd1-libs-debug', :path => 'D1/Debug/', :configurations => ['Debug']
  pod 'd1-libs-release', :path => 'D1/Release/', :configurations => ['Release']
```

{% endcode %}
{% endstep %}

{% step %}

### Set ENABLE\_BITCODE.

Set `ENABLE_BITCODE=NO` in the **Build Options** of each target.
{% endstep %}

{% step %}

### Implement the native iOS entry point of the plugin.

The following code snippets show how to create the method prototypes for the [Login ](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/5.-authentication/sdk-login)and [Configure ](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/3.-initialization/android-initialization)use cases.

{% code title="ios/D1Plugin.swift" overflow="wrap" expandable="true" %}

```swift
import Foundation
import UIKit
import PassKit
import D1
import React

@objc(D1Plugin)
/// D1 React Native plugin entry point.
class D1Plugin: NSObject {
  
  private var d1Task: D1Task?
  
  //MARK: public API
  
  @objc
  /// Configures the D1 SDK.
  /// - Parameters:
  ///   - d1ServiceURLString: Service URL.
  ///   - issuerID: Issuer ID.
  ///   - publicKeyExponent: Public Key Exponent.
  ///   - publicKeyModulus: Public Key Modulus.
  ///   - digitalCardURLString: Digital Cardl URL.
  ///   - consumerID: Consumer ID.
  ///   - callback: Callback.
  func configure(_ d1ServiceURLString:String, _ issuerID:String, _ publicKeyExponent: [UInt8], _ publicKeyModulus: [UInt8], _ digitalCardURLString:String, _ consumerID: String, _ callback: @escaping RCTResponseSenderBlock) -> Void {
    self.configureInternal(d1ServiceURLString, issuerID, publicKeyExponent, publicKeyModulus, digitalCardURLString, consumerID, callback)
  }
  
  @objc
  /// Logs is to D1 SDK.
  /// - Parameters:
  ///   - issueToken: Issuer token.
  ///   - callback: Callback.
  func login(_ issueToken: String, _ callback: @escaping RCTResponseSenderBlock) -> Void {
    self.loginInternal(issueToken, callback)
  }
  
  // MARK: private API
  
  /// Configures the D1 SDK.
  /// - Parameters:
  ///   - d1ServiceURLString: Service URL.
  ///   - issuerID: Issuer ID.
  ///   - publicKeyExponent: Public Key Exponent.
  ///   - publicKeyModulus: Public Key Modulus.
  ///   - digitalCardURLString: Digital Cardl URL.
  ///   - consumerID: Consumer ID.
  ///   - callback: Callback.
  private func configureInternal(_ d1ServiceURLString:String, _ issuerID:String, _ publicKeyExponent: [UInt8], _ publicKeyModulus: [UInt8], _ digitalCardURLString:String, _ consumerID: String, _ callback: @escaping RCTResponseSenderBlock) -> Void {
    
    var comp = D1Task.Components()
    comp.d1ServiceURLString = d1ServiceURLString
    comp.issuerID = issuerID
    comp.d1ServiceRSAExponent = Data(bytes: publicKeyExponent, count: publicKeyExponent.count)
    comp.d1ServiceRSAModulus = Data(bytes: publicKeyModulus, count: publicKeyModulus.count)
    comp.digitalCardURLString = digitalCardURLString
    d1Task = comp.task()
    
    let coreConfig = ConfigParams.coreConfig(consumerID: consumerID)
    // required for Card Processing & OEM Pay
    let cardConfig = ConfigParams.cardConfig()
    
    getD1Task().configure([coreConfig, cardConfig]) { errors in
      if let jsonError = self.createErrorJson(errors) {
        callback([jsonError])
        return
      }
      
      callback([NSNull()])
    }
  }
  
  /// Logs is to D1 SDK.
  /// - Parameters:
  ///   - issueToken: Issuer token.
  ///   - callback: Callback.
  func loginInternal(_ issueToken: String, _ callback: @escaping RCTResponseSenderBlock) -> Void {
    if var issueTokenData: Data = issueToken.data(using: .utf8) {
      getD1Task().login(&issueTokenData) { error in
        if let jsonError = self.createErrorJson(error) {
          callback([jsonError])
          return
        }
        
        callback([NSNull()])
      }
    }
  }
  
  /// Retrieves the D1Task. Need to configure the D1 SDK first.
  /// - Returns: D1Task object. fatalError is thrown if called without D1 SDK configuration.
  private func getD1Task() -> D1Task {
    if let d1Task = d1Task {
      return d1Task
    } else {
      fatalError("Need to configure D1 SDK first.")
    }
  }
  
  /// Creates a JSON response string from D1Error array.
  /// - Parameter error: D1Error object.
  /// - Returns: JSON string or nil if D1Error array is nil.
  private func createErrorJson(_ error: [D1Error]?) -> String? {
    if let error = error {
      do {
        var dic = [RD1Error]()
        for i in 0..<error.count {
          dic.append(RD1Error(code: error[i].code.rawValue, message: error[i].localizedDescription))
        }
        let jsonEncoder = JSONEncoder()
        let jsonData = try jsonEncoder.encode(dic)
        let json = String(data: jsonData, encoding: String.Encoding.utf8)
        
        return json
      } catch {
        // should not happen
        fatalError()
      }
    }
    
    return nil
  }
  
  /// Creates a JSON response string from D1Error object.
  /// - Parameter error: D1Error object.
  /// - Returns: JSON string or nil if D1Error object is nil.
  private func createErrorJson(_ error: D1Error?) -> String? {
    if let error = error {
      do {
        let jsonEncoder = JSONEncoder()
        let jsonData = try jsonEncoder.encode(RD1Error(code: error.code.rawValue, message: error.localizedDescription))
        let jsonRD1Error = String(data: jsonData, encoding: String.Encoding.utf8)
        
        return jsonRD1Error
      } catch {
        // this should not happen
        fatalError()
      }
    }
    
    return nil
  }
  
  //MARK: Custom struct/class
  
  struct RD1Error: Encodable {
    
    /// Wrapper default error
    var code: Int = 10000
    var message: String?
    
    enum RD1ErrorKeys: String, CodingKey {
      case code = "code", message = "message"
    }
    
    public func encode(to encoder: Encoder) throws {
      var container = encoder.container(keyedBy: RD1ErrorKeys.self)
      try container.encode(self.code, forKey: .code)
      try container.encode(self.message, forKey: .message)
    }
  }
}
```

{% endcode %}

```objectivec
#import "React/RCTBridgeModule.h"
```

{% endstep %}

{% step %}

### Export the method prototypes to React Native.

{% code title="ios/D1Sample-Bridging-Header.h" overflow="wrap" %}

```objectivec
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(D1Plugin, NSObject)

RCT_EXTERN_METHOD(configure:(NSString* _Nonnull)d1ServiceURLString:(NSString* _Nonnull)issuerID:(NSArray* _Nonnull)publicKeyExponent:(NSArray* _Nonnull)publicKeyModulus:(NSString* _Nonnull)digitalCardURLString:(NSString* _Nonnull)consumerId:(RCTResponseSenderBlock)callback)

RCT_EXTERN_METHOD(login:(NSString* _Nonnull)issueToken:(RCTResponseSenderBlock)callback)

@end
```

{% endcode %}
{% endstep %}
{% endstepper %}

### Calling the D1 Plugin API from the React Native Project

After the native parts for the Android and iOS platform have been added to the React Native project, the D1 API can be accessed from the React Native project.

{% code title="React Native (JavaScript)" overflow="wrap" %}

```javascript
const d1ServiceURLString = 'https://example.io';
const issuerID = 'issueId';
const publicKeyExponent = [0x01, ...];
const publicKeyModulus = [0x00, 0xb9, 0x76, ...];
const digitalCardURLString = 'https://example.io'
const consumerID = 'consumerID'
const token = "access token"

NativeModules.D1Plugin.configure(d1ServiceURLString, issuerID, publicKeyExponent, publicKeyModulus,
    digitalCardUrl, consumerID, null, null, (error: String) => {
    if (error) {
        // error
    } else {
        // success
    }
});

          
NativeModules.D1Plugin.login(token, (error: String) => {
    if (error) {
        // error
    } else {
        // success
    }
});
```

{% endcode %}

### References

* [D1 portal](https://thales-dis-dbp.stoplight.io/docs/d1-developer-portal/branches/main/ZG9jOjE1MjEwNTMy-digital-first-d1-ux)
* [D1 Android Sample application](https://github.com/ThalesGroup/dp-d1-sample-android)
* [D1 iOS Sample application](https://github.com/ThalesGroup/dp-d1-sample-ios)
* [React Native - Native Android Modules](https://reactnative.dev/docs/native-modules-android)
* [React Native - Native iOS Modules](https://reactnative.dev/docs/native-modules-ios)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.payments.thalescloud.io/nfc-payment/integrate-d1-sdk/getting-started/multiplatform-technology/integrate-d1-sdk-with-react-native.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
