# Field Validation Sequence - Quick Reference

## Overview

Validates and adjusts ISO8583 response fields before sending to devices.

## Enable/Disable

```elixir
# In channel_context:
channel_context = %{
  enable_field_validation: true  # default: true
}
```

## How to Add a New Field Validation

### 1. Add to Sequence (line ~92)

```elixir
defp get_default_validation_sequence do
  [
    # Existing...
    %{field: 39, action: :validate_response_code, priority: 1},
    
    # ADD YOUR RULE:
    %{field: XX, action: :validate_your_field, priority: N},
  ]
  |> Enum.sort_by(& &1.priority)
end
```

### 2. Add Action Handler (line ~113)

```elixir
defp process_field_validation_rule(%ISOMsg{} = response, field_rule, payload, channel_context) do
  # ...
  case action do
    # Existing...
    :validate_response_code -> validate_and_adjust_response_code(...)
    
    # ADD YOUR HANDLER:
    :validate_your_field -> validate_and_adjust_your_field(response, field_num, payload, channel_context)
    
    _ -> response
  end
end
```

### 3. Implement Validation Function

```elixir
defp validate_and_adjust_your_field(%ISOMsg{} = response, field_num, _payload, _channel_context) do
  case get_field_safe(response, field_num, nil) do
    nil ->
      # Field is missing
      # Option A: SET the field
      Logger.warning("Field #{field_num} missing, setting default")
      ISOMsg.set(response, field_num, "default_value")
      
      # Option B: Leave it missing
      # response
    
    "" ->
      # Field is empty
      # Option A: SET with value
      ISOMsg.set(response, field_num, "some_value")
      
      # Option B: UNSET the field
      ISOMsg.unset(response, field_num)
    
    value ->
      # Field exists with value
      if valid_format?(value) do
        # Valid, keep it
        response
      else
        # Invalid format
        # Option A: SET with corrected value
        ISOMsg.set(response, field_num, correct_format(value))
        
        # Option B: UNSET the field
        ISOMsg.unset(response, field_num)
      end
  end
end
```

## Common Patterns

### Pattern 1: Set Missing Field

```elixir
case get_field_safe(response, field_num, nil) do
  nil -> ISOMsg.set(response, field_num, "DEFAULT")
  value -> response
end
```

### Pattern 2: Remove Unwanted Field

```elixir
case get_field_safe(response, field_num, nil) do
  nil -> response  # Already absent
  _value -> ISOMsg.unset(response, field_num)
end
```

### Pattern 3: Conditional Set/Unset

```elixir
response_code = get_field_safe(response, 39, "96")

if response_code == "00" do
  # Approved: ensure field exists
  case get_field_safe(response, field_num, nil) do
    nil -> ISOMsg.set(response, field_num, "VALUE")
    _value -> response
  end
else
  # Declined: remove field if present
  case get_field_safe(response, field_num, nil) do
    nil -> response
    _value -> ISOMsg.unset(response, field_num)
  end
end
```

### Pattern 4: Validate and Correct Format

```elixir
case get_field_safe(response, field_num, nil) do
  nil -> response
  value ->
    if valid_format?(value) do
      response
    else
      corrected = fix_format(value)
      ISOMsg.set(response, field_num, corrected)
    end
end
```

## Available Operations

### Get Field Value (Safe)
```elixir
value = get_field_safe(response, field_num, default_value)
```

### Set Field
```elixir
response = ISOMsg.set(response, field_num, "new_value")
```

### Unset Field
```elixir
response = ISOMsg.unset(response, field_num)
```

### Check If Field Exists
```elixir
case ISOMsg.get(response, field_num) do
  {:ok, value} -> # Field exists
  {:error, _} -> # Field missing
  nil -> # Field missing
end
```

## Testing Template

```elixir
test "validates and adjusts field XX" do
  # Setup
  response = create_test_response(%{
    mti: "0210",
    # field XX not set
  })
  
  payload = %{upstream_response: response, channel_context: %{}}
  
  # Execute
  adjusted = validate_and_adjust_your_field(response, XX, payload, %{})
  
  # Verify - field was SET
  {:ok, value} = ISOMsg.get(adjusted, XX)
  assert value == "expected_value"
  
  # OR verify - field was UNSET
  result = ISOMsg.get(adjusted, XX)
  assert result == {:error, :field_not_found}
end
```

## Custom Validation Sequence Per Channel

```elixir
channel_context = %{
  enable_field_validation: true,
  field_validation_sequence: [
    %{field: 39, action: :validate_response_code, priority: 1},
    %{field: 4, action: :validate_amount, priority: 2},
    # Custom rules for this channel
  ]
}
```

## File Location

**Code**: `lib/da_product_app/events/listeners/response_preparation_listener.ex`
- Lines 88-253: Field validation sequence implementation
- Line 92: Default validation sequence
- Line 113: Action handlers
- Line 133+: Individual validation functions

## Logging

```bash
# Watch validation activity
tail -f logs/dev.log | grep "Validating field"

# See adjustments made
tail -f logs/dev.log | grep -E "(setting|removing|replacing|adjusting)"
```

## Security Examples

### Remove Sensitive Fields

```elixir
# PIN block should NEVER be in response
defp validate_and_adjust_pin_block(%ISOMsg{} = response, field_num, _payload, _channel_context) do
  case get_field_safe(response, field_num, nil) do
    nil -> response
    _value ->
      Logger.error("PIN block in response, removing (security)")
      ISOMsg.unset(response, field_num)
  end
end
```

### Mask PAN

```elixir
defp validate_and_adjust_pan(%ISOMsg{} = response, field_num, _payload, channel_context) do
  if Map.get(channel_context, :mask_pan_in_response, false) do
    case get_field_safe(response, field_num, nil) do
      nil -> response
      pan -> ISOMsg.set(response, field_num, mask_pan(pan))
    end
  else
    response
  end
end
```

## Priority Guidelines

| Priority | Field Type | Examples |
|----------|-----------|----------|
| 1-10 | Critical | Response code (39), Amount (4) |
| 11-20 | Security | PAN (2), Auth code (38) |
| 21-50 | Required | Terminal ID (41), Merchant ID (42) |
| 51-100 | Optional | Additional data, custom fields |

## Checklist for New Validation

- [ ] Added to validation sequence with priority
- [ ] Action handler added to case statement
- [ ] Validation function implemented
- [ ] Handles nil/empty/invalid cases
- [ ] Uses SET or UNSET appropriately
- [ ] Logs warnings/errors
- [ ] Unit tests written
- [ ] Documentation updated

---

**Full Guide**: `docs/FIELD_VALIDATION_SEQUENCE_GUIDE.md`
