Virtual Card Numbers Case: Trade-offs, Payment Validation Rules, Debugging

Read the full interview experience this question came from →

Quick Overview

A three-stage case interview on virtual credit card numbers. It covers the benefits and problems of virtual cards for cardholders and the issuer, a hand walkthrough that validates payments against digit-by-digit card number rules, and debugging messy validator code for inverted boolean operators and boundary errors.

Virtual Card Numbers Case: Trade-offs, Payment Validation Rules, Debugging

Company: Capital One

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

A credit card issuer lets cardholders create virtual card numbers. A virtual card number is linked to the cardholder's existing credit account and can be used in place of the physical card number, mostly for online purchases. This case interview runs in three stages: a discussion of the feature, a hand walkthrough that applies the issuer's card number rules to a list of payments, and a debugging exercise on code that implements the same rules. In the interview, the interviewer supplies the rules, the payments and the code. The ones below are illustrative stand-ins, so that the whole exercise can be practiced. What is assessed is the same either way: a structured discussion, checking one condition at a time without skipping any, and finding logic and boundary bugs in messy code. ### Clarifying Questions - Is a virtual number meant for online purchases only, or also for in-store and mobile-wallet payments? - Can a cardholder hold many virtual numbers at once, each locked to a different merchant? - In the walkthrough, should each invalid payment name every rule it breaks, or only the first one found? - Are payments evaluated strictly in the order listed? ### Part 1 — Benefits and problems Discuss what virtual card numbers offer, and what problems they create, for the cardholder and for the issuer. ```hint Two audiences, one lifecycle Keep the cardholder and the issuer separate, and follow a virtual number through its life: creation, everyday purchases, refunds and recurring charges, and closure. ``` #### What This Part Should Cover - Benefits and problems for the cardholder, including what breaks in everyday use - Benefits and costs for the issuer, including fraud, engineering and support load - A visible structure that the discussion follows, rather than loose points ### Part 2 — Validate the payments by hand A virtual card number has 16 digits, numbered 1 to 16 from the left. Each group of digits has a meaning and a rule: | Digits | Meaning | The rule passes when | |---|---|---| | 1 | Card type: `1` is single-use, `2` is multi-use | the digit is `1` or `2` | | 2–5 | Expiry as `MMYY`, in the year `20YY` | `MM` is from `01` to `12`, and the payment date is on or before the last day of that month | | 6–8 | Per-payment limit in whole dollars | the payment amount is less than or equal to the limit | | 9–15 | Merchant ID the number is locked to | it equals the payment's merchant ID | | 16 | Check digit | it equals the sum of digits 1–15, modulo 10 | Additional rules: - The card number must be exactly 16 characters long, all digits. - A payment is valid only if every rule passes. - Payments are evaluated in the order listed. A single-use number can have at most one valid payment. A payment that fails any rule does not use up the number. The payments (amounts in dollars): ```text # card_number merchant_id amount date 1 1062725044172034 4417203 250.01 2026-11-03 2 1062725044172034 4417203 249.99 2026-11-03 3 1062725044172034 4417203 10.00 2026-11-04 4 2122610090015504 9001550 100.00 2026-12-31 5 2122610090015504 9001551 20.00 2026-12-01 6 2122610090015504 9001550 35.00 2027-01-02 7 2122610090015505 9001550 5.00 2026-10-15 8 3122610090015505 9001550 5.00 2026-10-15 ``` For each payment, say whether it is valid and, if it is not, which rule it breaks. Talk through the reasoning as you would aloud. ```hint Same order, every time Fix an order for the checks, apply all of them to every payment, and keep track of which single-use numbers already have a valid payment. ``` #### What This Part Should Cover - One fixed sequence of checks applied to every payment, with nothing skipped - Careful treatment of the inclusive boundaries in the rules - State that carries over from one payment to the next ### Part 3 — Debug the validator The Java method below is meant to implement the Part 2 rules. The caller passes the payment date as `year`, `month` and `day`, and `used` holds the single-use numbers that already have a valid payment. The code is messy on purpose. Find the bugs, explain how each one affects the Part 2 payments, and fix them. ```java import java.util.Set; public class VirtualCardValidator { public static boolean isValid(String card, String merchantId, double amount, int year, int month, int day, Set<String> used) { if (card == null || card.length() != 16) return false; for (int i = 0; i < card.length(); i++) { char c = card.charAt(i); if (c < '0' || c > '9') return false; } char type = card.charAt(0); if (type != '1' || type != '2') return false; if (type == '1') { if (used.contains(card)) return false; used.add(card); } int expMonth = Integer.parseInt(card.substring(1, 3)); int expYear = 2000 + Integer.parseInt(card.substring(3, 5)); if (expMonth < 1 && expMonth > 12) return false; if (year > expYear || month > expMonth) return false; int limit = Integer.parseInt(card.substring(5, 8)); if (amount >= limit) return false; if (!card.substring(8, 15).equals(merchantId)) return false; int sum = 0; for (int i = 0; i < 15; i++) { sum += card.charAt(i) - '0'; } return sum % 10 == card.charAt(15) - '0'; } } ``` ```hint Can this condition ever be true? For every `if`, work out which inputs make it true, and compare that with the inputs its rule is supposed to reject. Then rerun the Part 2 payments through the code. ``` #### What This Part Should Cover - Reading each condition against the rule it implements - Spotting inverted boolean operators, misplaced side effects and boundary comparisons - Using the Part 2 payments as tests, and noticing which bugs they do not catch ### What a Strong Answer Covers - A framework stated up front and applied consistently in all three stages - Precision on boundaries and on state that carries across payments - Minimal, explained fixes, kept separate from style improvements - New test cases that would catch each bug - Clear communication while working through messy material under time pressure ### Follow-up Questions - How would you restructure the validator so that it reports every failing rule instead of a single boolean? - In a real authorization system, where should the "already used" state for single-use numbers live, and what happens if two payments on the same single-use number arrive at the same moment? - The method takes money as a `double`. What can go wrong, and what would you use instead? - How would a subscription work on a merchant-locked virtual number, and how would the rules need to change?

Overview: A three-stage case interview on virtual credit card numbers. It covers the benefits and problems of virtual cards for cardholders and the issuer, a hand walkthrough that validates payments against digit-by-digit card number rules, and debugging messy validator code for inverted boolean operators and boundary errors.

Read the full Capital One Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Capital One
Capital One logo
Capital One
Sep 22, 2026
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
0
0

A credit card issuer lets cardholders create virtual card numbers. A virtual card number is linked to the cardholder's existing credit account and can be used in place of the physical card number, mostly for online purchases. This case interview runs in three stages: a discussion of the feature, a hand walkthrough that applies the issuer's card number rules to a list of payments, and a debugging exercise on code that implements the same rules.

In the interview, the interviewer supplies the rules, the payments and the code. The ones below are illustrative stand-ins, so that the whole exercise can be practiced. What is assessed is the same either way: a structured discussion, checking one condition at a time without skipping any, and finding logic and boundary bugs in messy code.

Clarifying Questions Guidance

  • Is a virtual number meant for online purchases only, or also for in-store and mobile-wallet payments?
  • Can a cardholder hold many virtual numbers at once, each locked to a different merchant?
  • In the walkthrough, should each invalid payment name every rule it breaks, or only the first one found?
  • Are payments evaluated strictly in the order listed?

Part 1 — Benefits and problems

Discuss what virtual card numbers offer, and what problems they create, for the cardholder and for the issuer.

What This Part Should Cover Guidance

  • Benefits and problems for the cardholder, including what breaks in everyday use
  • Benefits and costs for the issuer, including fraud, engineering and support load
  • A visible structure that the discussion follows, rather than loose points

Part 2 — Validate the payments by hand

A virtual card number has 16 digits, numbered 1 to 16 from the left. Each group of digits has a meaning and a rule:

DigitsMeaningThe rule passes when
1Card type: 1 is single-use, 2 is multi-usethe digit is 1 or 2
2–5Expiry as MMYY, in the year 20YYMM is from 01 to 12, and the payment date is on or before the last day of that month
6–8Per-payment limit in whole dollarsthe payment amount is less than or equal to the limit
9–15Merchant ID the number is locked toit equals the payment's merchant ID
16Check digitit equals the sum of digits 1–15, modulo 10

Additional rules:

  • The card number must be exactly 16 characters long, all digits.
  • A payment is valid only if every rule passes.
  • Payments are evaluated in the order listed. A single-use number can have at most one valid payment. A payment that fails any rule does not use up the number.

The payments (amounts in dollars):

#  card_number        merchant_id  amount  date
1  1062725044172034   4417203      250.01  2026-11-03
2  1062725044172034   4417203      249.99  2026-11-03
3  1062725044172034   4417203       10.00  2026-11-04
4  2122610090015504   9001550      100.00  2026-12-31
5  2122610090015504   9001551       20.00  2026-12-01
6  2122610090015504   9001550       35.00  2027-01-02
7  2122610090015505   9001550        5.00  2026-10-15
8  3122610090015505   9001550        5.00  2026-10-15

For each payment, say whether it is valid and, if it is not, which rule it breaks. Talk through the reasoning as you would aloud.

What This Part Should Cover Guidance

  • One fixed sequence of checks applied to every payment, with nothing skipped
  • Careful treatment of the inclusive boundaries in the rules
  • State that carries over from one payment to the next

Part 3 — Debug the validator

The Java method below is meant to implement the Part 2 rules. The caller passes the payment date as year, month and day, and used holds the single-use numbers that already have a valid payment. The code is messy on purpose. Find the bugs, explain how each one affects the Part 2 payments, and fix them.

import java.util.Set;

public class VirtualCardValidator {
    public static boolean isValid(String card, String merchantId, double amount,
                                  int year, int month, int day, Set<String> used) {
        if (card == null || card.length() != 16) return false;
        for (int i = 0; i < card.length(); i++) {
            char c = card.charAt(i);
            if (c < '0' || c > '9') return false;
        }

        char type = card.charAt(0);
        if (type != '1' || type != '2') return false;

        if (type == '1') {
            if (used.contains(card)) return false;
            used.add(card);
        }

        int expMonth = Integer.parseInt(card.substring(1, 3));
        int expYear = 2000 + Integer.parseInt(card.substring(3, 5));
        if (expMonth < 1 && expMonth > 12) return false;
        if (year > expYear || month > expMonth) return false;

        int limit = Integer.parseInt(card.substring(5, 8));
        if (amount >= limit) return false;

        if (!card.substring(8, 15).equals(merchantId)) return false;

        int sum = 0;
        for (int i = 0; i < 15; i++) {
            sum += card.charAt(i) - '0';
        }
        return sum % 10 == card.charAt(15) - '0';
    }
}

What This Part Should Cover Guidance

  • Reading each condition against the rule it implements
  • Spotting inverted boolean operators, misplaced side effects and boundary comparisons
  • Using the Part 2 payments as tests, and noticing which bugs they do not catch

What a Strong Answer Covers Guidance

  • A framework stated up front and applied consistently in all three stages
  • Precision on boundaries and on state that carries across payments
  • Minimal, explained fixes, kept separate from style improvements
  • New test cases that would catch each bug
  • Clear communication while working through messy material under time pressure

Follow-up Questions Guidance

  • How would you restructure the validator so that it reports every failing rule instead of a single boolean?
  • In a real authorization system, where should the "already used" state for single-use numbers live, and what happens if two payments on the same single-use number arrive at the same moment?
  • The method takes money as a double . What can go wrong, and what would you use instead?
  • How would a subscription work on a merchant-locked virtual number, and how would the rules need to change?
Loading comments...