Stripe OA Questions: 4 Practice Problems with Solutions

Practice Stripe-style OA questions on parsing, state updates, billing, and routing with four original problems, Python solutions, and edge-case tests.

EmmaEmmaArticle Editing & Website Operations
Oct 29, 2026StripeSoftware Engineering Intern and New Graduate
Editorial illustration reused from our Stripe software engineering interview guide

For Stripe OA questions, practice implementing a precise set of rules, then extending your solution without breaking earlier behavior. A 2025–26 university-recruiting candidate report describes command parsing and state updates in one long problem. Use that report to guide your practice, but check your own invitation for the assigned format.

Start with one of the four exercises below. Each has a complete specification, sample input and output, hints, a Python solution, and tests worth adding yourself.

These are original SkillCopilotAI practice problems, not Stripe test questions. Suggested times are study budgets. This independent guide is not endorsed by Stripe or HackerRank.

Practice questionWhat you will work onSuggested session
1. Workshop booking eventsIncremental requirements, cancellations, duplicate events40–60 minutes
2. Usage billingAggregation, tier boundaries, integer amounts25–35 minutes
3. Worker request routingLoad tracking, tie-breaking, failure recovery30–40 minutes
4. Record validation and joinsCSV, malformed rows, duplicate IDs, deterministic output25–35 minutes

Choose one exercise per session. The Python programs read standard input and print standard output; run them locally as python solution.py < input.txt. For a different permitted language, keep the rules and expected outputs unchanged.

Question 1: Workshop Booking Events

Original practice · incremental implementation

Build a processor for workshop reservations. Commands arrive in input order. Every command starts with an event ID. Its first occurrence consumes that event ID even if the operation is ignored; later commands with the same event ID do nothing.

The first line contains q, followed by exactly q commands:

Command after the event IDRequired behavior
CREATE workshop capacityCreate a workshop. Ignore it if that workshop already exists.
BOOK booking workshopAccept a new booking only if the workshop exists, has a free place, and the booking ID has never been accepted.
CANCEL bookingCancel an active booking and release one place. Unknown or already-cancelled bookings do nothing.
CAPACITY workshop capacityChange capacity if the workshop exists and the new capacity is at least its active booking count. Otherwise ignore it.

A cancelled booking ID cannot be reused. A failed BOOK does not reserve its booking ID, so a later event may try that booking again. A duplicate event is ignored before any operation is considered.

Input syntax is valid: command names and argument counts are correct, capacities are nonnegative integers, and IDs contain 1–32 ASCII letters, digits, underscores, or hyphens. IDs are case-sensitive. There are at most 100,000 commands; each capacity is at most one billion. Invalid business operations, such as booking an unknown workshop, are still possible.

Print one line per workshop in ASCII lexicographic ID order: workshop capacity active_count. Print EMPTY if none were created.

Sample input

10
e1 CREATE art 2
e2 BOOK b1 art
e3 BOOK b2 art
e4 CAPACITY art 1
e5 CANCEL b1
e5 BOOK b3 art
e6 BOOK b3 art
e7 CANCEL b1
e8 BOOK b1 art
e9 CAPACITY art 3

Expected output

art 3 2

The capacity decrease fails because two bookings are active. Cancelling b1 releases a place. The repeated e5 is discarded; e6 then accepts b3. Cancelling b1 again has no effect, and b1 cannot be booked again. The active bookings are b2 and b3.

Build it in stages. First support CREATE and BOOK. Add CANCEL while preserving the original successful-booking behavior. Next implement capacity changes. Finally add event-ID deduplication and rerun the earlier cases. These stages are our practice design, not a claim about how Stripe reveals its questions.

Show a hint for question 1

Separate workshop capacity, active occupancy, booking history, and event history. Deleting a cancelled booking loses the information needed to reject its reuse. Marking every failed booking as used creates the opposite bug: it prevents a permitted retry.

Show the solution and Python program for question 1

Keep two different histories: seen event IDs and accepted booking IDs. The former records every first event; the latter records successful bookings, including cancelled ones. After every command, occupancy must equal the number of active bookings assigned to the workshop.

import sys


def solve(data):
    lines = data.splitlines()
    if not lines:
        return "EMPTY"
    count = int(lines[0])
    workshops, bookings, used, seen = {}, {}, {}, set()
    for line in lines[1:count + 1]:
        event, op, *args = line.split()
        if event in seen:
            continue
        seen.add(event)
        if op == "CREATE":
            name, capacity = args[0], int(args[1])
            if name not in workshops:
                workshops[name], used[name] = capacity, 0
        elif op == "BOOK":
            booking, name = args
            if (booking not in bookings and name in workshops
                    and used[name] < workshops[name]):
                bookings[booking] = [name, True]
                used[name] += 1
        elif op == "CANCEL":
            booking = args[0]
            if booking in bookings and bookings[booking][1]:
                name = bookings[booking][0]
                bookings[booking][1] = False
                used[name] -= 1
        elif op == "CAPACITY":
            name, capacity = args[0], int(args[1])
            if name in workshops and capacity >= used[name]:
                workshops[name] = capacity
    return "\n".join(
        f"{name} {workshops[name]} {used[name]}"
        for name in sorted(workshops)
    ) or "EMPTY"


if __name__ == "__main__":
    print(solve(sys.stdin.read()))

For q commands and w workshops, processing takes expected O(q) time with hash tables, followed by O(w log w) sorting. The stored state and input use O(q + w) space. Python integers cover the stated bounds.

Test to addResult to check
Capacity zero, then BOOKNo booking is accepted.
Failed BOOK, then create workshop, then a new event retries itThe retry can succeed.
One event ID first used for an ignored operationReusing that event ID remains a no-op.
Cancel twice, then try the same booking ID againOccupancy decreases once; the ID remains unavailable.
Reduce capacity to exactly the active countThe change is accepted.

After your attempt, explain why seen and bookings cannot be replaced with one set. If you use an AI assistant during preparation, give it your explanation and ask for a counterexample, then execute that case yourself. Our mock interview practice guide shows how to organize that review. SkillCopilotAI publishes this article and sells interview-assistance software; the practice code here runs independently of that product.

Question 2: Usage Billing

Original practice · integer arithmetic and aggregation

Each input record contains an account ID and a nonnegative number of usage units. Combine all records for an account before applying its monthly allowance or credit.

The first line is n allowance credit. The next n lines contain account units. Every account receives the same allowance in units and credit in cents, once each for the whole batch.

  1. Subtract the allowance from total units, with a floor of zero.
  2. Charge the first 100 remaining units at 5 cents each, the next 900 at 3 cents each, and all further units at 1 cent each. These are progressive bands, not one rate applied to the entire total.
  3. Subtract the credit from the calculated charge, again with a floor of zero.

Print account charge_in_cents in ASCII lexicographic account order. Include accounts with zero usage or zero charge. If n is zero, print EMPTY.

There are at most 100,000 records. Units, allowance, and credit are integers between zero and one billion. Account IDs contain 1–32 ASCII letters, digits, underscores, or hyphens and are case-sensitive. All rows are syntactically valid. Use a sufficiently wide integer type; no rounding or floating point is needed. These invented rates do not describe Stripe pricing.

Sample input

5 50 100
a 100
a 100
b 50
c 1150
c 0

Expected output

a 550
b 0
c 3200

Account a has 200 total units, then 150 billable units: 100 × 5 + 50 × 3 − 100 = 550 cents. Account c has 1,100 billable units: 100 × 5 + 900 × 3 + 100 × 1 − 100 = 3200 cents.

Show a hint for question 2

Try an account represented by two 100-unit records. If you subtract its allowance from each record, you have changed a monthly allowance into a per-record allowance. Aggregate first.

Show the solution and Python program for question 2

For billable usage u, split it into nonoverlapping bands: up to 100, the portion from 101 through 1,000, and the portion above 1,000. Subtract the credit only after adding those charges.

import sys


def solve(data):
    lines = data.splitlines()
    count, allowance, credit = map(int, lines[0].split())
    totals = {}
    for line in lines[1:count + 1]:
        account, raw_units = line.split()
        totals[account] = totals.get(account, 0) + int(raw_units)
    output = []
    for account in sorted(totals):
        units = max(0, totals[account] - allowance)
        cents = (min(units, 100) * 5
                 + min(max(units - 100, 0), 900) * 3
                 + max(units - 1000, 0))
        output.append(f"{account} {max(0, cents - credit)}")
    return "\n".join(output) or "EMPTY"


if __name__ == "__main__":
    print(solve(sys.stdin.read()))

With n records and a distinct accounts, expected time is O(n + a log a) and space is O(n + a) including the stored input. The pricing formula uses constant work per account.

Test total usage at allowance, allowance + 1, allowance + 100, allowance + 101, allowance + 1000, and allowance + 1001. Also check a credit larger than the charge, repeated records, and a total exceeding a 32-bit integer.

Question 3: Worker Request Routing

Original practice · deterministic simulation

Route sessions to workers. Every worker has a capacity, starts online, and initially holds no sessions. On CONNECT session, select the online worker with the fewest active sessions, provided it has spare capacity. Break ties by ASCII lexicographic worker ID, not by capacity or percentage utilization.

The first line contains w q. The next w lines give distinct worker capacity pairs. The final q lines are commands:

  • CONNECT session: leave an already-active session on its current worker. For an inactive session, apply the selection rule; if no worker qualifies, ignore the request.
  • DISCONNECT session: remove an active session. Unknown sessions do nothing.
  • DOWN worker: take a known worker offline and drop all its sessions. Dropped sessions are not reassigned automatically. Repeating DOWN has no additional effect.
  • UP worker: bring a known worker online. It does not restore dropped sessions; calling UP on an online worker changes nothing.

Unknown worker IDs in UP or DOWN are ignored. A dropped or disconnected session ID may connect again. Worker capacities never change.

Print active sessions as session worker in ASCII lexicographic session order, or EMPTY. Assume valid command syntax, case-sensitive IDs containing 1–32 ASCII letters, digits, underscores, or hyphens, at most 100 workers and 20,000 commands, and capacities between zero and 20,000.

Sample input

2 7
a 1
b 2
CONNECT s1
CONNECT s2
CONNECT s3
DOWN b
CONNECT s2
UP b
CONNECT s2

Expected output

s1 a
s2 b

The first CONNECT s2 after the outage fails: worker a is full and b is offline. Bringing b back does not restore s3; the final command explicitly reconnects only s2.

Show a hint for question 3

Track a session's assigned worker so disconnecting it updates the right load. When a worker goes down, remove the corresponding sessions as well as resetting its load; otherwise reconnects may incorrectly look active.

Show the solution and Python program for question 3

Scan the small worker set for each new connection. The (load, worker_id) tuple expresses both the main criterion and the tie-break. For an outage, collect matching session IDs before deleting them so the dictionary is not resized during iteration.

import sys


def solve(data):
    lines = data.splitlines()
    workers_count, commands_count = map(int, lines[0].split())
    capacity, load, online, sessions = {}, {}, {}, {}
    for line in lines[1:workers_count + 1]:
        worker, raw_capacity = line.split()
        capacity[worker] = int(raw_capacity)
        load[worker], online[worker] = 0, True
    start = workers_count + 1
    for line in lines[start:start + commands_count]:
        op, name = line.split()
        if op == "CONNECT":
            if name in sessions:
                continue
            candidates = [w for w in capacity
                          if online[w] and load[w] < capacity[w]]
            if candidates:
                worker = min(candidates, key=lambda w: (load[w], w))
                sessions[name] = worker
                load[worker] += 1
        elif op == "DISCONNECT":
            worker = sessions.pop(name, None)
            if worker is not None:
                load[worker] -= 1
        elif op == "DOWN" and name in capacity:
            online[name] = False
            for session in [s for s, w in sessions.items() if w == name]:
                del sessions[session]
            load[name] = 0
        elif op == "UP" and name in capacity:
            online[name] = True
    return "\n".join(f"{s} {sessions[s]}" for s in sorted(sessions)) or "EMPTY"


if __name__ == "__main__":
    print(solve(sys.stdin.read()))

Let s be the maximum active sessions. A connection scans w workers; an outage scans up to s sessions. Worst-case time is O(q(w + s) + s log s), and memory is O(q + w + s) including input. Under the declared bounds, scans are a reasonable reference implementation. For larger workloads, maintain worker-owned session sets so dropping one worker visits only its sessions; retaining a global session map still helps disconnects.

Test equal loads with unequal capacities, a worker of capacity zero, reconnecting an active session, an unknown disconnect, repeated outages, and a reconnect after all workers are full.

Question 4: Record Validation and Joins

Original practice · CSV parsing and duplicate policy

Join account metadata to payment-like event records and produce totals. The first line contains m n: the number of metadata rows followed by the number of event rows. Every row occupies one physical input line; quoted fields may contain commas and doubled quotes, but never embedded line breaks.

Metadata rows are CSV with exactly two fields: account,label. A valid account ID contains 1–32 ASCII letters, digits, underscores, or hyphens and is case-sensitive. The label must be nonempty. The last valid metadata row for an account wins. Invalid rows do not overwrite an earlier valid record. Finish reading metadata before processing events.

Event rows are CSV with exactly three fields: event,account,amount. An event is valid only when its ID contains 1–32 ASCII letters, digits, underscores, or hyphens, its account exists in the final metadata, and its amount consists of 1–9 ASCII digits. Event IDs are case-sensitive. Zero and leading zeroes are allowed; signs, spaces, and decimal points are not. The amount is an integer number of cents.

The first valid, joinable row for an event ID wins globally, even if another row with that ID names a different account. Invalid rows and unknown-account rows do not consume the event ID. Malformed CSV and rows with the wrong field count are ignored. Do not trim fields or change case.

Print CSV rows account,label,total_cents for every valid account, sorted by ASCII account ID. Include zero totals. Quote output labels when CSV requires it. Print EMPTY if no valid accounts exist. Both counts are at most 100,000; each physical row is at most 256 characters. Use wide integers for totals.

Sample input

3 6
a,"North, Inc"
b,Beta
a,Alpha
e1,a,100
e1,b,900
e2,z,200
e2,b,40
e3,b,-5
e3,b,5

Expected output

a,Alpha,100
b,Beta,45

The final label for a is Alpha. The second e1 cannot move its event to account b. The first e2 does not join an account and the first e3 has an invalid amount, so their later valid rows can contribute.

Show a hint for question 4

Separate parsing, row validation, joining, and duplicate detection. A raw comma split breaks "North, Inc". A Unicode-aware digit check may also accept characters excluded by the ASCII-only amount rule.

Show the solution and Python program for question 4

Use Python's CSV parser and writer for quoting. Validate the parsed fields before recording an event ID as consumed. The order matters: marking an invalid event as seen would suppress a later valid row.

import csv
import io
import re
import sys


def parse_row(line, size):
    try:
        row = next(csv.reader([line], strict=True))
    except (csv.Error, StopIteration):
        return None
    return row if len(row) == size else None


def identifier(value):
    return re.fullmatch(r"[A-Za-z0-9_-]{1,32}", value) is not None


def solve(data):
    lines = data.splitlines()
    count_accounts, count_events = map(int, lines[0].split())
    accounts = {}
    for line in lines[1:count_accounts + 1]:
        row = parse_row(line, 2)
        if row and identifier(row[0]) and row[1]:
            accounts[row[0]] = row[1]
    totals = dict.fromkeys(accounts, 0)
    seen = set()
    start = count_accounts + 1
    for line in lines[start:start + count_events]:
        row = parse_row(line, 3)
        if not row:
            continue
        event, account, amount = row
        if (not identifier(event) or account not in accounts
                or re.fullmatch(r"[0-9]{1,9}", amount) is None):
            continue
        if event in seen:
            continue
        seen.add(event)
        totals[account] += int(amount)
    output = io.StringIO()
    writer = csv.writer(output, lineterminator="\n")
    for account in sorted(accounts):
        writer.writerow([account, accounts[account], totals[account]])
    return output.getvalue().rstrip("\n") or "EMPTY"


if __name__ == "__main__":
    print(solve(sys.stdin.read()))

Let L be total input length, a valid accounts, and e accepted event IDs. Expected time is O(L + a log a). Memory is O(L + a + e) including the stored input and labels. Fixed maximum row length keeps parsing work bounded per row.

Test a quoted comma, a doubled quote, an empty label, a duplicate metadata row, a fourth event field, an unknown account followed by a valid retry, whitespace in an amount, and an amount of ten digits. Include a metadata-only account to verify that zero totals are printed.

What Public Reports Say About the Stripe OA

The candidate account linked at the start concerns India's 2025–26 university-recruiting process. It reports one main problem, a 60-minute limit, and work involving sequential commands, state transitions, and aggregated output. It supports practicing that shape of task; it cannot confirm the current duration, score threshold, or question assigned to a US intern or new graduate.

Stripe's official early-career page describes its programs and notes that internship scheduling follows local academic calendars. The page does not publish a universal OA specification. For your own invitation, check the platform, timer, languages, question count, and permitted resources before choosing a rehearsal format.

HackerRank's candidate introduction explains where to review duration and test structure and recommends its sample test. For a platform-oriented preparation checklist, use our HackerRank OA guide. Later live coding, debugging, and system design are covered separately in our Stripe software engineer interview guide.

How to Handle a Multi-Part Question

Before coding, identify the input contract, persistent state, ordering, and output. In question 1, “event ID is consumed” and “booking ID has been accepted” are different facts. In question 4, an invalid event does not consume its ID. Reusing an implementation without rereading that rule produces the wrong answer.

If all parts are visible, scan the later requirements before choosing your state. If only the current part is available, implement its stated contract cleanly and preserve working tests as new requirements appear. Do not assume an assessment lets you read unopened parts or skip ahead.

When an example fails, trace the smallest input that separates the expected state from your state. For the router, write a session-to-worker map after each command. For billing, calculate one account by hand before debugging the full batch. Before a late rewrite, identify what your current state representation cannot handle. Changing data structures without that diagnosis can cost time without fixing the failing case.

For a self-imposed 60-minute practice session, you might reserve 10 minutes for the contract and sample trace, 35 for implementation and extensions, and 15 for testing and cleanup. This is a study plan, not an official Stripe timer.

Checks Before You Finish

CheckConcrete example from these exercises
Identity policyAn ignored booking event consumes its event ID; a malformed joined event does not.
State after repeated actionsA second cancellation must not release another seat.
Numerical boundaryThe 101st billable unit costs 3 cents, not 5 or 1.
Failure recoveryBringing a worker online does not restore its dropped sessions.
Text formatA comma inside a label must survive CSV input and output.
Final submissionRemove debug output and run the complete program with the exact sample input.

Frequently Asked Questions

Are these real Stripe OA questions?

No. All four are original teaching exercises with invented rules, constraints, and data. They train implementation skills discussed in the cited public candidate report. They are not a prediction of your assigned assessment.

Is the Stripe OA one question or several parts?

The cited historical university-recruiting report describes one main problem. Different descriptions may count a growing problem and its stages differently. Check the question structure in your own invitation rather than assuming one fixed format.

What should interns and new grads practice?

Practice parsing, maps and sets, state transitions, sorting, and boundary tests, then rehearse under your assigned timer. Do not assume interns and new graduates in different regions receive the same assessment. Basic algorithms still matter when implementing or optimizing these tasks.

What is the passing score?

We have not verified a universal Stripe OA passing score. A candidate's reported test count and hiring result do not establish a cutoff. Focus on correct behavior, exact output, and the rules in your own assessment.

Can I use AI or my own IDE?

Use the permitted-resource instructions attached to your assessment. A practice workflow does not establish permission during a scored test, and one assessment's built-in tools do not establish another's rules. For these original exercises, you can code in your own environment and review assistance after your attempt.

About the Author and Practice Validation

Emma handles article editing and website operations at SkillCopilotAI. This article was prepared with AI-assisted research and drafting. It does not document Emma taking a Stripe hiring assessment or a completed specialist human review.

The four Python reference programs were checked against hand-worked samples, boundary cases, and 4,000 seeded randomized cases using separate reference models. In total, 4,018 output comparisons and four command-line sample runs passed. The reference models recomputed booking occupancy, priced units individually, stored sessions by worker, and checked event validity before CSV encoding. These finite tests support the exercises; they are not an exhaustive proof or evidence about Stripe's question pool. Sources and examples were checked October 29, 2026.

Ready to ace your next technical interview?

Practice interview questions, review your explanations, and explore SkillCopilotAI for interview preparation.