Katkat

Reason About and Validate Virtual Card Transactions

Capital OneSoftware Engineer · Onsite

Last updated

Implement a policy validation engine for encoded virtual card numbers against incoming transactions, handling rule parsing, Luhn checksum verification, and structured status reporting.

Problem Statement

Virtual card numbers (VCNs) allow customers to generate temporary card numbers with built-in spending controls. You are tasked with implementing a core transaction authorization rule engine that validates an incoming transaction against policy flags encoded directly into a 16-digit virtual card number.

Card Structure (1-based indices)

  • Digits 1–4: Issuer prefix. Must be "4147" or "5200". Any other prefix is considered an unrecognized card format.
  • Digit 5 (Usage Rule):
    • '1': Single-use card (only valid if is_first_use is True).
    • '2': Multi-use card (valid regardless of is_first_use).
  • Digit 6 (Channel Rule):
    • '0': Omnichannel (allowed for all transaction channels).
    • '1': Online-only (channel must be "ONLINE").
    • '2': In-store only (channel must be "IN_STORE").
  • Digit 7 (Merchant Category Code / MCC Restriction):
    • '0': Unrestricted (all MCCs allowed).
    • '1': Travel & Dining (MCC in range [3000, 3999] or [5812, 5814], inclusive).
    • '2': Groceries & Retail (MCC in range [5300, 5499], inclusive).
  • Digits 8–11: Spending limit per transaction in whole USD (e.g., "0050" means $50.00 = 5000 cents).
  • Digits 12–15: Random card identifier digits.
  • Digit 16: Check digit using the standard Luhn algorithm.

Validation & Evaluation Order

Evaluate the transaction in the following exact order. Return upon the first failure encountered:

  1. Card Validity: The PAN must be exactly 16 decimal digits, start with a valid prefix, have valid policy codes (digit 5 in '1'..'2', digit 6 in '0'..'2', digit 7 in '0'..'2'), and satisfy the Luhn algorithm. If any condition fails, return ("INVALID", "MALFORMED_CARD").
  2. Channel Policy: If the transaction channel does not match digit 6, return ("DECLINED", "CHANNEL_NOT_ALLOWED").
  3. Usage Policy: If digit 5 is '1' and is_first_use is False, return ("DECLINED", "CARD_EXHAUSTED").
  4. Category Policy: If digit 7 restricts MCC and the transaction mcc is outside the permitted ranges, return ("DECLINED", "CATEGORY_NOT_ALLOWED").
  5. Amount Limit: If amount_cents > limit_dollars * 100, return ("DECLINED", "EXCEEDS_LIMIT").
  6. Approval: If all checks pass, return ("APPROVED", "APPROVAL").

Function Signature

def validate_virtual_card_transaction(
    pan: str,
    amount_cents: int,
    channel: str,
    mcc: int,
    is_first_use: bool
) -> tuple[str, str]:
    pass

Examples

Example 1

# Digit breakdown:
# Prefix: 4147, Usage: 1 (single-use), Channel: 1 (online), Category: 0 (all), Limit: 0100 ($100), Random: 123, Check: 6
pan = "4147110010012346"  # Assume valid Luhn check
status, reason = validate_virtual_card_transaction(
    pan=pan,
    amount_cents=5000,
    channel="ONLINE",
    mcc=5411,
    is_first_use=True
)
# Output: ("APPROVED", "APPROVAL")

Example 2

# Same card, but attempted in-store
status, reason = validate_virtual_card_transaction(
    pan="4147110010012346",
    amount_cents=5000,
    channel="IN_STORE",
    mcc=5411,
    is_first_use=True
)
# Output: ("DECLINED", "CHANNEL_NOT_ALLOWED")

Constraints

  • pan is a string of arbitrary length and characters.
  • amount_cents is an integer, 1 <= amount_cents <= 10_000_000.
  • channel is a string, typically "ONLINE" or "IN_STORE".
  • mcc is an integer, 1000 <= mcc <= 9999.
  • is_first_use is a boolean.

Hints

Hint 1

Verify string format and compute the Luhn check before attempting to slice rule parameters to prevent indexing errors or invalid state evaluation.

Hint 2

Be mindful of 1-based index descriptions versus 0-based slicing in code: digits 8–11 correspond to python slice `pan[7:11]`.

Hint 3

Implement the policy checks as an early-exit pipeline following the exact sequence defined in the requirements.

Follow-up questions

  • How would you design the system to handle cumulative budget limits across multiple transactions instead of per-transaction limits?
  • If business stakeholders frequently update MCC groupings or add new restriction categories, how would you refactor the rule engine to avoid hardcoding ranges?
  • How would you protect against replay attacks and ensure concurrency safety if multiple authorizations for a single-use card arrive simultaneously?