0) Problem Restatement
A virtual card is a separate card number (with its own expiry and CVV) linked to a customer's real credit card account. Customers create them for online shopping: e.g., single-use cards, cards locked to one merchant (only Netflix can charge it), or cards with a spending limit. If a merchant is hacked, only the virtual number leaks, and the customer can close it without replacing their real card.
Capital One asked two parts: (1) product reasoning, meaning benefits and drawbacks for users and the bank, and (2) transaction validation: when a charge arrives with an encoded virtual card ID, decide approve or decline in real time. A related question added geography-aware rules.
1) Product Reasoning (say this briefly)
- For users: safer online shopping, easy cancellation, spending control, and fewer real-card reissues.
- Drawbacks for users: extra steps, and problems with merchants that need the physical card at pickup (hotels, rentals) or recurring charges tied to a single-use card.
- For the bank: lower fraud losses and reissue costs, more engagement, and data on merchant subscriptions.
- Costs for the bank: more complex authorization, more card numbers to manage, and support calls when a locked card is declined.
2) Requirements
- Create virtual cards with rules: single-use / multi-use, merchant lock (set on first use or chosen up front), spending limit (per transaction or monthly), expiry, and allowed countries.
- Pause, resume or close a card.
- Authorize each transaction in real time (under ~100 ms of our budget inside the card network's time limit).
- Every decision is logged and explainable.
3) Architecture
Architecture Diagram
flowchart LR
NET["Card network - auth request"] --> AUTH["Authorization Service"]
AUTH --> VAULT["Token vault - decode virtual PAN"]
AUTH --> VC[("Virtual cards + rules - cached")]
AUTH --> ACC["Core account - available credit"]
AUTH --> FR["Fraud scoring"]
AUTH -->|"approve / decline + reason"| NET
AUTH --> LOG[("Decision log")]
APP["Customer app"] --> MGMT["Card Management API"]
MGMT --> VC
MGMT --> VAULT- Token vault: maps the virtual card number (PAN) to
virtual_card_idand the real account. Card numbers live only here (a small PCI scope). The encoded ID in the request is decoded or looked up here. - Virtual card rules are cached in memory in the authorization service for speed, and invalidated on change.
- Core account service: the real available credit, with holds placed on approval.
4) Validation Rules (in order, fail fast)
- Decode and look up the card. Unknown → decline "invalid card".
- Status: closed or paused → decline.
- Expiry and CVV match.
- Single-use: already used → decline.
- Merchant lock: if locked, the merchant ID must match. If "lock on first use" and not yet locked, lock it to this merchant atomically (so two first charges can't both lock).
- Limits: amount ≤ per-transaction limit, and monthly spent + amount ≤ monthly limit.
- Geography: the merchant's country is in the allowed list (and optionally matches the customer's recent location or travel notice).
- Real account: available credit ≥ amount → place a hold.
- Fraud score below a threshold, or step-up (send the customer a confirmation).
- Approve: record the decision, update counters (spent, used flag), and return the approval code.
Each decline returns a specific reason code for the network and the customer's app ("Declined: card locked to another merchant").
5) Concurrency and Reliability
- Two charges at the same time on a single-use card: use a conditional update (
SET used = true WHERE id = ? AND used = false). Only one succeeds. - Monthly spend counters are updated atomically alongside the hold.
- Idempotency: networks retry authorization messages, so decisions are keyed by the network's transaction ID, and a retry returns the same answer.
- High availability: multi-region active-active authorization with replicated card rules. If the core account system is slow, card networks allow stand-in decisions with conservative limits.
6) Trade-offs & Alternatives
| Decision | Choice | Why | Alternative |
|---|---|---|---|
| Card data | Separate vault (tokenization) | Small PCI scope, safer | Store PANs everywhere: huge compliance risk |
| Rule checks | In-memory cached rules, fail fast | Meets network latency | DB queries per check: slow |
| First-use lock | Atomic conditional update | No double locks | Read-then-write: race |
| Retries | Idempotent by network transaction ID | Same answer on retry | New decision each time: double holds |
7) Wrap-Up
Virtual cards give customers safer, controllable card numbers linked to one real account, with lower fraud costs for the bank. Authorize each transaction by decoding the card through a token vault, then checking cached rules in a fail-fast order: status, expiry, single-use, merchant lock, limits, geography, available credit and fraud. Use atomic conditional updates for single-use and first-use locks, idempotency by network transaction ID, and explainable decline reasons.