> 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/apache-cordovawoshited1-sdkwosuru.md).

# Apache Cordovaを使用してD1 SDKを統合する

### Overview

Apache Cordova is an open-source mobile development framework. It allows you to use standard web technologies such as HTML5, CSS3, and JavaScript for cross-platform development. To access each device's capabilities such as sensors, data, network status, the applications are executed within their platform-specific wrappers using standards-compliant API bindings.

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

* Cordova plugin
* Creating Cordova plugin for D1 SDK
  * Basic plugin structure
  * JavaScript interface
  * Android platform
    * Java source code
    * Adding D1 SDK binaries to the plugin module
    * Updating the plugin manifest file for Android platform
  * iOS platform
    * Swift source code
    * Updating the plugin manifest file for iOS platform
    * D1 SDK Pod wrapper
* Integrating plugin module to Cordova project
  * Android application signing
  * Minimum Android SDK version supported for Android apps
  * Firebase Cloud Messaging
  * Calling the D1 plugin API from the Cordova project
* Integrating plugin module to Ionic Cordova project
* References

### Cordova plugin

When D1 SDK is integrated using Apache Cordova to your project, the Android and iOS native platforms are automatically generated. To integrate third party SDKs and native components, they have to be placed in to the respective modules called Plugins, which are then added to the Cordova project.

### Creating Cordova plugin for D1 SDK

This guide was tested with:

* D1 SDK
  * 4.0.0 Android
  * 4.0.0 iOS
* Cordova
  * CLI 11.1.0
  * Android 10.1.2
  * iOS 6.3.0
* Xcode 14.3.1

#### Basic plugin structure

The structure of the **Plugins** module is as follows:

{% code overflow="wrap" %}

```
D1Plugin
├── package.json
├── plugin.xml
├── src
│   ├── android
│   │   ├── assets
│   │   │   ├── gemcbp.properties
│   │   │   ├── mobilegateway.properties
│   │   │   └── rages.properties
│   │   ├── D1Plugin.java
│   │   ├── libs
│   │   │   ├── d1-debug.aar
│   │   │   └── d1-release.aar
│   │   ├─── d1-build.gradle
│   │   └── xml
│   │       └── apduservice.xml
│   └── ios
│       ├── D1Plugin.swift
│       ├── D1
│       │   ├── Debug
│       │   │   ├── d1-libs-debug.podspec
│       │   │   ├── package.json
│       │   │   ├── D1.xcframework
│       │   │   └── SecureLogAPI.xcframework
│       │   └── Release
│       │       ├── d1-libs-release.podspec
│       │       ├── package.json
│       │       ├── D1.xcframework
│       │       └── SecureLogAPI.xcframework
└── www
    └── D1Plugin.js
```

{% endcode %}

Where:

* package.json: Project meta data.
* plugin.xml: Top level Plugin manifest file.
* D1Plugin.java: Android native code entry point.
  * gemcbp.properties, mobilegateway.properties, rages.properties: NFC payment configuration files. &#x20;
* d1-debug.aar, d1-release.aar: Android binaries in debug and release mode.
* d1-build.gradle: Android native platform Gradle file which will be added to the generated Android platform.
* apduservice.xml: Host APDU service resource file
* D1Plugin.swift: iOS native code entry point.
* D1Plugin.js: JavaScript interface.
* D1.xcframework, SecureLogAPI.xcframework: iOS binaries in debug and release mode.
* d1-libs-debug.podspec: Podspec for the Pod wrapping D1 SDK Debug version.
* d1-libs-release.podspec: Podspec for the Pod wrapping D1 SDK Release version.

#### JavaScript interface

After the structure is set up, the JavaScript method prototypes will be called from the Cordova project.

The following code snippet shows how to create the method prototypes for the SDK login and Android initialization use cases:

{% code title="www/D1Plugin.js" overflow="wrap" expandable="true" %}

```javascript
var exec = require('cordova/exec');

var PLUGIN_NAME = 'D1Plugin';

/**
 * D1 SDK Cordova plugin JS interface.
 */
function D1Plugin() {
}

// DOC - start for callbacks.

/**
 * Error callback.
 * @callback ErrorCallback
 * @param {JSON{message: string, code: number}[]} errorDescription Description of error in form of a JSON string.
*/

/**
 * Generic success callback with no parameters.
 * @callback GenericSuccessCallback
*/

// DOC - end for callbacks.

/**
 * Configures the D1 SDK.
 * 
 * @param {GenericSuccessCallback} callback Success callback.
 * @param {ErrorCallback} errorCallback Error callback.
 * @param {string} serviceUrl D1 Service URL.
 * @param {string} issuerId Issuer ID.
 * @param {number[]} exponent Public key modulus.
 * @param {number[]} modulus Public key exponent.
 * @param {string} digitalCardUrl D1 digital card URL.
 * @param {string} consumerId End user ID.
 * @param {string} visaClientId Visa client ID.
 * @param {string} samsungServiceId Samsung service ID.
 */
D1Plugin.prototype.configure = function (callback, errorCallback, serviceUrl, issuerId, exponent, modulus, digitalCardUrl, consumerId) {
    exec(callback, errorCallback, PLUGIN_NAME, 'configure', [serviceUrl, issuerId, exponent, modulus, digitalCardUrl, consumerId, visaClientId, samsungServiceId]);
};

/**
 * Logs in to D1.
 *
 * @param {GenericSuccessCallback} callback Success callback.
 * @param {ErrorCallback} errorCallback Error callback.
 * @param {string} token JWT auth token.
 */
D1Plugin.prototype.login = function (callback, errorCallback, token) {
    exec(callback, errorCallback, PLUGIN_NAME, 'login', [token]);
};

// other methods - getCardMetaData, getCardDetails etc.

// module export

var d1Plugin = new D1Plugin();
module.exports = d1Plugin;
```

{% endcode %}

#### Android platform

#### Java source code

For the JavaScript method prototypes, it is necessary to create the approriate entry point in the Android Native code. To implement the Native Android entry point of the plugin, extend the Cordova Java class `CordovaPlugin` and everride the `execute` method.

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

```
package com.thalesgroup.plugin.d1;

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

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.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
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 androidx.annotation.NonNull;

/**
 * Android native code entry point for D1 Plugin.
 */
public class D1Plugin extends CordovaPlugin {

    private D1Task mD1Task;
    private TransactionListener mD1PayTransactionListener;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void pluginInitialize() {
        super.pluginInitialize();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean execute(final String action, final JSONArray data, final CallbackContext callbackContext)
            throws JSONException {
        switch (action) {
            case "configure": {
                final String serviceUrl = data.getString(0);
                final String issuerId = data.getString(1);
                final byte[] exponent = jsonArrayToByteArray(data.getJSONArray(2));
                final byte[] modulus = jsonArrayToByteArray(data.getJSONArray(3));
                final String digitalCardUrl = data.getString(4);
                final String consumerId = data.getString(5);
                final String visaClientId = data.getString(6);
                final String samsungServiceId = data.getString(7);

                configure(cordova.getContext(),
                          cordova.getActivity(),
                          serviceUrl,
                          issuerId,
                          exponent,
                          modulus,
                          digitalCardUrl,
                          consumerId,
                          visaClientId,
                          samsungServiceId,
                          callbackContext);
                break;
            }
            case "login":
                final String token = data.getString(0);
                login(token.getBytes(StandardCharsets.UTF_8), callbackContext);
                break;
            default:
                callbackContext.error("undefined action");
                return false;
        }

        return true;
    }

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

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

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

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

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

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

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

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

    /**
     * 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);
    }

    /**
     * Converts JSON array to byte array.
     *
     * @param jsonArray Input JSON array.
     * @return Byte array.
     * @throws JSONException If non valid JSON.
     */
    private byte[] jsonArrayToByteArray(@NotNull final JSONArray jsonArray) throws JSONException {
        final byte[] bytes = new byte[jsonArray.length()];
        for (int i = 0; i < jsonArray.length(); i++) {
            bytes[i] = (byte) (((int) jsonArray.get(i)) & 0xFF);
        }

        return bytes;
    }

    /**
     * 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;
    }

    /**
     * {@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 %}

#### Adding D1 SDK binaries to the plugin module

Add the D1 SDK binaries to the `d1-build.gradle` file of the **Plugin** module. You may also add the other plugin dependencies in this file.

{% code title="src/android/d1-build.gradle" overflow="wrap" expandable="true" %}

```
android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

allprojects {
    repositories {
        flatDir{
            dirs '../app/src/main/libs'
        }
    }
}

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"
    implementation "com.google.android.gms:play-services-base:18.0.1"
    implementation "androidx.appcompat:appcompat:[1.0.0,1.3.1]"
    implementation platform('com.google.firebase:firebase-bom:30.1.0')
    implementation 'com.google.firebase:firebase-messaging'
    releaseImplementation(name: 'd1-release', ext: 'aar')
    debugImplementation(name: 'd1-debug', ext: 'aar')
}
```

{% endcode %}

#### Updating the plugin manifest file for Android platform

The last step is to specify all files in the Plugin manifest file. You may also specify all additional Android Manfiest items (permissions and so on) in this file.

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

```
<?xml version='1.0' encoding='utf-8'?>
<plugin id="com.thalesgroup.plugin.d1" version="0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <name>D1Plugin</name>
    <js-module name="D1Plugin" src="www/D1Plugin.js">
        <clobbers target="D1Plugin" />
    </js-module>

    <!-- ios -->
    <platform name="ios">

    </platform>

    <!-- android -->
    <platform name="android">        
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="D1Plugin" >
                <param name="android-package" value="com.thalesgroup.plugin.d1.D1Plugin"/>
            </feature>
        </config-file>
        
        <config-file target="AndroidManifest.xml" parent="/manifest/application">
            <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>
        </config-file>

        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="D1Plugin" >
                <param name="android-package" value="com.thalesgroup.plugin.d1.D1Plugin"/>
            </feature>
        </config-file>
        
        <config-file target="AndroidManifest.xml" parent="/manifest/">
            <uses-feature android:name="android.hardware.nfc.hce" />
            <!-- In case the Application uses the biometric as CDCVM -->
            <uses-feature android:name="android.hardware.nfc" />
            <!--Even if deprecated from Android 10, this is needed for Android 9 and below -->
            <uses-permission android:name="android.permission.USE_FINGERPRINT" />

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

            <uses-permission android:name="android.permission.USE_BIOMETRIC" />
        </config-file>

        <config-file target="res/values/strings.xml" parent="/resources">
            <string name="label_payment">payment</string>
            <string name="label_d1_pay_description">payment description</string>
        </config-file>
        
        <resource-file src="src/android/assets/gemcbp.properties" target="assets/gemcbp.properties" />
        <resource-file src="src/android/assets/mobilegateway.properties" target="assets/mobilegateway.properties" />
        <resource-file src="src/android/assets/rages.properties" target="assets/rages.properties" />

        <resource-file src="src/android/xml/apduservice.xml" target="res/xml/apduservice.xml" />

        <source-file src="src/android/d1/D1Plugin.java" target-dir="src/com/thalesgroup/plugin/d1" />

        <framework src="src/android/d1-build.gradle" custom="true" type="gradleReference" />
        
        <resource-file src="src/android/libs/d1-debug.aar" target="libs/d1-debug.aar" />
        <resource-file src="src/android/libs/d1-release.aar" target="libs/d1-release.aar" />
    </platform>
</plugin>
```

{% endcode %}

#### iOS platform

#### Swift source code

For the JavaScript method prototypes, it is necessary to create the approriate entry point in the iOS Native code. To implement the Native iOS entry point of the plugin, extend the Cordova Objective-C class `CDVPlugin`.

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

```swift
import D1

/// D1 SDK iOS plugin entry point.
@objc(D1Plugin) class D1Plugin : CDVPlugin {
    
    private var d1Task: D1Task?
    
    @objc(configure:)
    /// Configures the D1 SDK.
    /// - Parameter command: Cordova command.
    func configure(command: CDVInvokedUrlCommand) {
        let serviceUrl = command.arguments[0] as! String
        let issuerId = command.arguments[1] as! String
        let exponent = command.arguments[2] as! NSArray
        let modulus = command.arguments[3] as! NSArray
        let digitalCardUrl = command.arguments[4] as! String
        let consumerId = command.arguments[5] as! String
        
        var exponentData = Data()
        for exponentByte in exponent {
            exponentData.append(exponentByte as! UInt8)
        }
        
        var modulusData = Data()
        for modulusByte in modulus {
            modulusData.append(modulusByte as! UInt8)
        }

        self.configure(serviceUrl, issuerId, exponentData, modulusData, digitalCardUrl, consumerId, self.commandDelegate, command.callbackId)
    }
    
    
    @objc(login:)
    /// Logs in in to D1.
    /// - Parameter command: Cordova command.
    func login(command: CDVInvokedUrlCommand) {
        let issuerToken = command.arguments[0] as! String
        self.login(issuerToken, self.commandDelegate, command.callbackId)
    }
    
    /// Configures the D1 SDK.
    /// - Parameters:
    ///   - d1ServiceURLString: Service URL.
    ///   - issuerID: Issuer ID.
    ///   - publicKeyExponent: Public Key Exponent.
    ///   - publicKeyModulus: Public Key Modulus.
    ///   - digitalCardURLString: Digital Cardl URL.
    ///   - consumerID: End user ID.
    ///   - callback: Callback.
    func configure(_ d1ServiceURLString:String, _ issuerID:String, _ publicKeyExponent: Data, _ publicKeyModulus: Data, _ digitalCardURLString:String, _ consumerID: String, _ callback: CDVCommandDelegate, _ callbackId: String) -> Void {
        
        var comp = D1Task.Components()
        comp.d1ServiceURLString = d1ServiceURLString
        comp.issuerID = issuerID
        comp.d1ServiceRSAExponent = publicKeyExponent
        comp.d1ServiceRSAModulus = publicKeyModulus
        comp.digitalCardURLString = digitalCardURLString
        d1Task = comp.task()
        
        let coreConfig = ConfigParams.coreConfig(consumerID: consumerID)
        // required for Card Processing & OEM Pay
        let cardConfig = ConfigParams.cardConfig()
        
        getD1Task().configure([coreConfig, cardConfig]) { errors in
            if let jsonError = self.createErrorJson(errors) {
                let pluginResult = CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: jsonError);
                callback.send(pluginResult, callbackId: callbackId);
                return
            }
            
            let pluginResult = CDVPluginResult(status: CDVCommandStatus_OK);
            callback.send(pluginResult, callbackId: callbackId);
        }
    }
    
    /// Logs is to D1 SDK.
    /// - Parameters:
    ///   - issueToken: Issuer token.
    ///   - callback: Callback.
    private func login(_ issueToken: String, _ callback: CDVCommandDelegate, _ callbackId: String) -> Void {
        if var issueTokenData: Data = issueToken.data(using: .utf8) {
            self.getD1Task().login(&issueTokenData) { error in
                if let jsonError = self.createErrorJson(error) {
                    let pluginResult = CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: jsonError);
                    callback.send(pluginResult, callbackId: callbackId);
                    return
                }
                
                let pluginResult = CDVPluginResult(status: CDVCommandStatus_OK);
                callback.send(pluginResult, callbackId: callbackId);
            }
        }
    }
    
    /// Creates a JSON response string from D1Error object.
    /// - Parameter error: D1Error object.
    /// - Returns: JSON string or nil if D1Error object is nil.
    private func createErrorJson(_ error: D1Error?) -> String? {
        if let error = error {
            do {
                let jsonEncoder = JSONEncoder()
                let jsonData = try jsonEncoder.encode(RD1Error(code: error.code.rawValue, message: error.localizedDescription))
                let jsonRD1Error = String(data: jsonData, encoding: String.Encoding.utf8)
                
                return jsonRD1Error
            } catch {
                // this should not happen
                fatalError()
            }
        }
        
        return nil
    }
    
    /// 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
    }
    
    /// 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.")
        }
    }
}
```

{% endcode %}

#### Updating the plugin manifest file for iOS platform

The last step is to specify all files in the Plugin manifest file.

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

```xml
<?xml version='1.0' encoding='utf-8'?>
<plugin id="com.thalesgroup.plugin.d1" version="0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <name>D1Plugin</name>
    <js-module name="D1Plugin" src="www/D1Plugin.js">
        <clobbers target="D1Plugin" />
    </js-module>

    <!-- android -->
    <platform name="android">  

    </platform>  

    <!-- ios -->
    <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <feature name="D1Plugin">
                <param name="ios-package" value="D1Plugin"/>
            </feature>
        </config-file>
      
        <source-file src="src/ios/D1Plugin.swift"/>

        <podspec>
            <pods use-frameworks="true">
                <pod name="d1-libs-debug" options=":path => '../../node_modules/d1-plugin/src/ios/D1/Debug/', :configurations => ['Debug']"/>
                <pod name="d1-libs-release" options=":path => '../../node_modules/d1-plugin/src/ios/D1/Release/', :configurations => ['Release']"/>
            </pods>
        </podspec>       
    </platform>    
</plugin>
```

{% endcode %}

#### D1 SDK Pod wrapper

The most simple way to differentiate between the Debug and Release version of D1 SDK on the iOS platform, is to wrap the D1 SDK binaries in to Pods.

**Debug Podspec**

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

```ruby
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="src/ios/D1/Debug/package.json" overflow="wrap" expandable="true" %}

```json
{
  "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="src/ios/D1/Release/d1-libs-release.podspec" overflow="wrap" expandable="true" %}

```ruby
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="src/ios/D1/Release/package.json" overflow="wrap" expandable="true" %}

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

### Integrating plugin module to Cordova project

1. Create a new Cordova project. To configure the D1 SDK on the Android platform for the D1 Push use case, you need to register the application ID for Google Pay. Use the application ID `com.thalesgroup.gemalto.d1.validation` for the Sandbox.

```
>> cordova create d1sample com.thalesgroup.gemalto.d1.validation D1Sample
```

2. Add the D1 plugin to the Cordova project.

```
>> cd d1sample
>> cordova plugin add /path/to/plugin
```

3. Add the Android and iOS platforms.

```
>> cordova platform add android
>> cordova platform add ios
```

4. Add other plugins and platforms.

```
>> cordova prepare
```

#### Android application signing

To run the NFC payment use case on the Android platform, the application needs to be signed with a specific keystore.

To sign an Android application with a specific keystore:

1. Copy the keystore file to the Cordova application folder and create a new `build.config` file.

```
-rw-r--r--  1 simon  staff  2053 Aug 29 15:49 android.keystore
-rw-r--r--  1 simon  staff   532 Aug 29 15:49 build.config
```

{% code title="d1sample/build.config" %}

```json
{
    "android": {
        "debug": {
            "keystore": "./android.keystore",
            "storePassword": "",
            "alias": "",
            "password" : "",
            "keystoreType": "",
            "packageType": "apk"
        },
        "release": {
            "keystore": "../android.keystore",
            "storePassword": "",
            "alias": "",
            "password" : "",
            "keystoreType": "",
            "packageType": "bundle"
        }
    }
}
```

{% endcode %}

2. Build the Android application with the specific `build.config` file.

```
>> cordova build android --buildConfig build.config
```

#### Minimum Android SDK version supported for Android apps

D1 SDK requires `AndroidX` and `minSdk=24`. To enable `AndroidX`, update the `config.xml` file.

{% code title="d1sample/config.xml" %}

```
<platform name="android">
  <preference name="AndroidXEnabled" value="true" />
</platform>
```

{% endcode %}

To set the minimum Android SDK version, export the `ORG_GRADLE_PROJECT_cdvMinSdkVersion=24` enviroment variable before building the Android application.

```
>> export ORG_GRADLE_PROJECT_cdvMinSdkVersion=24
>> cordova build android --buildConfig build.config
```

#### Firebase Cloud Messaging

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

{% code title="d1sample/config.xml" %}

```
<platform name="android">
  <preference name="GradlePluginGoogleServicesEnabled" value="true" />
  <resource-file src="google-services.json" target="app/google-services.json" />
</platform>
```

{% endcode %}

#### Calling the D1 plugin API from the Cordova project

After the D1 plugin has been added to the Cordova project, the D1 API can be accessed from JavaScript.

{% code overflow="wrap" %}

```
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"

D1Plugin.configure(function() {
    // success
}, function(error) {
    // error
}, d1ServiceURLString, issuerID, publicKeyExponent, publicKeyModulus, digitalCardURLString, consumerID, null, null);

D1Plugin.login(function() {
    // success
}, function(error) {
    // error
}, token);
```

{% endcode %}

### Integrating plugin module to Ionic Cordova project

The Cordova plugin can be also used in Ionic Cordova projects.

1. Create a new Ionic Cordova application.

```
>> ionic start --cordova
```

2. Add the D1 plugin to the Ionic Cordova project.

```
>> ionic cordova plugin add /path/to/plugin
```

3. Add the Android and iOS platforms.

```
>> ionic cordova platform add ios
>> ionic cordova platform add android
```

4. Build the web assets and copy them to the respective platforms.

```
>> ionic cordova prepare
```

{% hint style="info" %}
**Troubleshooting**

If the `ionic cordova build android --buildConfig build.config` command returns the error `Error: Unknown argument: platform ionic cordova`, you will need to install `cordova-builders`.

```
ng add @ionic/cordova-builders
```

{% endhint %}

### References

* D1 portal
* D1 Android Sample application
* D1 iOS Sample application
* Apache Cordova
* Ionic


---

# 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/apache-cordovawoshited1-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.
