# Mastercard Tap and PIN (Two-Leg Transaction Flow)

## Overview

Mastercard contactless (NFC) transactions may require online PIN verification when the issuer demands it. This is handled as a two-leg flow involving three parties: the POS Device, jPOS (this middleware), and YSP Host.

---

## Players

| Player | Role |
|--------|------|
| Device | POS terminal accepting Mastercard via NFC |
| jPOS | Middleware — translates between device and YSP |
| YSP | Acquiring host — approves or declines |

---

## Transaction Flow

### 1st Leg — Tap without PIN

```
Device  →  jPOS  →  YSP
               ←  YSP responds: DE39=65, DE48 containing tag 22 with value "04011"
Device  ←  jPOS   (relays DE39=65, DE48, and DE60 with PIN=true)
```

1. Device taps card via NFC and sends a standard MTI 0200 sale request (no DE52/DE53).
2. jPOS forwards to YSP as normal.
3. YSP responds with **DE39=65** (PIN required) and **DE48** containing the PIN indicator block.
4. jPOS parses DE48, detects the Tap+PIN condition, and returns to the device:
   - **DE39=65** — unchanged
   - **DE48** — relayed as-is from YSP
   - **DE60** — standard bank fields plus `"PIN": true`

### 2nd Leg — PIN Entry

```
Device  →  jPOS  →  YSP   (DE48="R22100201103011", DE52, DE53)
               ←  YSP responds: DE39=00 (approved) or decline
Device  ←  jPOS
```

1. Device detects DE39=65 from the 1st leg response, prompts cardholder for PIN.
2. Device resends the same MTI 0200 with the same processing code, adding:
   - **DE47** — JSON with `"pin": true` plus original transaction reference fields (`origTrace`, etc.)
   - **DE52** — Encrypted PIN block
   - **DE53** — Key Serial Number (KSN) for DUKPT
3. jPOS detects `pin=true` in DE47, constructs the YSP request with:
   - **DE48=`R22100201103011`** (fixed constant required by YSP for online PIN)
   - **DE52** — PIN block passed through unchanged
   - **DE53** — KSN passed through unchanged (NOT overwritten from DE63)
4. YSP approves or declines; jPOS relays the response to the device normally.

---

## DE48 Format (YSP)

YSP DE48 uses a proprietary TLV-like encoding:

```
[Alpha prefix] [Tag(2)] [Length(2, decimal)] [Value(Length chars)] ...
```

**Example:**
```
R5754GHS644ATT000ATO000AIS000MFR000CBR065CBV100ORS702MFL38333420101M0304310306115011003027308020502041633
│ ││└──────────────────────────────────────────────────────────┘│└──────────────────────────────────────┘
│ ││                    Value (54 chars)                        │           Value (42 chars)
│ │└─ Length=54                                                 └─ Length=42
│ └── Tag=57                                                   Tag=33
└── Prefix 'R'
```

| Component | Description |
|-----------|-------------|
| Alpha prefix | 1+ letters at the start (e.g., `R`) — skipped during parsing |
| Tag | 2 ASCII characters |
| Length | 2 ASCII decimal digits — byte count of the Value |
| Value | `Length` ASCII characters |

### Tap+PIN Detection

A YSP 1st leg response is a valid Tap+PIN trigger when **tag `22`** is present in DE48 and its value **contains `04011`**.

Example block: `220504011` → Tag=`22`, Length=`05`, Value=`04011`

---

## DE39=65 Handling Rules

| Condition | Action |
|-----------|--------|
| DE39=65 AND DE48 has tag `22` containing `04011` | Keep DE39=65, relay DE48, set DE60 `PIN=true` |
| DE39=65 AND DE48 is absent or tag `22` missing/value doesn't contain `04011` | Remap DE39 to **66** |

---

## DE47 Structure (2nd Leg)

DE47 carries a JSON `AdditionalData` payload. In the 2nd leg, the `pin` field is added:

```json
{
  "pin": true,
  "origMti": "0200",
  "origTrace": "000042",
  "origDate": "20260704",
  "origTime": "103055"
}
```

The `origTrace` (DE11 STAN of the 1st leg) links the two legs. The 2nd leg has a **new** DE11 STAN.

---

## Key Code Locations

| Concern | Class / File |
|---------|-------------|
| DE48 TLV parser | `org.jpos.tcpay.acquirer.ysp.YspDe48Parser` |
| DE39=65 remapping + DE48 relay + DE60 PIN flag | `JposYspTranslator.fromMessage()` |
| 2nd leg DE48 injection + DE53 passthrough | `YspRequestManager.getSaleMsg()` |
| `pin` field in DE47 | `org.jpos.tcpay.data_element.AdditionalData` |
| `PIN` field in DE60 | `org.jpos.tcpay.data_element.JposResponseDataElement60` |

---

## Field Mapping Summary

### 1st Leg — YSP Response → Device Response

| Field | Source | Notes |
|-------|--------|-------|
| DE39 | YSP response | Kept as 65 only when Tap+PIN indicator present in DE48 |
| DE48 | YSP response | Relayed as-is |
| DE60.PIN | Derived | Set to `true` when Tap+PIN detected |

### 2nd Leg — Device Request → YSP Request

| Field | Source | Notes |
|-------|--------|-------|
| DE48 | Fixed constant | `R22100201103011` — always this value for online PIN |
| DE52 | Device | PIN block cloned from device request unchanged |
| DE53 | Device | KSN cloned from device request; DE63→DE53 mapping is skipped |

---

## CLAUDE.md Reference

Add the following row to the feature doc table in `CLAUDE.md`:

```
| Mastercard Tap and PIN flow | `docs/feature/txn/tap-and-pin.md` |
```
