> 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/pin-management/integrate-d1-sdk/getting-started/multiplatform-technology/integrate-d1-sdk-using-xamarin.md).

# Integrate D1 SDK using Xamarin

### Overview

Xamarin is a cross-platform, open source app platform for building Android and iOS apps using .NET and C#.

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

* [Integrate a native library into a Xamarin.Forms app](#integrate-a-native-library-into-a-xamarinforms-app)
* [Integrate D1 SDK into a Xamarin.Forms app](#integrate-d1-sdk-into-a-xamarinforms-app)
* [References](#references)

### Integrate a native library into a Xamarin.Forms app

To use a native Android and iOS library or component in a multiplatform Xamarin.Forms App, multiple layers need to be created to access the native APIs on multiple platforms.

#### General C# library

To access the native API for Android and iOS in a Xamarin.Forms App in a multiplatform manner, you have to create a General C# Library, containing all the required APIs to implement in the native Android and iOS platforms. This General C# Library will then be accessed directly from the Xamarin.Forms App.

#### Android platform

**Android Bindings Library**

On the Android platform, firstly create an Android Bindings Library. The Android Bindings Library is an assembly containing Managed Callable Wrappers for Java types. To develop an Android Xamarin application, you can use this Android Bindings Library without the need to create additional layers (that is, General C# Library and Android Class Library).

**Android Class Library**

To use the Android Bindings Library in a Xamarin.Forms App, you have to create an Android Class Library which will be the intermediate layer between the Android Bindings Library and the General C# Library.

#### iOS platform

**iOS Proxy Framework**

The iOS Binding Library is only able to access Objective-C classes or Swift classes which are either annotated with the `objc` attribute or are subclassed from the `NSObject` class. In the case a pure Swift framework is used, you have to wrap this framework to an Objective-C framework to be able to create an iOS Binding Library.

**iOS Bindings Library**

Subsequently, you can create an iOS Bindings Library and wrap the iOS framework (either original framework or the proxy framework in case a pure Swift framework is used). To develop an iOS Xamarin application, you can use this iOS Bindings Library directly without the need to create additional layers (General C# Library and iOS Class Library).

**iOS Class Library**

To use the iOS Bindings Library in a Xamarin.Forms App, you have to create an iOS Class Library which will be the intermediate layer between the iOS Bindings Library and the General C# Library.

### Integrate D1 SDK into a Xamarin.Forms app

The wrapping of the [configure](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/3.-initialization/android-initialization) and [login](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/5.-authentication/sdk-login) D1 API calls will be described in the following sections.

This guide was tested with:

* D1 SDK
  * 3.2.0 Android
  * 3.2.0 iOS
* Visual Studio Community 2022 for Mac Preview (Version 17.6 Preview (17.6 build 1569)
* Runtime .NET 7.0.3 (64-bit)
* Xamarin.Android Version: 13.2.0.6 (Visual Studio Community)
* Xamarin.iOS Version: 16.4.0.6 Visual Studio Community
* Xcode 14.3.1

#### General C# Library

This library will be directly accessed from the Xamarin.Forms App. It defines the general interfaces which are implemented by the Android Class Library and iOS Class Library.

1. In Visual Studio, select **File > New Project > Multiplatform > Library > General > Class Library** to create a new class library.
2. In **Target framework**, select `.NET Standard 2.1`.
3. Add the general interfaces.

{% code title="Enums.cs" overflow="wrap" expandable="true" %}

```
namespace D1Plugin.Models
{
    public static class Enums
    {
        public enum D1ErrorCode
        {
            unknown,
            not_logged_in,
            not_authorized,
            device_environment_unsafe,
            core,
            no_card_activation_method,
            risk,
            card,
            card_no_pending_idv,
            // Following section is iOS specific
            ui_component_not_found, 
            card_not_supported,     
            not_initialized,        
            invalid_argument,
            // Following section is Android specific
            card_not_found,
            pay,
            pay_unrecoverable,
            pay_no_push_token,
            pay_digitization_in_completed,
            pay_deletion_in_progress
        }
    }
}
```

{% endcode %}

{% code title="ID1Error.cs" overflow="wrap" %}

```
namespace D1Plugin.Models
{
    public interface ID1Error
    {
        Enums.D1ErrorCode Code { get; }
        string ErrorDescription { get; }
        string FailureReason { get; }
        string RecoverySuggestion { get; }
    }
}
```

{% endcode %}

{% code title="ID1Plugin.cs" overflow="wrap" expandable="true" %}

```
using D1Plugin.Models;

/// <summary>
/// High level Wrapper for D1 SDK.
/// This wrapper can be used in both Forms and Native projects.
/// </summary>
namespace D1Plugin
{
    /// <summary>
    /// Singleton holder for ID1Plugin instance.
    /// </summary>
    public static class D1Singleton
    {
        private static ID1Plugin sHelperInstance = null;
        private static readonly object sHelperLock = new object();

        /// <summary>
        /// Registers the native implementation of ID1Plugin.
        /// </summary>
        /// <param name="implementation">Native implementation of ID1Plugin.</param>
        public static void RegisterImplementation(ID1Plugin implementation)
        {
            lock (sHelperLock)
            {
                sHelperInstance = implementation;
            }
        }

        /// <summary>
        /// Retrieves the native implementation of ID1Plugin.
        /// </summary>
        /// <returns>Native implementation of ID1Plugin.</returns>
        public static ID1Plugin Instance
        {
            get
            {
                lock (sHelperLock)
                {
                    return sHelperInstance;
                }
            }
        }
    }

    /// <summary>
    /// Generic ID1Plugin interface, which is then implemented on the native platforms.
    /// </summary>
    public interface ID1Plugin
    {
        /// <summary>
        /// Configures the D1 SDK.
        /// </summary>
        /// <param name="consumerId">Consumer ID - for specific user/customer.</param>
        /// <param name="d1ServiceURL">D1 Service URL.</param>
        /// <param name="d1DigitalCardURL">The URL for digital card operation.</param>
        /// <param name="d1IssuerID">Unique identifier for each customer.</param>
        /// <param name="d1ServiceRSAExponent">The RSA exponent of the public key for secure communication between D1 Service Server and the SDK.</param>
        /// <param name="d1ServiceRSAModulus">The RSA modulus of the public key for secure communication between D1 Service Server and the SDK.</param>
        /// <param name="callback">Callback.</param>
        void Configure(string consumerId,
            string d1ServiceURL,
            string d1DigitalCardURL,
            string d1IssuerID,
            byte[] d1ServiceRSAExponent,
            byte[] d1ServiceRSAModulus,
            Action<List<ID1Error>> callback);

        /// <summary>
        /// Logs in to D1 Services.
        /// </summary>
        /// <param name="accessToken">Issuer token.</param>
        /// <param name="callback">Callback.</param>
        void Login(string accessToken, Action<ID1Error> callback);
    }
}
```

{% endcode %}

#### Android

The Android part consists of:

* Android Bindings Library
* Android class Library

**Android Bindings Library**

1. In Visual Studio, select **File > New Project > Android > Library > Bindings Library** to create a new Android Bindings Library project.
2. In the Project explorer, right click on the **Jars** folder, select **Add/Existing Files...** and select both `d1-debug.aar` and `d1-release.aar` files to add to the newly created Android Bindings Library project.
3. Configure the build settings for Debug and Release by opening the project `*.csproj` file in some arbitrary text editor and modify it to use `d1-debug.aar` for the Debug build and `d1-release.aar` for the Release build.

{% code title="\*.csproj" overflow="wrap" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <LibraryProjectZip Include="Jars\d1-release.aar" Condition=" '$(Configuration)' == 'Release' " />
    <LibraryProjectZip Include="Jars\d1-debug.aar" Condition=" '$(Configuration)' == 'Debug' " />
  </ItemGroup>
</Project>
```

{% endcode %}

4. Update the obfuscated source code and other rules.

{% hint style="info" %}

* As Xamarin is unable to bind classes and methods which are dependent (extend or take as parameter) on the obfuscated source code,the obfuscated classes have to be marked as obfuscated in the `Metadata.xml` file. In addition, there may be certain naming conflicts which have to be resolved.
* The following Android binding configuration was tested with D1 SDK 2.4.0.
  {% endhint %}

{% code title="Metadata.xml" overflow="wrap" expandable="true" %}

```
<metadata>

  <!-- Remove all obfuscated parts of D1 SDK except few we need -->
  <remove-node path="/api/package[starts-with(@name, 'util') and @name!='util.q.a.j' and @name!='util.y.q' and @name!='util.q.a.i' and @name!='util.h.xy.ay' and @name!='util.q.a.i' and @name!='util.h.xy.ba' and @name!='util.q.a.f' and @name!='util.q.a.f' and @name!='util.q.a.s' and @name!='util.h.xy.az' and @name!='util.q.a.w' and @name!='util.q.a.y' and @name!='util.h.xy.bc' and @name!='util.q.a.w.c']" />

  <!-- Remove rest of the classes from packages we kept in previous step -->
  <remove-node path="/api/package[@name='util.q.a.j']/class[@name='c']" />
  <remove-node path="/api/package[@name='util.q.a.j']/class[@name='d']" />
  <remove-node path="/api/package[@name='util.q.a.j']/class[@name='e']" />
  <remove-node path="/api/package[@name='util.y.q']/class[@name='a']" />

  <attr path="/api/package[@name='util.q.a.j']/class[@name='b']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.y.q']/class[@name='d']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.i']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.i']/class[@name='d']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.h.xy.ay']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.i']/class[@name='g']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.f']/class[@name='b']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.f']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.h.xy.ba']/class[@name='rb']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.f']/class[@name='i']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='d']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='e']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='j']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='q']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='a']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.s']/class[@name='h']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.h.xy.az']/class[@name='d']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='e']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='g']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='b']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='f']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='a']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.y']/class[@name='d']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.y']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.y']/class[@name='f']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='c']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.q.a.w']/class[@name='t']" name="obfuscated">false</attr>
  <attr path="/api/package[@name='util.h.xy.bc']/class[@name='rc']" name="obfuscated">false</attr>

  <remove-node path="/api/package[@name='com.thalesgroup.gemalto.d1.d1pay']/class[@name='ContactlessTransactionListener']/method[@name='onAuthenticationRequired']" />
  <remove-node path="/api/package[@name='com.thalesgroup.gemalto.d1.d1pay']/class[@name='ContactlessTransactionListener']/method[@name='onError']" />
  <remove-node path="/api/package[@name='com.thalesgroup.gemalto.d1.d1pay']/class[@name='ContactlessTransactionListener']/method[@name='onReadyToTap']" />
  <remove-node path="/api/package[@name='com.thalesgroup.gemalto.d1.d1pay']/class[@name='ContactlessTransactionListener']/method[@name='onTransactionCompleted']" />
  <remove-node path="/api/package[@name='com.thalesgroup.gemalto.d1.d1pay']/class[@name='ContactlessTransactionListener']/method[@name='onTransactionStarted']" />

  <!-- Prevent duplicite definition of similiar listener method names -->
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.dcm']/interface[@name='WalletPinEventListener']/method[@name='onError']" name="managedName">WalletPinEventOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='CardListFetchListener']/method[@name='onSuccess']" name="managedName">CardListFetchOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='CardListFetchListener']/method[@name='onError']" name="managedName">CardListFetchOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='CardMetaDataFetchListener']/method[@name='onSuccess']" name="managedName">CardMetaDataFetchOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='CardMetaDataFetchListener']/method[@name='onError']" name="managedName">CardMetaDataFetchOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='MGCardLifecycleEventListener']/method[@name='onSuccess']" name="managedName">MGCardLifecycleEventOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='MGCardLifecycleEventListener']/method[@name='onError']" name="managedName">MGCardLifecycleEventOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='ProductListener']/method[@name='onSuccess']" name="managedName">ProductOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='ProductListener']/method[@name='onError']" name="managedName">ProductOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='TermsAndConditionsListener']/method[@name='onSuccess']" name="managedName">TermsAndConditionsOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='TermsAndConditionsListener']/method[@name='onError']" name="managedName">TermsAndConditionsOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='TransactionHistoryListener']/method[@name='onSuccess']" name="managedName">TransactionHistoryOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='TransactionHistoryListener']/method[@name='onError']" name="managedName">TransactionHistoryOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='MGConfigurationResetListener']/method[@name='onSuccess']" name="managedName">MGConfigurationResetOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='MGConfigurationResetListener']/method[@name='onError']" name="managedName">MGConfigurationResetOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.mobilegateway.listener']/interface[@name='MGDigitizationListener']/method[@name='onError']" name="managedName">MGDigitizationOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='AuthenticationStateListener']/method[@name='onSuccess']" name="managedName">AuthenticationStateOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='AuthenticationStateListener']/method[@name='onError']" name="managedName">AuthenticationStateOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='DataPreparationListener']/method[@name='onSuccess']" name="managedName">DataPreparationOnSuccess</attr>
  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='DataPreparationListener']/method[@name='onError']" name="managedName">DataPreparationOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='PushServiceListener']/method[@name='onError']" name="managedName">PushServiceOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='WalletSecureEnrollmentListener']/method[@name='onError']" name="managedName">WalletSecureEnrollmentOnError</attr>

  <attr path="/api/package[@name='com.gemalto.mfs.mwsdk.provisioning.listener']/interface[@name='EnrollingServiceListener']/method[@name='onError']" name="managedName">EnrollingServiceOnError</attr>


  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='PaymentManager.TransactionInfoListener']/method[@name='onSuccess']" name="managedName">TransactionInfoListenerOnSuccess</attr> 
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='PaymentManager.TransactionInfoListener']/method[@name='onFailure']" name="managedName">TransactionInfoListenerOnFailure</attr> 

  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='PaymentManager.CustomSheetTransactionInfoListener']/method[@name='onSuccess']" name="managedName">CustomSheetTransactionInfoOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='PaymentManager.CustomSheetTransactionInfoListener']/method[@name='onFailure']" name="managedName">CustomSheetTransactionInfoOnFailure</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='PaymentManager.CustomSheetTransactionInfoListener']/method[@name='onCardInfoUpdated']" name="managedName">CustomSheetTransactionInfoOnCardInfoUpdated</attr>


  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.card']/interface[@name='CardListener']/method[@name='onSuccess']" name="managedName">CardOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.card']/interface[@name='CardListener']/method[@name='onFail']" name="managedName">CardOnFail</attr>

  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.card']/interface[@name='GetCardListener']/method[@name='onSuccess']" name="managedName">GetCardOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.card']/interface[@name='GetCardListener']/method[@name='onFail']" name="managedName">GetCardOnFail</attr>

  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='MstPaymentListener']/method[@name='onSuccess']" name="managedName">MstPaymentOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='MstPaymentListener']/method[@name='onFail']" name="managedName">MstPaymentOnFail</attr>

  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='TransactionResultListener']/method[@name='onSuccess']" name="managedName">TransactionResultOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.payment']/interface[@name='TransactionResultListener']/method[@name='onFail']" name="managedName">TransactionResultOnFail</attr>

  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.service']/interface[@name='UserSignUpNotifyListener']/method[@name='onSuccess']" name="managedName">UserSignUpNotifyOnSuccess</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2.service']/interface[@name='UserSignUpNotifyListener']/method[@name='onFail']" name="managedName">UserSignUpNotifyListenerOnFail</attr>


  <!-- Xamarin binding limitation.. TODO: Should be hide this and use from final project? TapAndPay-->
  <attr path="/api/package[@name='com.google.android.gms.tapandpay']/interface[@name='TapAndPay']/field[@name='TapAndPay']" name="managedName">TapAndPayInterface</attr>
  <attr path="/api/package[@name='com.samsung.android.sdk.samsungpay.v2']/class[@name='BindRetry']" name="visibility">public</attr>


</metadata>
```

{% endcode %}

**Android Class Library**

The Android Class Library wraps the Android Bindings Library and implements the general interfaces defined in the General C# Class Library.

Step 1: Create a new Android Class Library.

In Visual Studio, select **File > New Project > Android > Library > Class Library**.

Step 2: Add the dependencies.

The Android Class Library is dependent on the Android Bindings Library and the General C# Library.

1. In the Project Explorer, right-click on the high level solution, select **Add/Existing Project...** and select \***.csproj**. Perform the same for both Android Bindings Library and General C# Class Library.
2. Right-click on the **References** folder in the Android Class Library project, and select **Add Project Reference....**.
3. Click the `Projects` tab and select both Android Bindings Library and the General C# Class Library.

Step 3: Add Xamarin.Essentials.

In the Project Explorer, right-click on the `Android Class Library` project, select `Manage NuGet packages...` and search to add `Xamarin.Essentials`.

Step 4: Implement the interfaces.

{% code title="D1Plugin.cs" overflow="wrap" expandable="true" %}

```
using System;
using System.Collections.Generic;
using Com.Thalesgroup.Gemalto.D1;
using D1Plugin.Models;
using D1Plugin;
using Android.App;
using Com.Thalesgroup.Gemalto.D1.D1pay;
using Com.Thalesgroup.Gemalto.D1.Card;
using D1PluginAndroid.Helpers;

/// <summary>
/// Native Android Wrapper for D1 SDK.
/// </summary> 
namespace D1PluginAndroid
{

    /// <summary>
    /// Native Android implementation of <code>ID1Helper</code>.
    /// </summary>
    public class D1Plugin : ID1Plugin
    { 
        private D1Task mD1Task = null;
        private Activity mActivity = null;
        public static ContactlessTransactionListener sContactlessTransactionListener;
        //private static Notification sNotification;
        
        public D1Plugin(Activity activity)
        {
            mActivity = activity;
        }

        public D1Plugin()
        {
            
        }

        public void Configure(
            string consumerId,
            string d1ServiceURL,
            string d1DigitalCardURL,
            string d1IssuerID,
            byte[] d1ServiceRSAExponent,
            byte[] d1ServiceRSAModulus,
            Action<List<ID1Error>> callback)
        {
            mD1Task = new D1Task.Builder().SetContext(mActivity)
                    .SetD1ServiceURL(d1ServiceURL)
                    .SetIssuerID(d1IssuerID)
                    .SetD1ServiceRSAExponent(d1ServiceRSAExponent)
                    .SetD1ServiceRSAModulus(d1ServiceRSAModulus)
                    .SetDigitalCardURL(d1DigitalCardURL).Build();


            var coreConfig = ConfigParams.BuildConfigCore(consumerId);
            var cardConfig = ConfigParams.BuildConfigCard(mActivity, OEMPayType.GooglePay, null);

            var d1PayConfing = D1PayConfigParams.Instance;

            sContactlessTransactionListener = new ContactlessTransactionListenerImpl();
            d1PayConfing.ContactlessTransactionListener = sContactlessTransactionListener;

            d1PayConfing.SetReplenishAuthenticationUIStrings("Replenishment Title",
                                                                  "Replenishment Subtitle",
                                                                  "Replenishment Description",
                                                                  "Cancel");

            mD1Task.Configure(new Callbacks.ConfigDelegate(callback), coreConfig, cardConfig, d1PayConfing);
        }

        public void Login(string accessToken, Action<ID1Error> callback)
        {
            mD1Task.Login(System.Text.Encoding.UTF8.GetBytes(accessToken), new Callbacks.ErrorDelegate(callback, true));
        }

        private class ContactlessTransactionListenerImpl : ContactlessTransactionListener
        {

            protected override void OnAuthenticationRequired(VerificationMethod p0)
            {
                // TODO
            }

            protected override void OnError(D1Exception exception)
            {
                // TODO
            }

            protected override void OnReadyToTap()
            {
                // TODO
            }

            protected override void OnTransactionCompleted()
            {
                // TODO
            }

            protected override void OnTransactionStarted()
            {
                // TODO
            }
        }
    }
}
```

{% endcode %}

{% code title="D1ErrorImpl.cs" overflow="wrap" expandable="true" %}

```
using Com.Thalesgroup.Gemalto.D1;
using D1Plugin.Models;
using D1PluginAndroid.Helpers;

namespace D1PluginAndroid.Models
{
    public class D1ErrorImpl : ID1Error
    {
        private D1Exception mException = null;

        private D1ErrorImpl(D1Exception exception)
        {
            mException = exception;
        }

        public static ID1Error optionalObject(D1Exception exception)
        {
            if (exception != null)
            {
                return new D1ErrorImpl(exception);
            }
            else
            {
                return null;
            }
        }


        public Enums.D1ErrorCode Code { get { return EnumConverter.D1ErrorCodeFromNative(mException.GetErrorCode()); } }

        public string ErrorDescription { get { return mException.LocalizedMessage; } }

        public string FailureReason { get { return mException.GetErrorCode().Message; } }

        public string RecoverySuggestion { get { return mException.GetErrorCode().RecoverySuggestion; } }
    }
}
```

{% endcode %}

{% code title="Callbacks.cs" overflow="wrap" expandable="true" %}

```
using System;
using System.Collections.Generic;
using Com.Thalesgroup.Gemalto.D1;
using D1Plugin.Models;
using Xamarin.Essentials;
using static Com.Thalesgroup.Gemalto.D1.D1Task;
using D1PluginAndroid.Models;

namespace D1PluginAndroid.Helpers
{
    public static class Callbacks
    {
        public static void SyncIfNeeded(bool callInMainThread, Action action)
        {
            if (callInMainThread && !MainThread.IsMainThread)
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    action.Invoke();
                });
            } else
            {
                action.Invoke();
            }
        }

        public class ErrorDelegate : Java.Lang.Object, ICallback
        {
            private Action<ID1Error> mOriginalCallback;
            private bool mMainThread;

            public ErrorDelegate(Action<ID1Error> callback, bool mainThread)
            {
                mOriginalCallback = callback;
                mMainThread = mainThread;
            }

            public void OnError(D1Exception exception)
            {
                SyncIfNeeded(mMainThread, () => mOriginalCallback.Invoke(D1ErrorImpl.optionalObject(exception)));
            }

            public void OnSuccess(Java.Lang.Object voidObject)
            {
                SyncIfNeeded(mMainThread, () => mOriginalCallback.Invoke(null));
            }
        }

        public class ConfigDelegate : Java.Lang.Object, IConfigCallback
        {
            private Action<List<ID1Error>> mOriginalCallback;

            public ConfigDelegate(Action<List<ID1Error>> callback)
            {
                mOriginalCallback = callback;
            }

            public void OnError(IList<D1Exception> errors)
            {
                var retValue = new List<ID1Error>();
                foreach (var loopError in errors)
                {
                    retValue.Add(D1ErrorImpl.optionalObject(loopError));
                }

                SyncIfNeeded(true, () => mOriginalCallback.Invoke(retValue));
            }

            public void OnSuccess(Java.Lang.Object voidObject)
            {
                SyncIfNeeded(true, () => mOriginalCallback.Invoke(null));
            }
        }

    }

}
```

{% endcode %}

{% code title="EnumConverter.cs" overflow="wrap" expandable="true" %}

```
using D1Plugin.Models;
using D1ErrorCode = Com.Thalesgroup.Gemalto.D1.D1Exception.ErrorCode;


namespace D1PluginAndroid.Helpers
{
    public static class EnumConverter
    { 
        public static Enums.D1ErrorCode D1ErrorCodeFromNative(D1ErrorCode errorCode)
        {
            if (errorCode == D1ErrorCode.ErrorNotLoggedIn)
            {
                return Enums.D1ErrorCode.not_logged_in;
            }
            else if (errorCode == D1ErrorCode.ErrorNotAuthorized)
            {
                return Enums.D1ErrorCode.not_authorized;
            }
            else if (errorCode == D1ErrorCode.ErrorDeviceEnvironmentUnsafe)
            {
                return Enums.D1ErrorCode.device_environment_unsafe;
            }
            else if (errorCode == D1ErrorCode.ErrorCore)
            {
                return Enums.D1ErrorCode.core;
            }
            else if (errorCode == D1ErrorCode.ErrorNoCardActivationMethod)
            {
                return Enums.D1ErrorCode.no_card_activation_method;
            }
            else if (errorCode == D1ErrorCode.ErrorRisk)
            {
                return Enums.D1ErrorCode.risk;
            }
            else if (errorCode == D1ErrorCode.ErrorCard)
            {
                return Enums.D1ErrorCode.card;
            }
            else if (errorCode == D1ErrorCode.ErrorCardNoPendingIdv)
            {
                return Enums.D1ErrorCode.card_no_pending_idv;
            }
            else if (errorCode == D1ErrorCode.ErrorCardNotFound)
            {
                return Enums.D1ErrorCode.card_not_found;
            }
            else if (errorCode == D1ErrorCode.ErrorD1pay)
            {
                return Enums.D1ErrorCode.pay;
            }
            else if (errorCode == D1ErrorCode.ErrorD1payUnrecoverable)
            {
                return Enums.D1ErrorCode.pay_unrecoverable;
            }
            else if (errorCode == D1ErrorCode.ErrorD1payNoPushToken)
            {
                return Enums.D1ErrorCode.pay_no_push_token;
            }
            else if (errorCode == D1ErrorCode.ErrorD1payDigitizationCompleted)
            {
                return Enums.D1ErrorCode.pay_digitization_in_completed;
            }
            else if (errorCode == D1ErrorCode.ErrorD1payDeletionInProgress)
            {
                return Enums.D1ErrorCode.pay_deletion_in_progress;
            }
            else
            {
                return Enums.D1ErrorCode.unknown;
            }
        }

    }

}
```

{% endcode %}

#### iOS

The iOS part consists of:

* iOS Proxy Framework
* iOS Bindings Library
* iOS Class Library

**iOS Proxy Framework**

The first step is to build a native Swift framework with Objective-C header enabled. The framework has the header embedded in the package in the following directory: .framework/Headers/-Swift.h. This header exposes the public interfaces, which will be used to create Xamarin.iOS binding metadata and generate C# classes exposing the Swift framework members.

Step 1: Create a new XCode Framework project.

Click **File > New > Project > Multiplatform > Framework & Library > Framework** to create a new XCode Framework project.

Step 2: Moving the D1 frameworks.

Move these D1 frameworks and place them in **General > Frameworks and Libraries**.

```
* D1.xcframework
```

Step 3: Add the D1 wrapper code.

{% code title="D1Task.swift" overflow="wrap" expandable="true" %}

```
import Foundation
import D1
import UIKit

@objc
public class D1Task : NSObject {
    internal var d1Task: D1.D1Task
    
    internal init(_ d1Task: D1.D1Task) {
        self.d1Task = d1Task
    }
    
    @objc
    public static func getSDKVersions() -> [String : String] {
        return D1.D1Task.getSDKVersions()
    }
    
    @objc
    public func configure(_ configParams: Set<ConfigParams>,
                          completion: @escaping ([D1Error]?) -> Void) {
        var inputSet: Set<D1.ConfigParams> = []
        for loopParam in configParams {
            inputSet.insert(loopParam.configParams)
        }
        d1Task.configure(inputSet) { d1Errors in
            if let d1Errors = d1Errors {
                var retList: [D1Error] = []
                for loopObject in d1Errors {
                    retList.append(D1Error.init(loopObject))
                }
                completion(retList)
            } else {
                completion(nil)
            }
        }
    }
    
    @objc
    public func login(_ accessToken: String,
                      completion: @escaping (D1Error?) -> Void) {
        if var accessTokenData: Data = accessToken.data(using: .utf8) {
            d1Task.login(&accessTokenData) { d1Error in
                completion(D1Error.optionalObject(d1Error))
            }
        }
    }
}
```

{% endcode %}

```
import Foundation
import D1

@objc
public class ConfigParams: NSObject {
    internal let configParams: D1.ConfigParams

    internal init(_ configParams : D1.ConfigParams) {
        self.configParams = configParams
    }
    
    @objc
    public static func coreConfig(consumerID: String) -> ConfigParams {
        return ConfigParams(D1.ConfigParams.coreConfig(consumerID: consumerID))
    }
    
    @objc
    public static func cardConfig() -> ConfigParams {
        return ConfigParams(D1.ConfigParams.cardConfig())
    }
}
```

{% code overflow="wrap" expandable="true" %}

```
import Foundation
import D1

@objc
public class D1Error : NSObject {
    internal var d1Error: D1.D1Error

    internal init(_ d1Error: D1.D1Error) {
        self.d1Error = d1Error
    }
    
    internal static func optionalObject(_ object: D1.D1Error?) -> D1Error? {
        if let retValue = object {
            return D1Error(retValue)
        } else {
            return nil
        }
    }

    @objc
    public var code: D1ErrorCode {
        return D1ErrorCode(from: d1Error.code)
    }
    
    @objc
    public var failureReason: String? {
        return d1Error.failureReason
    }

    @objc
    public var errorDescription: String? {
        return d1Error.errorDescription
    }

    @objc
    public var recoverySuggestion: String? {
        return d1Error.recoverySuggestion
    }
}
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
import Foundation
import D1

@objc
public enum D1ErrorCode : Int {
    case unknown
    case cancelled
    case notLoggedIn
    case notAuthorized
    case deviceEnvironmentUnsafe
    case core
    case noCardActivationMethod
    case uiComponentNotFound
    case risk
    case card
    case cardNotSupported
    case cardNoPendingIDV
    case notInitialized
    case invalidArgument

    // Unknown is not part of original error code, but it will not be wise to crash druing error handling.
    internal init(from: D1.D1Error.Code) {
        switch from {
        case .cancelled: self = .cancelled
        case .notLoggedIn: self = .notLoggedIn
        case .notAuthorized: self = .notAuthorized
        case .deviceEnvironmentUnsafe: self = .deviceEnvironmentUnsafe
        case .core: self = .core
        case .noCardActivationMethod: self = .noCardActivationMethod
        case .uiComponentNotFound: self = .uiComponentNotFound
        case .risk: self = .risk
        case .card: self = .card
        case .cardNotSupported: self = .cardNotSupported
        case .cardNoPendingIDV: self = .cardNoPendingIDV
        case .notInitialized: self = .notInitialized
        case .invalidArgument: self = .invalidArgument
            
        @unknown default:
            self = .unknown
        }
    }
}
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
import Foundation
import D1

@objc
public class Components : NSObject {
    internal var components: D1.D1Task.Components

    internal init(_ components: D1.D1Task.Components) {
        self.components = components
    }

    @objc
    public var d1ServiceURLString: String? {
        get {
            return self.components.d1ServiceURLString
        }
        set(newValue) {
            self.components.d1ServiceURLString = newValue
        }
    }

    @objc
    public var issuerID: String? {
        get {
            return self.components.issuerID
        }
        set(newValue) {
            self.components.issuerID = newValue
        }
    }
    
    @objc
    public var d1ServiceRSAExponent: Data? {
        get {
            return self.components.d1ServiceRSAExponent
        }
        set(newValue) {
            self.components.d1ServiceRSAExponent = newValue
        }
    }

    @objc
    public var d1ServiceRSAModulus: Data? {
        get {
            return self.components.d1ServiceRSAModulus
        }
        set(newValue) {
            self.components.d1ServiceRSAModulus = newValue
        }
    }

    @objc
    public var digitalCardURLString: String? {
        get {
            return self.components.digitalCardURLString
        }
        set(newValue) {
            self.components.digitalCardURLString = newValue
        }
    }

    @objc
    public func task() -> D1Task {
        return D1Task(self.components.task())
    }

    @objc
    public override init() {
        self.components = D1.D1Task.Components()
    }
}
```

{% endcode %}

Step 4: Build the framework.

1. Select the appropriate platform (Device or Simulator)
2. `Product/Build`
3. `Product/Show Build folder in finder` to locate the created framework.

**iOS Bindings Library**

By now, you should have the framework with the Objective-C generated interface header ready to be consumed by a Xamarin.iOS binding. The next step is to prepare the API definition interfaces which are used by a binding project to generate C# classes. These definitions can either be created manually or automatically by the Objective [Sharpie](https://learn.microsoft.com/en-us/xamarin/cross-platform/macios/binding/objective-sharpie/) tool and the generated header file.

Use Sharpie to generate the metadata.

Step 1: Create the API definition using `Sharpie` tool.

{% code overflow="wrap" %}

```
>> sharpie bind --sdk=iphoneos16.2 --output="XamarinApiDef" --namespace="D1Proxy" --scope="/Path/to/framework/D1Proxy.framework/Headers" /Path/to/framework/D1Proxy.framework/Headers/D1Proxy-Swift.h
```

{% endcode %}

This will give the output:

{% code title="ApiDefinitions.cs" overflow="wrap" expandable="true" %}

```
using System;
using D1Proxy;
using Foundation;
using ObjCRuntime;

namespace D1Proxy
{
	// @interface Components : NSObject
	[BaseType (typeof(NSObject), Name = "_TtC7D1Proxy10Components")]
	interface Components
	{
		// @property (copy, nonatomic) NSString * _Nullable d1ServiceURLString;
		[NullAllowed, Export ("d1ServiceURLString")]
		string D1ServiceURLString { get; set; }

		// @property (copy, nonatomic) NSString * _Nullable issuerID;
		[NullAllowed, Export ("issuerID")]
		string IssuerID { get; set; }

		// @property (copy, nonatomic) NSData * _Nullable d1ServiceRSAExponent;
		[NullAllowed, Export ("d1ServiceRSAExponent", ArgumentSemantic.Copy)]
		NSData D1ServiceRSAExponent { get; set; }

		// @property (copy, nonatomic) NSData * _Nullable d1ServiceRSAModulus;
		[NullAllowed, Export ("d1ServiceRSAModulus", ArgumentSemantic.Copy)]
		NSData D1ServiceRSAModulus { get; set; }

		// @property (copy, nonatomic) NSString * _Nullable digitalCardURLString;
		[NullAllowed, Export ("digitalCardURLString")]
		string DigitalCardURLString { get; set; }

		// -(D1Task * _Nonnull)task __attribute__((warn_unused_result("")));
		[Export ("task")]
		[Verify (MethodToProperty)]
		D1Task Task { get; }
	}

	// @interface ConfigParams : NSObject
	[BaseType (typeof(NSObject), Name = "_TtC7D1Proxy12ConfigParams")]
	[DisableDefaultCtor]
	interface ConfigParams
	{
		// +(ConfigParams * _Nonnull)coreConfigWithConsumerID:(NSString * _Nonnull)consumerID __attribute__((warn_unused_result("")));
		[Static]
		[Export ("coreConfigWithConsumerID:")]
		ConfigParams CoreConfigWithConsumerID (string consumerID);

		// +(ConfigParams * _Nonnull)cardConfig __attribute__((warn_unused_result("")));
		[Static]
		[Export ("cardConfig")]
		[Verify (MethodToProperty)]
		ConfigParams CardConfig { get; }
	}

	// @interface D1Error : NSObject
	[BaseType (typeof(NSObject), Name = "_TtC7D1Proxy7D1Error")]
	[DisableDefaultCtor]
	interface D1Error
	{
		// @property (readonly, nonatomic) enum D1ErrorCode code;
		[Export ("code")]
		D1ErrorCode Code { get; }

		// @property (readonly, copy, nonatomic) NSString * _Nullable failureReason;
		[NullAllowed, Export ("failureReason")]
		string FailureReason { get; }

		// @property (readonly, copy, nonatomic) NSString * _Nullable errorDescription;
		[NullAllowed, Export ("errorDescription")]
		string ErrorDescription { get; }

		// @property (readonly, copy, nonatomic) NSString * _Nullable recoverySuggestion;
		[NullAllowed, Export ("recoverySuggestion")]
		string RecoverySuggestion { get; }
	}

	// @interface D1Task : NSObject
	[BaseType (typeof(NSObject), Name = "_TtC7D1Proxy6D1Task")]
	[DisableDefaultCtor]
	interface D1Task
	{
		// +(NSDictionary<NSString *,NSString *> * _Nonnull)getSDKVersions __attribute__((warn_unused_result("")));
		[Static]
		[Export ("getSDKVersions")]
		[Verify (MethodToProperty)]
		NSDictionary<NSString, NSString> SDKVersions { get; }

		// -(void)configure:(NSSet<ConfigParams *> * _Nonnull)configParams completion:(void (^ _Nonnull)(NSArray<D1Error *> * _Nullable))completion;
		[Export ("configure:completion:")]
		void Configure (NSSet<ConfigParams> configParams, Action<NSArray<D1Error>> completion);

		// -(void)login:(NSString * _Nonnull)accessToken completion:(void (^ _Nonnull)(D1Error * _Nullable))completion;
		[Export ("login:completion:")]
		void Login (string accessToken, Action<D1Error> completion);
	}
}
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
using ObjCRuntime;

namespace D1Proxy
{
	[Native]
	public enum D1ErrorCode : long
	{
		Unknown = 0,
		Cancelled = 1,
		NotLoggedIn = 2,
		NotAuthorized = 3,
		DeviceEnvironmentUnsafe = 4,
		Core = 5,
		NoCardActivationMethod = 6,
		UiComponentNotFound = 7,
		Risk = 8,
		Card = 9,
		CardNotSupported = 10,
		CardNoPendingIDV = 11,
		NotInitialized = 12,
		InvalidArgument = 13
	}
}
```

{% endcode %}

The generated API definitions need to be reviewed and in most cases to be updated. For this reason the `Verify` keyword is added to the generated files as an indication for this.

{% code overflow="wrap" expandable="true" %}

```
[Verify] attributes intentionally cause C# compilation errors so that you are forced to verify the binding. You should remove the [Verify] attribute when you have reviewed (and possibly corrected) the code.
```

{% endcode %}

On the generated files, perform the following updates:

{% code title="ApiDefinitions.cs" overflow="wrap" expandable="true" %}

```csharp
// from
interface D1Error 
{
}

// to
interface D1Error : INativeObject
{
}

// from
interface ConfigParams
{
    [Export("cardConfig")]
    ConfigParams CardConfig { get; }
}

// to
interface ConfigParams : INativeObject
{ 
    [Export("cardConfig")]
    ConfigParams CardConfig();
}

// from
interface Components
{
    [Export("task")]
    D1Task Task { get; }
}

// from
interface Components
{
    [Export("task")]
    D1Task Task();
}
```

{% endcode %}

Step 2: Create a new iOS Bindings Library.

Select **File > New Project > iOS > Library > Bindings Library** to create a new iOS Bindings Library.

Step 3: Replace ApiDefinitions.cs and StructsAndEnums.cs files.

Replace the `ApiDefinitions.cs` and `StructsAndEnums.cs` files with the newly created ones from the step 1.

Step 4: Add Native Reference.

Right-click on **Native References**, select **Add Native Reference** and select the newly created proxy framework.

**iOS Class Library**

The iOS Class Library wraps the iOS Bindings Library and implements the general interfaces defined in the General C# Class Library.

Step 1: Create a new iOS Class Library.

In Visual Studio, select **File > New Project... > iOS > Library > Class Library** to create a new iOS Class Library.

Step 2: Add the dependencies.

The iOS Class Library is dependent on the iOS Bindings Library and the General C# Library.

1. In the Project Explorer, right-click on the high level solution,select **Add/Existing Project...** and select the \***.csproj**. Perform the same for both iOS Bindings Library and General C# Class Library.
2. Right-click on **References** folder in the Android Class Library project, and select **Add Project Reference...**.
3. Click the **Projects** tab and select both iOS Bindings Library and the General C# Class Library.

Step 3: Add Xamarin.Essentials.

In the Project Explorer, right-click on the `iOS Class Library` project and select `Manage NuGet packages...` and search to add `Xamarin.Essentials`.

Step 4: Implement the interfaces.

{% code title="D1Plugin.cs" overflow="wrap" expandable="true" %}

```csharp
/// <summary>
/// Native iOS Wrapper for D1 SDK.
/// </summary>
namespace D1Plugin.iOS
{
    /// <summary>
    /// Native iOS implementation of <code>ID1Helper</code>.
    /// </summary>
    public class D1Plugin : ID1Plugin
    {

        private D1Task mD1Task = null;
        public UIViewController mRootViewController { get; set; }

        public D1Plugin()
        {
            // Empty constructor for dependency binding.
        }

        public void Configure(string consumerId,
            string d1ServiceURL,
            string d1DigitalCardURL,
            string d1IssuerID,
            byte[] d1ServiceRSAExponent,
            byte[] d1ServiceRSAModulus,
            Action<List<ID1Error>> callback)
        {
            var components = new Components();
            components.D1ServiceURLString = d1ServiceURL;
            components.DigitalCardURLString = d1DigitalCardURL;
            components.IssuerID = d1IssuerID;
            components.D1ServiceRSAExponent = NSData.FromArray(d1ServiceRSAExponent);
            components.D1ServiceRSAModulus = NSData.FromArray(d1ServiceRSAModulus);

            var configParams = new NSSet<ConfigParams>(
                ConfigParams.CoreConfigWithConsumerID(consumerId),
                ConfigParams.CardConfig()
                );

            mD1Task = components.Task();
            mD1Task.Configure(configParams, (NSArray<D1Error> obj) => {
                if (obj != null)
                {
                    var retValue = new List<ID1Error>();
                    foreach (D1Error loopError in (IEnumerable<D1Error>)obj)
                    {
                        retValue.Add(D1ErrorImpl.optionalObject(loopError));
                    }

                    callback.Invoke(retValue);
                }
                else
                {
                    callback.Invoke(null);
                }
            });
        }

        public void Login(string accessToken, Action<ID1Error> callback)
        {
            mD1Task.Login(accessToken, (D1Error error) =>
            {
                callback.Invoke(D1ErrorImpl.optionalObject(error));
            });
        }
    }
}
```

{% endcode %}

{% code title="D1ErrorImpl.cs" overflow="wrap" expandable="true" %}

```csharp
using D1Plugin.Models;
using D1Wrapper.iOS.Helpers;

namespace D1Plugin.iOS.Models
{
    public class D1ErrorImpl : ID1Error
    {
        private D1Proxy.D1Error mError = null;

        private D1ErrorImpl(D1Proxy.D1Error error)
        {
            mError = error;
        }

        public static ID1Error optionalObject(D1Proxy.D1Error error)
        {
            if (error != null)
            {
                return new D1ErrorImpl(error);
            }
            else
            {
                return null;
            }
        }

        public Enums.D1ErrorCode Code { get { return EnumConverter.D1ErrorCodeFromNative(mError.Code); } }

        public string ErrorDescription { get { return mError.ErrorDescription; } }

        public string FailureReason { get { return mError.FailureReason; } }

        public string RecoverySuggestion { get { return mError.RecoverySuggestion; } }
    }

}
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```csharp
using D1Plugin.Models;
using D1Proxy;

namespace D1Wrapper.iOS.Helpers
{
    public static class EnumConverter
    {
        public static Enums.D1ErrorCode D1ErrorCodeFromNative(D1ErrorCode errorCode)
        {
            switch (errorCode)
            {
                case D1ErrorCode.Unknown: return Enums.D1ErrorCode.unknown;
                case D1ErrorCode.NotLoggedIn: return Enums.D1ErrorCode.not_logged_in;
                case D1ErrorCode.NotAuthorized: return Enums.D1ErrorCode.not_authorized;
                case D1ErrorCode.DeviceEnvironmentUnsafe: return Enums.D1ErrorCode.device_environment_unsafe;
                case D1ErrorCode.Core: return Enums.D1ErrorCode.core;
                case D1ErrorCode.NoCardActivationMethod: return Enums.D1ErrorCode.no_card_activation_method;
                case D1ErrorCode.UiComponentNotFound: return Enums.D1ErrorCode.ui_component_not_found;
                case D1ErrorCode.Risk: return Enums.D1ErrorCode.risk;
                case D1ErrorCode.Card: return Enums.D1ErrorCode.card;
                case D1ErrorCode.CardNotSupported: return Enums.D1ErrorCode.card_not_supported;
                case D1ErrorCode.CardNoPendingIDV: return Enums.D1ErrorCode.card_no_pending_idv;
                case D1ErrorCode.NotInitialized: return Enums.D1ErrorCode.not_initialized;
                case D1ErrorCode.InvalidArgument: return Enums.D1ErrorCode.invalid_argument;
            }

            return Enums.D1ErrorCode.unknown;
        }

    }

}
```

{% endcode %}

#### C# Forms Application

Step 1: Create a new Xamarin.Forms app.

Click **File > New > Project > Multiplatform > App > Blank App** to create a new Xamarin Forms App.

{% hint style="info" %}
To be able to access the [Sandbox Environment](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/environments/mobile-sdk-sandbox) on the Android platform, the application needs to have the package name `com.thalesgroup.gemalto.d1.validation` which is registered for [Google Pay](https://thales-dis-dbp.stoplight.io/docs/d1-developer-portal/tpa40s3iff528-d1-push-onboarding-for-wallets#google-pay).
{% endhint %}

Step 2: Add the project dependencies.

1. In the Project Explorer, right-click on the high level solution, select **Add/Existing Project...** and add all the newly created projects (General C# Library, Android Class Library and iOS Class Library, Android Bindings Library, iOS Bindings library) to the solution.
2. In the Project Explorer, right click on the **Dependencies** folder of the Xamarin.Forms app project and select **Add Project Reference...**.
3. Select the required General C# Library that you wish to add.

{% hint style="info" %}

* To be able to add the project dependencies, the projects need to use the same .NET Standard Library version (tested with .NET Standard Library 2.1).
* The Xamarin Forms App is dependent on the General C# Class Library.
* The Android part of the Xamarin.Forms app is dependent on:
  * The Android Class Library.
  * The General C# Library.
* The iOS part of the Xamarin Forms App is dependent on:
  * The iOS Class Library.
  * The General C# Library.
    {% endhint %}

{% code title=".csproj\*" overflow="wrap" %}

```
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
    <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
    <ProduceReferenceAssemblyInOutDir>true</ProduceReferenceAssemblyInOutDir>
  </PropertyGroup>
</Project>
```

{% endcode %}

#### Android setup

Step 1: Add the Project dependencies.

1. In the Project Explorer, right click on the **References** folder of the Xamarin.Forms app project and select **Add Project Reference...**.
2. Select the required General C# Library and the Android Class Library.

Step 2: [Add the other dependencies](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/1.-binary-integration/android).

The following dependencies need to be added to the Android Xamarin.Forms app:

* Square.OkHttp3.LoggingInterceptor
* Xamarin.GooglePlayServices.Base

These can be added as NuGet packages as described in the following steps:

1. In the Project Explorer, right click on the **Packages** folder under the Xamarin.Forms app project.
2. Select `Manage NuGet Packages...` and add the appropriate packages.

There is another dependency, JNA, which has to be added manually in the form of an AAR file.

1. Download the [AAR file](https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.5.0/jna-5.5.0.aar).
2. Copy the downloaded AAR file to the **Assets** folder under the Android Xamarin Forms App project.
3. Right-click on the AAR file and select **Properties**.
4. Set the **Build action** to **AndroidAarLibrary**.

Step 3: Add other assets and resources.

1. Add the [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).
2. Add and update the following files to the Android Xamarin.Forms project:

{% code title="xml/apduservice.xml" overflow="wrap" expandable="true" %}

```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="values/strings.xml" overflow="wrap" %}

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

{% endcode %}

3. Add the [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).

```
Assets/
├── gemcbp.properties
├── mobilegateway.properties
└── rages.properties
```

Step 4: Update the `AndroidManifest.xml` file.

{% code title="AndroidManifest.xml" overflow="wrap" expandable="true" %}

```
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.thalesgroup.gemalto.d1.validation">
	<uses-feature android:name="android.hardware.nfc.hce" />
	<uses-feature android:name="android.hardware.nfc" />
	<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="31" />
	<application android:theme="@style/MainTheme" android:allowBackup="false" android:label="@string/app_name">
		<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>
</manifest>
```

{% endcode %}

Step 5: Update the application signing keystore.

To run the [NFC Wallet](https://app.gitbook.com/o/fwy1mtbRONGA2YDKDBr0/s/62lLFDcmLCeqqwmy4Fee/integrate-d1-sdk/getting-started/configuration/3.-initialization/nfc-payment-service-initialization) 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#issuer-application-signing-key-app_pk).

Step 6: Add Firebase Cloud Messaging (FCM).

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.

Add the dependency, Xamarin.Firebase.Messaging, to the Android Xamarin.Forms app.

These can be added as NuGet packages as described in the following steps:

1. In the Project Explorer, right click on the **Packages** folder under the Xamarin.Forms app project.
2. Select **Manage NuGet Packages...** and add the appropriate packages.
3. Move the `google-services.json` to the Android Xamarin.Forms project. Right click on the `google-services.json` file, select **Properties** and set the **Build action** to `GoogleServicesJson`.

Step 7: Register native D1 implementations.

{% code title="MainActivity.cs" overflow="wrap" expandable="true" %}

```csharp
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

        // Register refference to actual implementation.
        var d1Plugin = new D1PluginAndroid.D1Plugin(this);
        D1Plugin.D1Singleton.RegisterImplementation(d1Plugin);

        LoadApplication(new App());
    }
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
```

{% endcode %}

#### iOS setup

1. In the Project Explorer, right click on the **References** folder of the Xamarin.Forms app project and select **Add Project Reference...** and add the General C# Library and the iOS Class Library.
2. Register the native D1 implementations.

{% code title="AppDelegate.cs" expandable="true" %}

```csharp
using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

namespace validation.iOS
{
    // The UIApplicationDelegate for the application. This class is responsible for launching the 
    // User Interface of the application, as well as listening (and optionally responding) to 
    // application events from iOS.
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            var impl = new D1Plugin.iOS.D1Plugin();
            D1Plugin.D1Singleton.RegisterImplementation(impl);

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}
```

{% endcode %}

#### Access the D1 API from the Xamarin.Forms app

After performing all the above steps, the D1 API can now be accessed from the Xamarin.Forms app.

{% code expandable="true" %}

```csharp
string D1ServiceURLString = "https://example.io";
string IssuerID = "issueId";
byte[] PublicKeyExponent = StringToByteArray("010001");
byte[] PublicKeyModulus = StringToByteArray("00B9765708...");
const string DigitalCardURLString = "https://example.io";
const string ConsumerID = "consumerID";

D1Plugin.D1Singleton.Instance.Configure(ConsumerID,
    D1ServiceURLString,
    DigitalCardURLString,
    IssuerID,
    PublicKeyExponent,
    PublicKeyModulus, (List<ID1Error> errors) =>
    {
        if (errors == null)
        {
            // Configure success
        }
        else
        {
            // Configure error
        }
    });

D1Plugin.D1Singleton.Instance.Login(token, async (ID1Error error) =>
{

    if (error == null)
    {
        // Login success
    }
    else
    {
        // Login error
    }
});
```

{% 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)
* [Xamarin - Binding Java library](https://learn.microsoft.com/en-us/xamarin/android/platform/binding-java-library/)
* [Xamarin - Binding Swift library](https://learn.microsoft.com/en-us/xamarin/ios/platform/binding-swift/)


---

# 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/pin-management/integrate-d1-sdk/getting-started/multiplatform-technology/integrate-d1-sdk-using-xamarin.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.
