Katkat

Analyze and Debug Virtual-Card Payment Rules

Capital OneSoftware Engineer · Onsite

Last updated

Implement and debug a rule-validation engine for virtual card authorization that accurately parses currency strings into integer cents and evaluates spending boundaries and merchant restrictions.

Virtual-Card Authorization Engine

Virtual card numbers (VCNs) provide enhanced security by allowing users to generate temporary, merchant-locked, or budget-constrained card credentials linked to their primary account. When an authorization request arrives, the engine must decide whether to approve or decline the charge based on configured policy rules.

Implement a function evaluate_transaction that validates an incoming transaction against a virtual card's configuration rules and updates the card's state if approved.

Requirements

  1. Amount Parsing & Precision: Transaction amounts are provided as string formatted currency (e.g., "$124.50" or "25.00"). You must parse this into an exact integer number of cents without floating-point inaccuracies.
  2. Card Status: The card must be active (is_active == True) and not expired. The transaction timestamp must satisfy valid_from <= timestamp <= expires_at (epoch seconds).
  3. Single-Use Restriction: If single_use == True, the card can only approve a transaction if transactions_approved == 0.
  4. Merchant Lock: If merchant_lock is non-null, the transaction's merchant_id must match merchant_lock exactly.
  5. Spending Limits:
    • max_per_transaction_cents: The charge amount cannot strictly exceed this limit (inclusive boundary: amount <= max_per_transaction_cents).
    • remaining_budget_cents: The charge amount cannot exceed the remaining budget.
  6. State Update: If the transaction is approved, return (True, "APPROVED") and deduct the amount from remaining_budget_cents and increment transactions_approved by 1. If declined, return (False, REASON) where REASON corresponds to the first violated rule in the order listed above.

Function Signature

def evaluate_transaction(card: dict, txn: dict) -> tuple[bool, str]:
    pass

Reason Codes

  • "INVALID_AMOUNT"
  • "CARD_INACTIVE_OR_EXPIRED"
  • "SINGLE_USE_EXCEEDED"
  • "MERCHANT_MISMATCH"
  • "TRANSACTION_LIMIT_EXCEEDED"
  • "INSUFFICIENT_BUDGET"
  • "APPROVED"

Example

card = {
    "is_active": True,
    "valid_from": 1700000000,
    "expires_at": 1700003600,
    "single_use": False,
    "merchant_lock": "M_AMZN_01",
    "max_per_transaction_cents": 10000, # $100.00
    "remaining_budget_cents": 15000,     # $150.00
    "transactions_approved": 0
}

txn1 = {
    "amount": "$99.99",
    "timestamp": 1700001000,
    "merchant_id": "M_AMZN_01"
}

approved, reason = evaluate_transaction(card, txn1)
# approved -> True, reason -> "APPROVED"
# card["remaining_budget_cents"] is now 5001
# card["transactions_approved"] is now 1

txn2 = {
    "amount": "$55.00",
    "timestamp": 1700002000,
    "merchant_id": "M_AMZN_01"
}

approved, reason = evaluate_transaction(card, txn2)
# approved -> False, reason -> "INSUFFICIENT_BUDGET"

Hints

Hint 1

Avoid converting the currency string to a float before converting to cents; split the string by the decimal point or use the `decimal` module to prevent rounding errors like 0.1 + 0.2 != 0.3.

Hint 2

Pay close attention to inclusive boundary conditions (<= versus <) for validity timestamps and per-transaction limits.

Hint 3

Ensure you check conditions in the strict priority order specified and only mutate card state after all checks pass.

Follow-up questions

  • How would you handle concurrent authorization requests on the same virtual card in a distributed banking service?
  • If currency conversions (e.g., foreign transaction fees or EUR to USD conversion) were introduced, where should the exchange rate calculation and rounding occur?
  • How would you extend this logic to support recurring subscription limits (e.g., maximum $50 per calendar month)?