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