Design Corporate Card System

Hard45 min
1 / 30
understanding6 min read

Problem Statement & Context

Corporate Card System — Problem Statement & Context

Problem Statement & Context

Corporate card platforms (Brex, Ramp, Stripe Issuing) sit between employers and card networks. You issue virtual or physical cards, enforce spend policies in milliseconds at authorization time, stream transactions to finance, and settle against company funding accounts. Unlike expense reports filed after the fact, the critical path is auth-time decisioning with network timeouts under 2 seconds and issuer SLAs targeting <100ms internal processing.

Personas

  • Cardholder: swipe, see declines with reasons, attach memos post-auth
  • Manager / Admin: set per-card limits, MCC blocks, vendor locks
  • Finance: fund accounts, reconcile GL, chase out-of-policy spend

Scale anchors

Assume 42k tenants, 4.8M active cards, 95M authorizations/month, 1.4B settled volume/month. Lunch-hour peak 3.2× daily auth rate. Targets: auth decision p99 <85ms, card create API p99 <400ms, webhook delivery p99 <5s.

Core tension

authorization_id must be idempotent across network retries while card_id spend counters update atomically. You never store PAN—only issuer tokens and auth metadata.

pythonOne Dark Pro
1def evaluate_spend(policy: dict, req: dict) -> dict:
2 if req["mcc"] in policy.get("blocked_mccs", []):
3 return {"approved": False, "code": "MCC_BLOCKED"}
4 if req["amount_cents"] + req["daily_spent"] > policy["daily_limit_cents"]:
5 return {"approved": False, "code": "LIMIT_DAILY"}
6 return {"approved": True, "code": "APPROVED"}
typescriptOne Dark Pro
1interface AuthWebhookPayload {
2 authorizationId: string;
3 cardId: string;
4 amountCents: number;
5 currency: string;
6 mcc: string;
7 decision: "approved" | "declined";
8 declineCode?: string;
9}
10
11export async function postAuthWebhook(
12 tenantId: string,
13 payload: AuthWebhookPayload,
14): Promise<void> {
15 const body = JSON.stringify(payload);
16 const sig = signHmac(body, tenantSecrets.get(tenantId));
17 await fetch(webhookUrl(tenantId), {
18 method: "POST",
19 headers: { "X-Signature": sig, "Idempotency-Key": payload.authorizationId },
20 body,
21 });
22}
javaOne Dark Pro
1public final class AuthorizationService {
2 public AuthDecision decide(AuthRequest req, String idempotencyKey) {
3 return idempotencyStore.computeIfAbsent(idempotencyKey, k -> {
4 PolicySnapshot snap = policyCache.get(req.cardId(), req.policyVersion());
5 AuthDecision d = ruleEngine.evaluate(req, snap);
6 ledger.appendHold(req, d);
7 audit.log(req, d, snap.id());
8 return d;
9 });
10 }
11}

Section 1 focus

For design-corporate-card, Problem Statement & Context should emphasize authorization_id idempotency, policy_version at swipe time, and fail-closed declines—not post-hoc expense reports.

Why interviewers care

Corporate Card System interviews reward crisp scope, explicit trade-offs, and failure stories—not generic microservice diagrams.

Interview checkpoint

Name one failure story for Problem Statement & Context that proves you understand real outages, not happy-path diagrams.

Key Highlights

  • Problem Statement & Context: auth-time policy on card_id
  • Idempotent authorization_id across network retries
  • Issuer token boundary—never persist PAN
  • Phase understanding checkpoint for design-corporate-card
Signal
When discussing Problem Statement & Context, cite policy_version on auth and structured decline_code.
Delivery
Quantify 118 auths/s lunch peak and <85ms internal auth p99 separately from issuer RTT.

Section Rescue Kit

Buzzwords to use:

AuthSnapshot-1DeclineCode-1

Safe statements:

  • "We never store PAN—only issuer tokens and auth metadata."
  • "If policy cache is stale beyond TTL, I fail closed to decline."
Design Corporate Card System - System Design | WinJob | WinJob