# EMV Configuration Guide

This document covers the full EMV configuration system: the XML config file format, how AIDs and CAPKs are loaded into hardware at startup, how the three loader implementations work, and how remote TMS updates replace the configuration at runtime.

---

## 1. Overview

```
assets/YSDK_L3_configuration.xml
  └─ EmvConfigXmlParser.parseXml()
        ├─ IEmvParamLoader.loadCtAid()       → Contact AID → hardware
        ├─ IEmvParamLoader.loadClessAid()    → Contactless AID → hardware
        └─ IEmvParamLoader.loadCapk()        → CAPK → hardware

Three loader implementations chosen at runtime:
  YsdkEmvParamLoader  — Morefun YSDK hardware (default)
  BEmvParamLoader     — Newland NSDK built-in
  BExtEmvParamLoader  — Newland NSDK external PIN pad
```

The config file name is defined in `core/.../constant/FileConst.java`:
```java
EMV_CONFIG = "YSDK_L3_configuration.xml"
```

---

## 2. Config File Structure — `YSDK_L3_configuration.xml`

The file lives in `core/src/main/assets/` and has this shape:

```xml
<root>
  <config name="CONTACT">
    <entry name="Terminal Configuration"> ... </entry>
    <entry name="AID CT VISA A0000000031010"> ... </entry>
    <entry name="AID CT MC A0000000041010"> ... </entry>
    <!-- one <entry> per card AID -->
  </config>

  <config name="CONTACTLESS">
    <!-- same structure as CONTACT; currently empty in this build -->
  </config>

  <config name="PublicKeys">
    <entry name="CAPK VISA 89"> ... </entry>
    <entry name="CAPK MASTERCARD 05"> ... </entry>
    <!-- one <entry> per CAPK -->
  </config>
</root>
```

### 2.1 Terminal Configuration Entry

The first entry in each interface config section (`name="Terminal Configuration"`) sets terminal-wide defaults. These are loaded via `loader.loadCtAid(tlv, isTerminalAid=true)`.

```xml
<entry name="Terminal Configuration">
  <item tag="9F06" value="00000000000000000000000000000000" />  <!-- AID wildcard -->
  <item tag="DF24" value="F4C0F0E8EF0E60" />                   <!-- ICS -->
  <item tag="5F2A" value="0784" />                              <!-- Transaction Currency Code -->
  <item tag="9F35" value="22" />                               <!-- Terminal Type -->
  <item tag="9F33" value="E0F8C8" />                           <!-- Terminal Capabilities -->
  <item tag="9F40" value="FF80F0A001" />                       <!-- Additional Terminal Capabilities -->
  <item tag="9F1A" value="0784" />                             <!-- Terminal Country Code -->
  <item tag="9F1B" value="00000000" />                         <!-- Terminal Floor Limit (cents) -->
  <item tag="9F1C" value="3132333435363738" />                 <!-- Terminal Identification (ASCII) -->
  <item tag="9F1E" value="3030303030303031" />                 <!-- IFD Serial Number (ASCII) -->
  <item tag="9F01" value="123456789000" />                     <!-- Acquirer Identifier -->
  <item tag="9F15" value="1234" />                             <!-- Merchant Category Code -->
  <item tag="9F16" value="313233343536373839303132333435" />   <!-- Merchant Identifier (ASCII) -->
  <item tag="9F3C" value="0784" />                             <!-- Reference Currency Code -->
  <item tag="5F36" value="02" />                               <!-- Transaction Currency Exponent -->
  <item tag="9F09" value="0002" />                             <!-- Application Version Number -->
  <item tag="DF44" value="9F3704" />                           <!-- Default DDOL -->
  <item tag="DF45" value="9F02065F2A029A039C0195059F3704" />  <!-- Default TDOL -->
  <item tag="9F7A" value="01" />                               <!-- EC Terminal Support Indicator -->
  <item tag="DF15" value="00000000" />                         <!-- Random selection threshold -->
  <item tag="DF16" value="00" />                               <!-- Max target % for random selection -->
  <item tag="DF17" value="00" />                               <!-- Target % for random selection -->
  <item tag="9F7B" value="000000000000" />                     <!-- Reader Contactless TX Limit -->
  <item tag="DF01" value="00" />                               <!-- Application Selection Indicator -->
</entry>
```

### 2.2 AID Entry

Every card application AID gets its own `<entry>`. These are loaded via `loader.loadCtAid(tlv, isTerminalAid=false)`.

```xml
<entry name="AID CT VISA A0000000031010">
  <item tag="9F06" value="A0000000031010" />   <!-- AID — mandatory -->
  <item tag="DF01" value="00" />               <!-- Application Selection Indicator (0=partial, 1=exact) -->
  <item tag="9F09" value="0096" />             <!-- Application Version Number -->
  <item tag="DF11" value="DC4000A800" />       <!-- TAC Default -->
  <item tag="DF12" value="DC4004F800" />       <!-- TAC Online -->
  <item tag="DF13" value="0010000000" />       <!-- TAC Denial -->
  <item tag="9F1B" value="00000000" />         <!-- Floor Limit (per-AID, overrides terminal) -->
  <item tag="DF14" value="9F3704" />           <!-- DDOL -->
  <item tag="DF15" value="00000000" />         <!-- Random selection threshold -->
  <item tag="DF16" value="00" />               <!-- Max target % -->
  <item tag="DF17" value="00" />               <!-- Target % -->
  <item tag="DF18" value="01" />               <!-- Online PIN support -->
  <item tag="9F1D" value="0000000000000000" /> <!-- Terminal Risk Management Data -->
  <item tag="9F7B" value="000000000000" />     <!-- Contactless TX Limit -->
  <item tag="DF19" value="000000000000" />     <!-- Contactless Floor Limit -->
  <item tag="DF20" value="999999999999" />     <!-- Contactless Transaction Limit (Ctless Limit) -->
  <item tag="DF21" value="000000050000" />     <!-- Contactless CVM Limit -->
</entry>
```

#### TAC Bitmask Explained

TAC (Terminal Action Code) bitmasks tell the terminal which TVR (Terminal Verification Results) bits trigger each outcome:

| Tag | Name | Effect when bit set |
|-----|------|---------------------|
| `DF11` | TAC Default | Decline offline if bit matches TVR |
| `DF12` | TAC Online | Force online if bit matches TVR |
| `DF13` | TAC Denial | Unconditional decline if bit matches TVR |

#### Contactless Limit Tags

| Tag | Name | Notes |
|-----|------|-------|
| `DF19` | Contactless Floor Limit | Amount below which contactless is allowed without going online |
| `DF20` | Contactless Transaction Limit | Maximum amount for tap-and-go |
| `DF21` | Contactless CVM Limit | Above this, CVM (PIN/signature) required for contactless |

### 2.3 CAPK Entry

Each CAPK (Certification Authority Public Key) is an `<entry>` under `<config name="PublicKeys">`. Key attributes use `key=` / `value=` instead of `tag=` / `value=`.

```xml
<entry name="CAPK VISA 89">
  <item key="RID"            value="A000000003" />                  <!-- 5-byte Registered Application Provider ID -->
  <item key="Index"          value="89" />                          <!-- CAPK Index (hex) -->
  <item key="Hash"           value="7170850B97F83952045CF9CA8B7612DFEB69E9EF" />
  <item key="Exponent"       value="03" />                          <!-- RSA public exponent -->
  <item key="Modulus"        value="E5E195705CE6..." />             <!-- RSA modulus (hex) -->
  <item key="Hash Algorithm" value="01" />                          <!-- 1 = SHA-1 -->
  <item key="Sign Algorithm" value="01" />                          <!-- 1 = RSA -->
</entry>
```

### 2.4 AIDs Configured in This Build

#### Contact AIDs

| Entry Name | AID | Notes |
|-----------|-----|-------|
| Terminal Configuration | — | Terminal-wide defaults |
| AID CT Amex | `A00000002501` | American Express |
| AID CT CUP Debit | `A000000333010101` | China UnionPay Debit |
| AID CT CUP Credit | `A000000333010102` | China UnionPay Credit |
| AID CT CUP Quasi Credit | `A000000333010103` | China UnionPay Quasi Credit |
| AID CT Discover | `A0000001523010` | Discover |
| AID CT Jaywan | `A0000009122024` – `A0000009122225` | Jaywan (UAE) — 6 AIDs |
| AID CT JCB | `A0000000651010` | JCB |
| AID CT MC | `A0000000041010` | Mastercard Credit/Debit |
| AID CT MC Maestro | `A0000000043060` | Maestro |
| AID CT RUPAY | `A0000005241010` | RuPay (India) |
| AID CT VISA | `A0000000031010` | Visa Credit/Debit |
| AID CT VISA Electron | `A0000000032010` | Visa Electron |

#### Contactless AIDs

Currently **empty** in `YSDK_L3_configuration.xml`. Contactless limits for contact AIDs are embedded in the contact AID entries via `DF19`, `DF20`, `DF21`.

### 2.5 CAPKs Configured in This Build

| Scheme | RID | Indices |
|--------|-----|---------|
| VISA | `A000000003` | 89, 92, 94, 52 |
| Mastercard | `A000000004` | 05, 06, EF, F0, F1, F3, F4, F8, FA, FE |
| JCB | `A000000065` | 08, 0F, 11, 12, 13, 14 |
| PBOC / CUP | `A000000333` | 08, 09, 0B |
| Amex | `A000000025` | 04, 0F, 10, 62, 64, 65, 66, 67, 68, C3, C8, C9, CA |
| Discover / Diners | `A000000152` | 5C, 5D |
| Jaywan | `A000000912` | 01, FE |
| RuPay | `A000000524` | 6A, 6B, 6C, 6D |

---

## 3. Loader Selection — `SelfCheckHelper.loadEmvConfig()`

`core/.../tools/SelfCheckHelper.java` selects the loader at startup based on two params:

```
PARAMS_KEY_EXTERNAL_PINPAD  (boolean)
PARAMS_KEY_YSDK_HARDWARE    (boolean, default true)

external == true            → BExtEmvParamLoader  (external PIN pad via NSDK Ext)
external == false, isYsdk   → YsdkEmvParamLoader  (Morefun YSDK hardware — default)
external == false, !isYsdk  → BEmvParamLoader     (Newland NSDK built-in)
```

### Reload Decision

```java
boolean loadAidCapk =
    !ParamsUtils.getBoolean(PARAMS_KEY_EMV_AID_CAPK)  // flag not set (first run / cleared)
    || forceLoad                                        // explicit reload requested
    || loader.isCapkLoss()                             // hardware reports no CAPKs
    || loader.isCtAidLoss()                            // hardware reports no contact AIDs
    || loader.isClessAidLoss();                        // hardware reports no contactless AIDs
```

Each `is*Loss()` queries the hardware counter:
- `BEmvParamLoader` / `BExtEmvParamLoader` — calls `aidLoader.getAidCount()` / `capkLoader.getCapkCount()`
- `YsdkEmvParamLoader` — calls `emvHandler.getAidParaList().size() == 0`

On successful load, `PARAMS_KEY_EMV_AID_CAPK` is set to `true` so subsequent restarts skip re-loading unless hardware reports data loss.

---

## 4. XML Parsing — `EmvConfigXmlParser.parseXml()`

`core/.../tools/EmvConfigXmlParser.java`

```
parseXml(context/inputStream, loader)
  │
  ├─ loader.clearCtAid()       ← wipe existing contact AIDs from hardware
  ├─ loader.clearClessAid()    ← wipe existing contactless AIDs
  ├─ loader.clearCapk()        ← wipe existing CAPKs
  │
  ├─ Parse XML DOM: root → config[] → entry[] → item[]
  │
  ├─ config name="CONTACT" or "CONTACTLESS":
  │    For each entry:
  │      ├─ Pack all <item tag="..." value="..."> into TLV byte array
  │      ├─ isTerminalEntry = (entryName == "Terminal Configuration")
  │      └─ loader.loadCtAid(hexTlv, isTerminalEntry)
  │             or loader.loadClessAid(hexTlv, isTerminalEntry)
  │
  └─ config name="PublicKeys":
       For each entry:
         ├─ Populate CapkBean from <item key="..." value="...">
         └─ loader.loadCapk(capkBean)
```

**Important:** The parser clears all existing hardware data before loading. A parse failure mid-way will leave the hardware partially populated — the `PARAMS_KEY_EMV_AID_CAPK` flag is only set `true` on full success, so the next startup will re-attempt.

Custom EMV tags (`<CUSTOM>` child elements within an AID entry) are encoded into a proprietary TLV at tag `0x1F811F` and appended to the AID TLV bundle.

---

## 5. Loader Implementations

### 5.1 `YsdkEmvParamLoader` — Default

Uses Morefun YSDK `EmvHandler` from `DeviceHelper.getEmvHandler()`.

```java
// Contact AID
emvHandler.addAidParam(HexUtil.hexStringToByte(aidTlv));  // terminal config: no-op (TODO)

// Contactless AID — same method, different interface flag
emvHandler.addAidParam(HexUtil.hexStringToByte(aidTlv));

// CAPK — builds a TLV list using JPos TLVList
TLVList tlv = new TLVList();
tlv.append(0x9F06, capkBean.getRid());
tlv.append(0x9F22, new byte[]{ (byte) capkBean.getIndex() });
tlv.append(0xDF02, HexUtil.hexStringToByte(capkBean.getModulus()));
tlv.append(0xDF04, HexUtil.hexStringToByte(capkBean.getExponent()));
tlv.append(0xDF03, HexUtil.hexStringToByte(capkBean.getHash()));
tlv.append(0xDF06, String.valueOf(capkBean.getAlgorithmIndicator()));
tlv.append(0xDF07, String.valueOf(capkBean.getHashAlgorithm()));
tlv.append(0xDF05, "20261231");   // hardcoded CAPK expiry
emvHandler.addCAPKParam(tlv.pack());
```

### 5.2 `BEmvParamLoader` — NSDK Built-in

Uses `EmvProvider.getInstance()` to get NSDK `AID` and `CAPK` objects.

```java
// AID
byte[] aidTlvBytes = BytesUtils.hexToBytes(aidTlv);
if (isTerminalAid) {
    aidContactLoader.loadTerminalConfig(aidTlvBytes);
} else {
    aidContactLoader.loadAID(aidTlvBytes);
}

// CAPK
CAPKEntry capkEntry = new CAPKEntry();
capkEntry.setIndex(capkBean.getIndex());
capkEntry.setRID(BytesUtils.hexToBytes(capkBean.getRid()));
capkEntry.setHash(BytesUtils.hexToBytes(capkBean.getHash()));
capkEntry.setExponent(BytesUtils.hexToBytes(
    StringUtils.fill(capkBean.getExponent(), "0", 6, true)));  // left-pad to 3 bytes
capkEntry.setModulus(BytesUtils.hexToBytes(capkBean.getModulus()));
capkEntry.setModuleLen(capkEntry.getModulus().length);
capkEntry.setHashAlgorithm((byte) capkBean.getHashAlgorithm());
capkEntry.setAlgorithmIndicator((byte) capkBean.getAlgorithmIndicator());
capkLoader.load(capkEntry);
```

NSDK `EmvL3` is initialised in `EmvProvider`:
```java
emvL3.configUnSet(config, L3_CFG_SUPPORT_EC);          // EC disabled
emvL3.configUnSet(config, L3_CFG_SUPPORT_SM);          // SM disabled
emvL3.configSet(config, L3_CFG_SUPPORT_RF_AFTERFINALSELECT);
emvL3.init(context.getFilesDir() + "/emv/", config);
```

### 5.3 `BExtEmvParamLoader` — External PIN Pad

Identical API to `BEmvParamLoader` but uses `ExtAID` / `ExtCAPK` from the NSDK external module. All calls are wrapped in `try-catch (NSDKException)` — exceptions return `false` rather than checking integer return codes. Requires `ExtServiceHelper.getInstance().isInit() == true`.

---

## 6. `IEmvParamLoader` Interface

`sdk_helper/.../emv/IEmvParamLoader.java`

| Method | Description |
|--------|-------------|
| `loadCtAid(String aidTlv, boolean isTerminalAid)` | Load one contact AID or terminal config block |
| `loadClessAid(String aidTlv, boolean isTerminalAid)` | Load one contactless AID or terminal config block |
| `loadCapk(CapkBean capkBean)` | Load one CAPK entry |
| `deleteOneCtAid(String aid9F06)` | Remove a single contact AID by AID value |
| `deleteOneClessAid(String aid9F06)` | Remove a single contactless AID by AID value |
| `deleteOneCapk(String rid, String index)` | Remove a CAPK by RID + index |
| `clearCtAid()` | Wipe all contact AIDs from hardware |
| `clearClessAid()` | Wipe all contactless AIDs from hardware |
| `clearCapk()` | Wipe all CAPKs from hardware |
| `isCtAidLoss()` | Returns `true` if hardware has no contact AIDs |
| `isClessAidLoss()` | Returns `true` if hardware has no contactless AIDs |
| `isCapkLoss()` | Returns `true` if hardware has no CAPKs |

---

## 7. Remote EMV Update — `UPDATE_L3_CONFIG`

When the TMS sends an `UPDATE_L3_CONFIG` MQTT command, the flow is:

```
MqttIntegration.onMessageReceived()
  └─ TmsCommandHandler.handleUpdateL3Config(requestId, payload)
        ├─ ConfigFileManager.getVersion("emv_config") vs payload.version
        │    └─ Same version → MQTT "success" (skip)
        ├─ MQTT "initiated"
        └─ RemoteParamsUpdater.updateParams(ctx, downloadUrl, version, "emv_config")
              └─ FlyParameterHelper.fetchParametersIgnoreSsl(downloadUrl)
                   └─ ZIP response contains entry "Newland_L3_configuration" → InputStream
                        └─ RemoteParamsUpdater.parseEmv(inputStream)
                              ├─ Select loader (BExtEmvParamLoader or BEmvParamLoader)
                              │   Note: YsdkEmvParamLoader is NOT used for remote updates —
                              │   remote path selects based on PARAMS_KEY_EXTERNAL_PINPAD only
                              └─ EmvConfigXmlParser.parseXml(inputStream, loader)
                                   └─ Same clear → load pipeline as startup
                                   └─ On success: PARAMS_KEY_EMV_AID_CAPK = true
                                                  ConfigFileManager.setVersion("emv_config", version)
                                                  MQTT "success"
```

The remote ZIP package must contain a file named `Newland_L3_configuration` (without extension). The file format is identical to `YSDK_L3_configuration.xml`.

---

## 8. `CapkBean` Data Model

`sdk_helper/.../emv/bean/CapkBean.java`

| Field | Type | XML attribute | Description |
|-------|------|--------------|-------------|
| `rid` | String | `RID` | 5-byte Registered Application Provider ID (hex) |
| `index` | int | `Index` | CAPK index — parsed as hex (`Integer.parseInt(value, 16)`) |
| `hash` | String | `Hash` | SHA-1 or SHA-256 hash of the public key (hex) |
| `exponent` | String | `Exponent` | RSA public exponent — `"03"` or `"010001"` (hex) |
| `modulus` | String | `Modulus` | RSA modulus (hex, variable length) |
| `hashAlgorithm` | int | `Hash Algorithm` | `1` = SHA-1, `2` = SHA-256 |
| `algorithmIndicator` | int | `Sign Algorithm` | `1` = RSA |
| `expiredDate` | String | — | Not set from XML; `YsdkEmvParamLoader` hardcodes `"20261231"` |

---

## 9. Adding a New AID or CAPK

### New AID

1. Add a new `<entry>` block under `<config name="CONTACT">` in `YSDK_L3_configuration.xml`.
2. `name` attribute is free text (used only for logging).
3. Tag `9F06` must contain the full AID hex string.
4. Include at minimum: `9F06` (AID), `DF11`/`DF12`/`DF13` (TAC triplet), `9F09` (version), `DF01` (selection indicator).
5. Add contactless limits (`DF19`, `DF20`, `DF21`) if the scheme supports tap-and-go.

### New CAPK

1. Add a new `<entry>` block under `<config name="PublicKeys">`.
2. All seven `key=` attributes are required: `RID`, `Index`, `Hash`, `Exponent`, `Modulus`, `Hash Algorithm`, `Sign Algorithm`.
3. `Index` value is hex (e.g., `"89"`).
4. `Hash Algorithm` and `Sign Algorithm` are decimal (e.g., `"01"`).

### Deploying via TMS

Send an `UPDATE_L3_CONFIG` MQTT command with a `downloadUrl` pointing to a ZIP file. The ZIP must contain a file named `Newland_L3_configuration` whose content matches the XML schema above. Increment `version` to bypass the version-check guard.

---

## 10. Relevant Source Files

| File | Role |
|------|------|
| `core/src/main/assets/YSDK_L3_configuration.xml` | Master EMV config — AIDs and CAPKs |
| `core/.../constant/FileConst.java` | Defines `EMV_CONFIG = "YSDK_L3_configuration.xml"` |
| `core/.../tools/SelfCheckHelper.java` | Loader selection, reload decision, startup trigger |
| `core/.../tools/EmvConfigXmlParser.java` | XML → TLV → loader calls |
| `core/.../tools/RemoteParamsUpdater.java` | Remote update: parseEmv() path |
| `core/.../tms/TmsCommandHandler.java` | MQTT UPDATE_L3_CONFIG handler |
| `sdk_helper/.../emv/IEmvParamLoader.java` | Loader interface |
| `sdk_helper/.../emv/YsdkEmvParamLoader.java` | YSDK hardware loader (default) |
| `sdk_helper/.../emv/BEmvParamLoader.java` | NSDK built-in loader |
| `sdk_helper/.../emv/BExtEmvParamLoader.java` | NSDK external PIN pad loader |
| `sdk_helper/.../emv/EmvProvider.java` | NSDK EmvL3 / ExtEMVL3 singleton |
| `sdk_helper/.../emv/bean/CapkBean.java` | CAPK data model |
