> 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/ja/d1-sdkwosuru/meru/maruchipurattofmu/ionic-capacitorwoshited1-sdkwosuru.md).

# Ionic Capacitorを使用してD1 SDKを統合する

### Overview

[Capacitor](https://capacitorjs.com/) is an open source native runtime for building Web Native apps. With Capacitor, you can create cross-platform iOS, Android, and Progressive Web Apps using JavaScript, HTML, and CSS.

[Capacitor](https://capacitorjs.com/) is an alternative to [Cordova](https://cordova.apache.org/) as the runtime environment when developing issuer applications using [Ionic](https://ionicframework.com/).

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

* [Capacitor plugin](#capacitor-plugin)
* [Creating Cordova plugin for D1 SDK](#creating-cordova-plugin-for-d1-sdk)
  * [TypeScript interface](#typescript-interface)
  * [Android platform](#android-platform)
    * [Implementing the Java native plugin entry point](#implementing-the-java-native-plugin-entry-point)
    * [Adding D1 SDK dependencies to the Capacitor plugin](#adding-d1-sdk-dependencies-to-the-capacitor-plugin)
    * [NFC Wallet configuration files](#nfc-wallet-configuration-files)
  * [iOS platform](#ios-platform)
    * [Implement the Swift native plugin entry point](#implement-the-swift-native-plugin-entry-point)
    * [Adding D1 SDK dependencies to the Capacitor plugin module](#adding-d1-sdk-dependencies-to-the-capacitor-plugin-module)
  * [Integrating the Capacitor plugin to Ionic Capacitor application](#integrating-the-capacitor-plugin-to-ionic-capacitor-application)
    * [Adding the D1 Capacitor plugin to your Capacitor project](#adding-the-d1-capacitor-plugin-to-your-capacitor-project)
    * [Updating the Capacitor Android application](#updating-the-capacitor-android-application)
    * [Updating the Capacitor iOS application](#updating-the-capacitor-ios-application)
* [References](#references)

### Capacitor Plugin

When integrating third party SDK's and native components, the Android and iOS native projects have to be placed in the respective modules called Plugins, which are then added to the Capacitor project.

### Creating Cordova Plugin for D1 SDK

#### Prerequisites

This guide was tested with:

* D1 SDK
  * 4.0.0 Android
  * 4.0.0 iOS
* Ionic
  * Ionic CLI 6.20.8
  * Angular CLI 16.1.4
* Capacitor
  * Capacitor CLI 5.2.1
  * Capacitor/android 5.2.1
  * Capacitor/core 5.2.1
  * Capacitor/ios 5.2.1
* Xcode 14.3.1

To create a template for the Capacitor plugin:

```
>> npm init @capacitor/plugin@latest
```

Plugin compilation is done using the following command:

```
>> npm run build
```

#### TypeScript interface

After the template is created, the TypeScript method prototypes will be called from the Capacitor project.

The following code snippets show how to create the method prototypes for the [Login ](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/8Ae02aBY0x2Bht2uwRgd)and [Configure ](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/ka4SX5q1fin8N0occPzr)use cases.

{% code title="capacitor-plugin/src/definitions.ts" overflow="wrap" %}

```
export interface D1Plugin {
  configure(options: { serviceUrl: string, issuerId: string, exponent: number[], modulus: number[], digitalCardUrl: string, consumerId: string, visaClientId: string | null, samsungServiceId: string | null }): Promise<void>;
  login(options: { token: string} ): Promise<void>;
}
```

{% endcode %}

{% code title="capacitor-plugin/src/web.ts" overflow="wrap" %}

```
import { WebPlugin } from '@capacitor/core';

import type { D1Plugin } from './definitions';

export class D1Web extends WebPlugin implements D1Plugin {

  async configure(_options: { serviceUrl: string, issuerId: string, exponent: number[], modulus: number[], digitalCardUrl: string, consumerId: string, visaClientId: string | null, samsungServiceId: string | null  }): Promise<void> {
    throw new Error('Method not implemented.');
  }

  async login(_options: { token: string} ): Promise<void> {
    throw new Error('Method not implemented.');
  }
}
```

{% endcode %}

{% code title="capacitor-plugin/src/index.ts" %}

```
import { registerPlugin } from '@capacitor/core';

import type { D1Plugin } from './definitions';

const D1 = registerPlugin<D1Plugin>('D1', {
  web: () => import('./web').then(m => new m.D1Web()),
});

export * from './definitions';
export { D1 };
```

{% endcode %}

### Android platform

#### Implementing the Java native plugin entry point

For the TypeScript method prototypes:

1. Create the appropriate entry point in the Android native code.
2. To implement the native Android entry point of the plugin, extend the Capacitor Java class `Plugin`.

{% code title="capacitor-plugin/android/src/main/java/com/thalesgroup/d1plugin/D1Plugin.java" overflow="wrap" expandable="true" %}

```
package com.thalesgroup.d1plugin;

import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
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 org.json.JSONException;

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 java.net.CookieManager;

import androidx.annotation.NonNull;

/**
 * D1Plugin Android native entry point.
 */
@CapacitorPlugin(name = "D1")
public class D1Plugin extends Plugin {
    private D1Task mD1Task;
    private ContactlessTransactionListener mContactlessTransactionListener;

    /**
     * Configures the D1 SDK.
     *
     * @param pluginCall       PlubinCall.
     */
    @PluginMethod
    public void configure(@NonNull final PluginCall pluginCall) {
        CookieManager.setDefault(new CookieManager());

        byte[] exponent;
        byte[] modulus;
        try {
            final List<Integer> exponentList = pluginCall.getArray("exponent").toList();
            final List<Integer> modulusList = pluginCall.getArray("modulus").toList();

            exponent = new byte[exponentList.size()];
            for (int i = 0; i < exponentList.size(); i++) {
                exponent[i] = exponentList.get(i).byteValue();
            }

            modulus = new byte[modulusList.size()];
            for (int i = 0; i < modulusList.size(); i++) {
                modulus[i] = modulusList.get(i).byteValue();
            }
        } catch (JSONException e) {
            // should not happen
            throw new IllegalStateException(e);
        }

        final String serviceUrl = pluginCall.getString("serviceUrl");
        final String issuerId = pluginCall.getString("issuerId");
        final String digitalCardUrl = pluginCall.getString("digitalCardUrl");
        final String consumerId = pluginCall.getString("consumerId");
        final String visaClientId = pluginCall.getString("visaClientId");
        final String samsungServiceId = pluginCall.getString("samsungServiceId");

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

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

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

        mD1Task.configure(new D1Task.ConfigCallback<>() {
            @Override
            public void onSuccess(final Void data) {
                pluginCall.resolve();
            }

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

    /**
     * Logs in to D1 services.
     *
     * @param pluginCall       Callback.
     */
    @PluginMethod
    public void login(@NonNull final PluginCall pluginCall) {
        final String token = pluginCall.getString("token");
        getD1Task().login(Objects.requireNonNull(token).getBytes(StandardCharsets.UTF_8), new D1Task.Callback<Void>() {
            @Override
            public void onSuccess(final Void unused) {
                pluginCall.resolve();
            }

            @Override
            public void onError(@NonNull final D1Exception exception) {
                pluginCall.reject(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 TransactionListener 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 %}

**Android default `CookieManager`**

{% hint style="warning" %}
Internally the D1 SDK manages cookies. Capacitor sets the default `CookieManager` to `CapacitorCookieManager`, which causes a `RuntimeException`, which is ignored, but causes the D1 SDK to not return a callback. As a workaround set a standard `CookieManager` as the default one before configuring the the SDK.

```
CookieManager.setDefault(new CookieManager());
```

{% endhint %}

#### Adding D1 SDK dependencies to the Capacitor plugin

Add the D1 SDK binaries [dependencies](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/uA2aK3YNgFQtsPAOPF1Y).

1. Create the Android modules using D1 SDK.

```
d1plugin-libs-debug/
├── build.gradle
└── d1-debug.aar

d1plugin-libs-release/
├── build.gradle
└── d1-release.aar
```

{% code title="d1plugin-libs-debug/build.gradle" overflow="wrap" %}

```
configurations.maybeCreate("default")
artifacts.add("default", file('d1-debug.aar'))
```

{% endcode %}

{% code title="d1plugin-libs-release/build.gradle" overflow="wrap" %}

```
configurations.maybeCreate("default")
artifacts.add("default", file('d1-release.aar'))
```

{% endcode %}

2. Add the dependencies to the Capacitor plugin.

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

```
dependencies {

    debugImplementation project(":d1plugin-libs-debug")
    releaseImplementation project(":d1plugin-libs-release")

    // other dependencies
    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"
}
```

{% endcode %}

#### NFC Wallet Configuration Files

1. Add the [NFC Wallet configuration files](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/szgSW0iOBkDj62qiSupT) to the Capacitor plugin.

```
capacitor-plugin/android/src/main/assets/
├── gemcbp.properties
├── mobilegateway.properties
└── rages.properties
```

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

```
<resources>
    <string name="fragment_payment_to_pay">To pay</string>
    <string name="fragment_payment_verify_by_keyguard">Authenticate</string>
</resources>
```

{% endcode %}

### iOS platform

#### Implement the Swift native plugin entry point

For the TypeScript method prototypes:

1. Create the appropriate entry point in the iOS native code.
2. To implement the Native iOS entry point of the plugin, extend the Capacitor Swift class `CAPPlugin`.

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

```
import Foundation
import Capacitor
import D1


@objc(D1Plugin)
/// D1Plugin iOS native entry point.
public class D1Plugin: CAPPlugin {
    private var d1Task: D1Task?
    
    /// Configures the D1 SDK.
    /// - Parameters:
    ///   - call: PluginCall.
    @objc func configure(_ call: CAPPluginCall) {
        // mandatory parameters
        let d1ServiceUrl = call.getString("serviceUrl")!
        let issueId = call.getString("issuerId")!
        let digitalCardURLString = call.getString("digitalCardUrl")!
        let consumerID = call.getString("consumerId")!
        let exponent = call.getArray("exponent")!
        let modulus = call.getArray("modulus")!
        
        var exponentData = Data()
        for byte in exponent {
            var char = UInt8(truncating: byte as! NSNumber)
            exponentData.append(&char, count: 1)
        }
        
        var modulusData = Data()
        for byte in modulus {
            var char = UInt8(truncating: byte as! NSNumber)
            modulusData.append(&char, count: 1)
        }
        
        var comp = D1Task.Components()
        comp.d1ServiceURLString = d1ServiceUrl
        comp.issuerID = issueId
        comp.digitalCardURLString = digitalCardURLString
        comp.d1ServiceRSAExponent = exponentData
        comp.d1ServiceRSAModulus = modulusData
        d1Task = comp.task()

        let coreConfig = ConfigParams.coreConfig(consumerID: consumerID)
        let cardConfig = ConfigParams.cardConfig()

        getD1Task().configure([coreConfig, cardConfig]) { errors in
            if let jsonError = self.createErrorJson(errors) {
                call.reject(jsonError)
                return
            }
            
            call.resolve()
        }
    }
    
    /// Logs in to D1.
    /// - Parameters:
    ///   - call: PluginCall.
    @objc func login(_ call: CAPPluginCall) {
        // mandatory parameter
        let token = call.getString("token")!
        if var tokenData = token.data(using: .utf8) {
            getD1Task().login(&tokenData) { (error: D1Error?) in
                if let error = error {
                    if let jsonError = self.createErrorJson([error]) {
                        call.reject(jsonError)
                    }
                }
                
                call.resolve()
            }
        }
    }
    
    /// 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
    }
    
    //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 %}

3. Export the method prototypes to the Capacitor plugin.

{% code title="capacitor-plugin/ios/src/D1Plugin/D1Plugin.m" %}

```
#import <Foundation/Foundation.h>
#import <Capacitor/Capacitor.h>

CAP_PLUGIN(D1Plugin, "D1",
           CAP_PLUGIN_METHOD(configure, CAPPluginReturnPromise);
           CAP_PLUGIN_METHOD(login, CAPPluginReturnPromise);
)
```

{% endcode %}

#### Adding D1 SDK dependencies to the Capacitor plugin module

1. The most simple way to differentiate between the [Debug and Release version of D1 SDK](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/vSGEjE3xWNMk85SlK515) on the iOS platform, is to wrap the D1 SDK binaries in to [Pods](https://cocoapods.org/).

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

**Debug Podspec**

{% code title="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="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="D1/Release/d1-libs-release.podspec" %}

```
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="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 %}

2. Update the `podspec` file of the Capacitor plugin.

{% code title="capacitor-plugin/D1plugin.podspec" %}

```
require 'json'

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

Pod::Spec.new do |s|
  s.name = 'D1plugin'
  s.version = package['version']
  s.summary = package['description']
  s.license = package['license']
  s.homepage = package['repository']['url']
  s.author = package['author']
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
  s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
  s.ios.deployment_target  = '13.0'
  s.dependency 'Capacitor'
  s.swift_version = '5.1'
  s.dependency "d1-libs-debug", :configurations => ['Debug']
  s.dependency "d1-libs-release", :configurations => ['Release']
end
```

{% endcode %}

### Integrating the Capacitor plugin to Ionic Capacitor application

#### Adding the D1 Capacitor plugin to your Capacitor project

1. After all the updates are done, the Capacitor plugin needs to be built.

{% code overflow="wrap" %}

```
>> cd capacitor-plugin
>> npm run build
```

{% endcode %}

2. Add the Capacitor plugin to the Capacitor project. Use the application ID `com.thalesgroup.gemalto.d1.validation` for the sandbox, when creating the Ionic Capacitor application.

```
>> ionic start --package-id=com.thalesgroup.gemalto.d1.validation
>> cd capacitor-application
>> npm install /path/to/capacitor-plugin
```

3. Add the Android and iOS platforms.

```
>> ionic capacitor add ios
>> ionic capacitor add android
```

4. Synchronize the Capacitor project with the respective platforms.

```
>> ionic capacitor sync
>> ionic capacitor sync android
>> ionic capacitor sync ios
```

5. Access the D1 Capacitor plugin API using TypeScript.

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

```
import { Component } from '@angular/core';
import { D1 } from 'd1plugin';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})

export class HomePage {

  constructor() {

  }

  async test() {
    const self = this;

    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"

    D1.configure({ serviceUrl: d1ServiceURLString, issuerId: issuerID, exponent: publicKeyExponent, modulus: publicKeyModulus, digitalCardUrl: digitalCardURLString, consumerId: consumerID, visaClientId: null, samsungServiceId: null }).then(() => {
      console.log("Configure OK");

      D1.login({ token: token}).then(() => {
        console.log("Login OK");
        
      }).catch((error: any) => {
        console.log(error);
      }); 

    }).catch((error: any) => {
      console.log(error);
    });    
  }
}
```

{% endcode %}

#### Updating the Capacitor Android application

The following updates are to be applied to the Android platform:

1. Reference the new Android modules which contain the D1 SDK binaries in the Ionic Capacitor Android application.

{% code title="capacitor-application/android/settings.gradle" overflow="wrap" %}

```
include ':d1plugin-libs-debug'
project(':d1plugin-libs-debug').projectDir = new File('/path/to/d1plugin-libs-debug/android')

include ':d1plugin-libs-release'
project(':d1plugin-libs-release').projectDir = new File('/path/to/d1plugin-libs-release/android')
```

{% endcode %}

2. Update the minimum SDK version supported to 24.

{% code title="capacitor-application/android/variables.gradle" overflow="wrap" %}

```
ext {
    minSdkVersion = 24
    .
    .
 }
```

{% endcode %}

3. Set `android:allowBackup` to `false`.

{% code title="capacitor-application/android/app/src/main/AndroidManifest.xml" %}

```
<application
    android:allowBackup="false"
    .
    .
</application>
```

{% endcode %}

4. As NFC Wallet on the Android platform uses Firebase Cloud Messaging (FCM), Google Messaging Services has to be enabled in the Android application to use FCM.
   * Add FCM to Ionic application:

     ```
     >> npm install @capacitor/push-notifications
     ```
   * Add the `google-services.json` file to Android application.

     ```
     android/app
     ├── build
     ├── build.gradle
     ├── capacitor.build.gradle
     ├── google-services.json
     ├── keystore
     ├── proguard-rules.pro
     └── src
     ```
5. 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](broken://spaces/62lLFDcmLCeqqwmy4Fee/pages/TGeGf00YiFjIao7Ck8Bj).

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

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

```actionscript-3
signingConfigs {
        debug {
            storeFile file('keystore/keystore')
            storePassword 'password'
            keyAlias 'd1demo'
            keyPassword 'password'
        }
        release {
            storeFile file('keystore/keystore')
            storePassword 'password'
            keyAlias 'd1demo'
            keyPassword 'password'
        }
    }

    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }

        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
```

{% endcode %}

#### Updating the Capacitor iOS application

1. Add the Pods for D1 SDK Debug and Release version.

{% code title="ios/App/Podfile" overflow="wrap" %}

```
target 'App' do
  capacitor_pods
  # Add your Pods here
  pod 'D1pluginLibsDebug', :path => '/path/to/d1plugin-libs-debug', :configurations => ['Debug']
  pod 'D1pluginLibsRelease', :path => '/path/to/d1plugin-libs-release', :configurations => ['Release']
end
```

{% endcode %}

### References

* D1 portal
* [D1 Android Sample application](https://github.com/ThalesGroup/dp-d1-sample-android)
* [D1 iOS Sample application](https://github.com/ThalesGroup/dp-d1-sample-ios)
* [Capacitor](https://capacitorjs.com/)
* [Ionic](https://ionicframework.com/docs/)


---

# 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, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/pin-management/ja/d1-sdkwosuru/meru/maruchipurattofmu/ionic-capacitorwoshited1-sdkwosuru.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
