← Back to blog Stripe OA Real Exam Questions: 2026 Spring Hiring Question Bank & Pitfall Checklist
Stripe

Stripe OA Real Exam Questions: 2026 Spring Hiring Question Bank & Pitfall Checklist

2026-05-13

Among fintech companies, Stripe's OA style is the most distinctive—no LeetCode Hard, but 15-20 hidden test cases per problem, so "right idea, wrong code" is the most common failure mode. This article aggregates the latest 1point3acres reports from Stripe 2026 spring OA (Q1-Q2), ranked by frequency, with three real problems, complete Python solutions, and the grading system's deduction details. All problems are from real candidate reports between January and April 2026—completely different from older brace expansion / shipping cost problems.

Stripe 2026 Spring OA Overview

Dimension Details
Platform HackerRank for Work
Total time 120 minutes
Problems Typically 1 large problem split into 4 Parts
Difficulty Each Part is LeetCode Easy ~ Medium, escalating
Hidden tests 15-20 total, 5-7 per Part
Pass line 80%+ tests passed

Problem 1: Payment Reconciliation (Highest Frequency)

Part 1: Basic Reconciliation

Statement: Given two data sources bank_transactions and internal_records, each row (transaction_id, amount, timestamp). Return transactions only in internal records and not confirmed by the bank.

def find_unreconciled(bank_transactions, internal_records):
    """
    bank_transactions: List[Tuple[str, int, int]]
    internal_records: List[Tuple[str, int, int]]
    return: List[Tuple[str, int, int]], sorted by timestamp ascending
    """
    bank_ids = {tx[0] for tx in bank_transactions}
    unreconciled = [r for r in internal_records if r[0] not in bank_ids]
    unreconciled.sort(key=lambda x: x[2])
    return unreconciled

Time complexity: O(n + m log m).

Part 2: Fuzzy Match on Amount + Timestamp

Statement: From Part 1's unreconciled list, find pairs where amount matches and timestamps differ ≤ 60s—possibly the same transaction with a typo in the ID.

from collections import defaultdict

def find_fuzzy_matches(unreconciled, bank_transactions, time_tolerance=60):
    bank_by_amount = defaultdict(list)
    for tx in bank_transactions:
        bank_by_amount[tx[1]].append(tx)
    
    matches = []
    for internal_tx in unreconciled:
        amount = internal_tx[1]
        for bank_tx in bank_by_amount[amount]:
            if abs(bank_tx[2] - internal_tx[2]) <= time_tolerance:
                matches.append((internal_tx, bank_tx))
                break
    
    return matches

Stripe deductions:

Part 3: Conflict Resolution

Statement: When multiple internal transactions could match one bank transaction, assign by closest timestamp.

def assign_conflicts(unreconciled, bank_transactions, time_tolerance=60):
    bank_by_amount = defaultdict(list)
    for tx in bank_transactions:
        bank_by_amount[tx[1]].append(tx)
    
    used_bank = set()
    result = []
    unreconciled_sorted = sorted(unreconciled, key=lambda x: x[2])
    
    for internal_tx in unreconciled_sorted:
        amount = internal_tx[1]
        best_match = None
        best_diff = time_tolerance + 1
        for bank_tx in bank_by_amount[amount]:
            if bank_tx[0] in used_bank:
                continue
            diff = abs(bank_tx[2] - internal_tx[2])
            if diff < best_diff:
                best_diff = diff
                best_match = bank_tx
        
        if best_match:
            used_bank.add(best_match[0])
            result.append((internal_tx, best_match))
    
    return result

Part 4: Streaming Reconciliation (State Reset)

The hardest part: process streaming inputs in real time, match each new transaction immediately or hold it, and after 5 minutes unmatched, escalate for manual review.

from collections import deque

class StreamReconciler:
    def __init__(self, time_tolerance=60, escalation_timeout=300):
        self.time_tolerance = time_tolerance
        self.escalation_timeout = escalation_timeout
        self.pending_internal = deque()
        self.pending_bank = deque()
        self.matched = []
        self.escalated = []
    
    def process(self, tx_type, tx):
        if tx_type == "internal":
            self.pending_internal.append(tx)
        else:
            self.pending_bank.append(tx)
        
        self._try_match(tx[2])
        self._escalate_old(tx[2])
    
    def _try_match(self, current_time):
        for internal_tx in list(self.pending_internal):
            for bank_tx in list(self.pending_bank):
                if (internal_tx[1] == bank_tx[1] and 
                    abs(internal_tx[2] - bank_tx[2]) <= self.time_tolerance):
                    self.matched.append((internal_tx, bank_tx))
                    self.pending_internal.remove(internal_tx)
                    self.pending_bank.remove(bank_tx)
                    return self._try_match(current_time)
    
    def _escalate_old(self, current_time):
        while self.pending_internal and current_time - self.pending_internal[0][2] > self.escalation_timeout:
            self.escalated.append(self.pending_internal.popleft())
        while self.pending_bank and current_time - self.pending_bank[0][2] > self.escalation_timeout:
            self.escalated.append(self.pending_bank.popleft())

Problem 2: Webhook Retry System (30% Frequency)

Statement

Implement Stripe's webhook exponential backoff. After each failure, the next retry delay doubles (1s, 2s, 4s, 8s, ...), up to 8 attempts. Support:

from collections import defaultdict
import heapq

class WebhookRetrySystem:
    def __init__(self, max_attempts=8, base_delay=1):
        self.max_attempts = max_attempts
        self.base_delay = base_delay
        self.attempts = defaultdict(int)
        self.due_heap = []
        self.failed_permanently = []
    
    def dispatch(self, event_id, attempt_at):
        self.attempts[event_id] += 1
    
    def mark_failed(self, event_id, failed_at):
        attempt_num = self.attempts[event_id]
        if attempt_num >= self.max_attempts:
            self.failed_permanently.append(event_id)
            return
        delay = self.base_delay * (2 ** (attempt_num - 1))
        retry_time = failed_at + delay
        heapq.heappush(self.due_heap, (retry_time, event_id))
    
    def get_due_events(self, now):
        due = []
        while self.due_heap and self.due_heap[0][0] <= now:
            _, event_id = heapq.heappop(self.due_heap)
            due.append(event_id)
        return due

Stripe deductions:

Problem 3: Currency Conversion Graph (25% Frequency)

Statement

Given exchange rates rates = [(from, to, rate), ...], implement convert(from_currency, to_currency, amount). If no direct rate, find an intermediate conversion path.

from collections import defaultdict, deque

class CurrencyConverter:
    def __init__(self, rates):
        self.graph = defaultdict(dict)
        for from_cur, to_cur, rate in rates:
            self.graph[from_cur][to_cur] = rate
            self.graph[to_cur][from_cur] = 1.0 / rate
    
    def convert(self, from_cur, to_cur, amount):
        if from_cur == to_cur:
            return amount
        if from_cur not in self.graph:
            return None
        
        queue = deque([(from_cur, amount)])
        visited = {from_cur}
        
        while queue:
            current_cur, current_amount = queue.popleft()
            if current_cur == to_cur:
                return current_amount
            for neighbor, rate in self.graph[current_cur].items():
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append((neighbor, current_amount * rate))
        
        return None

Test:

rates = [
    ("USD", "EUR", 0.9),
    ("EUR", "GBP", 0.85),
    ("GBP", "JPY", 180),
]
conv = CurrencyConverter(rates)
print(conv.convert("USD", "JPY", 100))  # 100 * 0.9 * 0.85 * 180 = 13770.0

Stripe hidden tests:

Stripe OA Strategy Summary

Mistake Deduction Fix
No empty input handling 5 pts/Part First line: if not data: return [...]
No negative/zero handling 3 pts/Part Refunds are negative
Brute force O(n²) 0 pts (TLE) Use hashmap or heap
Abbreviated function names 1 pt/method bank_tx > bt
No unit tests 5 pts total Add at least 3 asserts

FAQ

Do I need to finish all Parts?

Not necessarily. Stripe scores on absolute points + Part completion. Full credit on Parts 1+2 (~60%) still has ~30% pass rate. Prioritize 100% correctness on Parts 1-2 before attempting Part 3.

Can I Google during the OA?

Officially no (HackerRank records via webcam), but enforcement is loose. Looking up docs (e.g., heapq syntax) is fine—don't search algorithm solutions. Graders flag stylistic anomalies suggesting non-original code.

How fast is feedback?

Usually 5-10 business days. Stripe's HR cadence is stable, and rejections are explicit (friendlier than Google/Meta).

Do these problems match 1point3acres?

Yes—all problems come from 2026 Q1-Q2 Stripe board on 1point3acres. Variable names and Part splits vary slightly per candidate; Stripe's question bank rotates monthly.

Recommended language?

Python (first choice), JavaScript (second). Stripe internally uses Ruby, but the OA doesn't enforce it. Avoid C++: Stripe problems are string-heavy and C++ is slower to write.


Preparing for Stripe OA?

oavoservice maintains the Stripe problem bank (including older problems like brace expansion, shipping cost, subscription notifications not covered here), with timed OA mocks and hidden-test-case rehearsals. Coaches include former Stripe engineers familiar with every grading deduction.

Add WeChat: Coding0201 to get the Stripe OA problem pack.

#Stripe #OA #PaymentSystems #Webhook #RealQuestions #1point3acres


Contact

Email: [email protected]
Telegram: @OAVOProxy