Module 4: Key Data Elements Deep Dive

1 / 24

Slide 1: The Heart of the Message

You've parsed the structure. Now understand the meaning.

Field  Name                    What It Tells You
─────  ────                    ─────────────────
DE2    Primary Account Number  Which card
DE3    Processing Code         What type of transaction
DE4    Amount                  How much money
DE7    Transmission DateTime   When (UTC)
DE11   STAN                    Unique transaction ID
DE35   Track 2 Data            Swipe data (PAN + expiry + more)
DE37   RRN                     Network reference number
DE39   Response Code           Approved or why not

These fields appear in 90% of authorization messages.

2 / 24

Slide 2: DE2 - Primary Account Number (PAN)

PAN: 4532015112830366
     ├──────┤├──────────┤├┤
     IIN/BIN  Account    Check
              Number     Digit

IIN First Digits:
4...       = Visa
51-55...   = Mastercard
34, 37...  = American Express
6011, 65.. = Discover

Key facts:

  • LLVAR n..19 (variable length, 13-19 digits)
  • Last digit is Luhn check digit
  • First 6-8 digits identify the issuer
  • Never log full PAN - mask as 453201******0366
3 / 24

Slide 3: Luhn Algorithm

Validate PAN integrity - catches typos and transmission errors.

PAN: 4 5 3 2 0 1 5 1 1 2 8 3 0 3 6 6

Step 1: From right, double every second digit
        8 5 6 2 0 1 10 1 2 2 16 3 0 3 12 6

Step 2: If result > 9, subtract 9
        8 5 6 2 0 1 1 1 2 2 7 3 0 3 3 6

Step 3: Sum all digits
        8+5+6+2+0+1+1+1+2+2+7+3+0+3+3+6 = 50

Step 4: Check: sum % 10 == 0?
        50 % 10 = 0 ✓ Valid!
4 / 24

Slide 4: Luhn in Go

func ValidateLuhn(pan string) bool {
    sum := 0
    double := false

    // Process right-to-left
    for i := len(pan) - 1; i >= 0; i-- {
        digit := int(pan[i] - '0')
        if digit < 0 || digit > 9 {
            return false
        }

        if double {
            digit *= 2
            if digit > 9 {
                digit -= 9
            }
        }
        sum += digit
        double = !double
    }

    return sum%10 == 0
}
5 / 24

Slide 5: DE3 - Processing Code Structure

DE3: 00 00 00
     ├┤ ├┤ ├┤
     │  │  └─ To Account Type
     │  └─ From Account Type
     └─ Transaction Type

Transaction Type (first 2 digits):
00 = Purchase
01 = Cash Advance
09 = Purchase with Cash Back
20 = Refund
30 = Balance Inquiry

Account Type (digits 3-6):
00 = Default
10 = Savings
20 = Checking
30 = Credit

Example: 010020 = Cash Advance from Credit to Checking

6 / 24

Slide 6: DE4 - Amount Field

Field: DE4 (Transaction Amount)
Type:  Fixed n 12
Value: "000000010000"  = ???

The answer depends on currency!

Currency Exponents (DE49):

CurrencyCodeExponent"000000010000" means
USD8402$100.00
EUR9782€100.00
JPY3920¥10000
KWD414310.000 KWD

Always pair DE4 with DE49!

7 / 24

Slide 7: The Three Amount Fields

Cross-border transaction: US cardholder in Japan

╔═══════════════════════════════════════════════════════╗
║  Merchant (Tokyo)    Acquirer       Cardholder (US)   ║
║     ¥5000              $33.00          $33.00         ║
╚═══════════════════════════════════════════════════════╝
     ↓                    ↓               ↓
   DE4                  DE5             DE6
   "000000005000"       "000000003300"  "000000003300"
   DE49="392"           DE50="840"      DE51="840"
   (JPY)                (USD)           (USD)
  • DE4: Transaction currency (what merchant charged)
  • DE5: Settlement currency (acquirer receives)
  • DE6: Billing currency (cardholder sees)
8 / 24

Slide 8: Date/Time Fields - The Timezone Trap

DE7:  Transmission Date/Time - n 10 (MMDDhhmmss) - UTC
DE12: Local Time - n 6 (hhmmss) - Terminal timezone
DE13: Local Date - n 4 (MMDD) - Terminal timezone

Example: Transaction in New York at 2:30 PM EST
DE7  = "0315193045"  (UTC: 19:30:45)
DE12 = "143045"      (Local: 14:30:45)
DE13 = "0315"        (Local: March 15)

Critical issues:

  1. No year in any date field!
  2. DE7 is UTC, DE12+DE13 are local
  3. Year boundary bugs at Dec 31 → Jan 1
9 / 24

Slide 9: DE11 (STAN) vs DE37 (RRN)

┌────────────────────────────────────────────────────────┐
│                                                        │
│  DE11 (STAN)              DE37 (RRN)                  │
│  ──────────               ──────────                  │
│  6 digits                 12 alphanumeric             │
│  Terminal-assigned        Network-assigned            │
│  Unique per terminal/day  Unique network-wide         │
│  For message matching     For customer service lookup │
│                                                        │
│  Example: "123456"        Example: "240315123456"     │
│                                                        │
└────────────────────────────────────────────────────────┘

STAN links request↔response RRN appears on receipts and statements

10 / 24

Slide 10: DE35 - Track 2 Data Structure

Track 2: 4532015112830366D2512101DDDDDDDDDDDDD
         ├──────PAN──────┤│├──┤├─┤├──────────┤
                         │ │   │  │
                         │ │   │  └ Discretionary data
                         │ │   └ Service code (3 digits)
                         │ └ Expiry YYMM
                         └ Separator (= or D)

Components:
- PAN:           4532015112830366
- Separator:     D
- Expiry:        2512 (December 2025)
- Service Code:  101
- Discretionary: DDDDDDDDDDDDD

Type: LLVAR z..37 (z = Track 2 code set)

11 / 24

Slide 11: Service Code Breakdown

Service Code: 1 0 1
              │ │ └─ Digit 3: PIN/Service restrictions
              │ └─ Digit 2: Authorization type
              └─ Digit 1: Interchange rules

Digit 1        Digit 2          Digit 3
────────       ─────────        ────────
1 = Intl+Chip  0 = Normal       0 = No restrictions
2 = Intl+Mag   2 = Call issuer  1 = No cash
5 = Natl+Chip  4 = Except floor 3 = ATM only
6 = Natl+Mag                    6 = PIN required

Example: 201 = International magnetic, normal auth, no cash

12 / 24

Slide 12: Parsing Track 2 in Go

type Track2Data struct {
    PAN           string
    ExpiryYYMM    string
    ServiceCode   string
    Discretionary string
}

func ParseTrack2(track2 string) (Track2Data, error) {
    // Find separator (= or D)
    sepIdx := strings.IndexAny(track2, "=D")
    if sepIdx == -1 {
        return Track2Data{}, fmt.Errorf("no separator")
    }

    pan := track2[:sepIdx]
    rest := track2[sepIdx+1:]

    // Expiry (4) + Service code (3) + Discretionary
    return Track2Data{
        PAN:           pan,
        ExpiryYYMM:    rest[0:4],
        ServiceCode:   rest[4:7],
        Discretionary: rest[7:],
    }, nil
}
13 / 24

Slide 13: DE39 - Response Codes

Code  Meaning                 Category
────  ───────                 ────────
00    Approved                ✓ Success
01    Refer to issuer         ⚠ Referral
05    Do not honor            ✗ Decline
12    Invalid transaction     ✗ Decline
14    Invalid card number     ✗ Decline (Luhn fail?)
41    Lost card - pick up     ✗ Decline (retain card)
51    Insufficient funds      ✗ Decline
54    Expired card            ✗ Decline
55    Incorrect PIN           ✗ Decline
91    Issuer unavailable      ⚠ Retry
96    System malfunction      ⚠ Retry

Rule: Only 00 means approved. Everything else needs handling.

14 / 24

Slide 14: Response Code Categorization

type ResponseCategory string

const (
    Approved    ResponseCategory = "approved"
    Declined    ResponseCategory = "declined"
    Referral    ResponseCategory = "referral"
    SystemError ResponseCategory = "system_error"
    PickUp      ResponseCategory = "pick_up"
)

func Categorize(code string) ResponseCategory {
    switch code {
    case "00":
        return Approved
    case "01", "02":
        return Referral
    case "41", "43":
        return PickUp
    case "91", "96":
        return SystemError
    default:
        return Declined
    }
}
15 / 24

Slide 15: DE54 - Additional Amounts

For balance inquiries and complex transactions:

DE54: "0001840C000000150000"
      ├┤├┤├──┤│├────────────┤
      00 01 840 C  000000150000

Structure (20 bytes per record):
- Bytes 1-2:  Account type (00 = default)
- Bytes 3-4:  Amount type (01 = available balance)
- Bytes 5-7:  Currency (840 = USD)
- Byte 8:     Sign (C = credit, D = debit)
- Bytes 9-20: Amount (000000150000 = $1500.00)

Multiple records can be concatenated.

16 / 24

Slide 16: Authorization Request Field Map

0100 Message - Typical Fields
═══════════════════════════════════════════════════════
MTI    "0100"                   Auth request
DE2    "4532015112830366"       PAN (LLVAR)
DE3    "000000"                 Purchase
DE4    "000000010000"           $100.00
DE7    "0315143045"             Mar 15, 14:30:45 UTC
DE11   "123456"                 STAN
DE12   "143045"                 Local time
DE13   "0315"                   Local date
DE14   "2512"                   Card expiry Dec 2025
DE22   "051"                    Chip read
DE35   "4532...D2512101..."     Track 2
DE37   "240315123456"           RRN
DE41   "TERM0001"               Terminal ID
DE42   "MERCH00001"             Merchant ID
DE49   "840"                    USD
═══════════════════════════════════════════════════════
17 / 24

Slide 17: Common Pitfalls

1. PAN Length
   ✗ if len(pan) != 16  // Amex is 15!
   ✓ if len(pan) < 13 || len(pan) > 19

2. Amount Without Currency
   ✗ amount := 10000  // $100 or ¥10000?
   ✓ type Money struct { Amount int64; Currency string }

3. Track 2 Separator
   ✗ strings.Index(track2, "=")   // Misses 'D'
   ✓ strings.IndexAny(track2, "=D")

4. Response Code as Number
   ✗ if code == 0    // String "00" vs int 0
   ✓ if code == "00"

5. Year in Dates
   ✗ year := time.Now().Year()  // December edge case!
   ✓ Handle Dec→Jan transition explicitly
18 / 24

Slide 18: Field Dependencies

┌─────────────────────────────────────────────────────┐
│  POS Entry Mode (DE22) determines required fields   │
│                                                     │
│  DE22 = "051" (Chip)                               │
│    → DE55 (EMV data) REQUIRED                      │
│    → DE35 (Track 2) OPTIONAL                       │
│                                                     │
│  DE22 = "021" (Magnetic Stripe)                    │
│    → DE35 (Track 2) REQUIRED                       │
│    → DE55 ABSENT                                   │
│                                                     │
│  DE22 = "010" (Manual/Keyed)                       │
│    → DE2, DE14 from keyboard                       │
│    → No DE35, no DE55                              │
└─────────────────────────────────────────────────────┘
19 / 24

Slide 19: Quick Reference Card

┌──────────────────────────────────────────────────────┐
│  FIELD  │ TYPE        │ KEY POINTS                  │
├─────────┼─────────────┼─────────────────────────────┤
│  DE2    │ LLVAR n..19 │ Luhn, mask in logs          │
│  DE3    │ Fixed n 6   │ TxnType+FromAcct+ToAcct     │
│  DE4    │ Fixed n 12  │ Pair with DE49 for decimals │
│  DE7    │ Fixed n 10  │ MMDDhhmmss UTC, no year     │
│  DE11   │ Fixed n 6   │ STAN, unique per term/day   │
│  DE12   │ Fixed n 6   │ hhmmss local time           │
│  DE13   │ Fixed n 4   │ MMDD local date             │
│  DE35   │ LLVAR z..37 │ PAN=YYMMSSC+disc            │
│  DE37   │ Fixed an 12 │ RRN, on receipts            │
│  DE39   │ Fixed an 2  │ "00"=approved               │
│  DE49   │ Fixed n 3   │ ISO 4217 currency           │
│  DE54   │ LLLVAR      │ 20-byte balance records     │
└──────────────────────────────────────────────────────┘
20 / 24

Slide 20: Key Takeaways

  1. PAN is not a number - Keep as string, validate with Luhn
  2. Amounts need currency - DE4 is meaningless without DE49
  3. Dates have no year - Handle Dec 31 → Jan 1 carefully
  4. Track 2 is structured - PAN + Separator + Expiry + ServiceCode + Data
  5. Response "00" is special - Only code that means approved
  6. STAN vs RRN - STAN for matching, RRN for reference
  7. DE22 drives validation - Entry mode determines required fields

When debugging: Read fields like a story - who, what, how much, when, and what happened.

21 / 24

Slide 21: Integrity Rules for Field Pairs

IF DE2 + DE35 present -> PAN cores should match
IF DE4 present -> DE49 required for exact value
IF DE22 starts with 0 -> manual mode -> expect terminal-entered DE2/DE14
IF DE22 = 051 -> EMV data (DE55) should be present
IF DE22 = 021 -> Track2 (DE35) should be present
IF DE39 != "00" -> do not map directly to customer messaging

Goal: Parse correctness + semantic correctness = protocol correctness.

22 / 24

Slide 22: Progressive Validation Ladder

1) Field syntax stage

  • LLVAR length prefix validation
  • fixed-length shape checks

2) Dependency stage

  • Required-field matrix by DE22/MTI
  • STAN/Date correlation

3) Coherence stage

  • Cross-field consistency (PAN duplicates, timestamp alignment)
  • Currency-aware amount interpretation

4) Action stage

  • Response decision
  • Error typing (FIELD_INVALID, DEPENDENCY_MISSING, SEMANTIC_MISMATCH)

This ordering prevents one bad field from corrupting every downstream decision.

23 / 24

Quick Reference Card

High-value checks:
- PAN as string, never numeric
- DE4 + DE49 for value
- DE22 dictates DE35/DE55 expectations
- DE11/DE12/DE13 correlation key
- DE54 length must be multiple of 20 bytes
24 / 24
Use arrow keys or click edges to navigate. Press H to toggle help, F for fullscreen.