← Stripe Interview Insights

Stripe·Software Engineer·Onsite - System Design / Architecture·Senior

Senior
Jul 2026

Summary

Stripe system design round for a software engineer role. The problem was an invoice processing system that needed to be extended with refunds, adjustments, and an audit trail. Pretty dense for a single session.

Questions Asked (4)

Q1

You have an existing invoice processing system with line items and payment status tracking. Extend it to support full and partial refunds, line-item adjustments, discounts, and credits, while keeping totals always reconciled and all operations idempotent.

System DesignData ModelingTechnical Trade-offs
Author's notes

This is a lot to hold in your head at once.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements and constraints, then design an append-only ledger model that records all financial events (charges, refunds, adjustments, discounts, credits) as immutable entries. Ensure idempotency via unique operation IDs and derive reconciled totals by aggregating ledger entries, not by mutating stored totals.

Pro tip: Emphasize that an append-only ledger with derived totals is the industry standard for financial systems (e.g., Stripe, Square) because it provides auditability, simplifies idempotency, and prevents reconciliation drift.

1. Clarify Requirements and Scope

Ask about expected volume, consistency needs, and whether refunds can be partial or multiple per line item. Confirm that idempotency applies to all mutating operations.

2. Design the Data Model

Propose an append-only ledger with entries for charges, refunds, adjustments, discounts, and credits, each linked to invoice and line items. Include fields for amount, type, timestamp, and a unique idempotency key.

3. Ensure Idempotency

Use client-supplied idempotency keys to deduplicate requests. Store processed keys with results, and reject or return the original response for duplicate requests.

4. Maintain Reconciled Totals

Derive totals by aggregating ledger entries rather than storing mutable totals. Optionally cache computed totals with invalidation on new entries, and provide a reconciliation job to verify consistency.

5. Handle Concurrency and Edge Cases

Address race conditions with optimistic locking or serializable transactions. Discuss partial refunds exceeding remaining balance, currency handling, and how discounts/credits interact with refunds.

Key Points to Mention

  • Append-only ledger pattern for immutability and auditability
  • Idempotency keys and deduplication strategy
  • Derived totals vs. stored totals for reconciliation
  • Handling partial refunds and line-item adjustments
  • Discounts and credits as negative ledger entries
  • Concurrency control (e.g., optimistic locking, transactions)

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

How would you design the schema for an append-only audit trail that captures every state change across creation, payment, refund, and adjustment events, including timestamp and actor?

Data ModelingSystem Design
Author's notes

I went with an event table where each row is immutable and carries a payload describing the change.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements: append-only, immutable, captures all state changes with timestamp and actor. Then propose a schema with a central audit table that records events, using a polymorphic reference to the entity and a JSON payload for event-specific details, ensuring it's scalable and queryable.

Pro tip: Emphasize that the audit trail should be write-once and never updated or deleted, and discuss how to handle schema evolution and efficient querying for compliance and debugging.

1. Clarify Requirements

Ask about the scope: what entities are audited (e.g., charges, refunds), retention policies, and query patterns. Confirm that the trail must be append-only and immutable.

2. Design Core Schema

Propose a central audit_events table with columns: id, event_type, entity_type, entity_id, timestamp, actor_id, and a JSON payload for event-specific data. Ensure it's indexed for common queries.

3. Handle Event Types

Define how to represent different events (creation, payment, refund, adjustment) either via a type field and payload, or separate tables per event type. Discuss trade-offs.

4. Ensure Immutability and Scalability

Explain how to enforce append-only (e.g., database permissions, no update/delete). Discuss partitioning by time, archiving, and read replicas for scale.

5. Address Querying and Compliance

Describe how to query the trail for audits (e.g., by entity, actor, time range) and how to handle data retention and privacy regulations.

Key Points to Mention

  • Append-only design: no updates or deletes, use database constraints or triggers.
  • Central audit table with polymorphic associations (entity_type, entity_id) to capture all events.
  • Use of JSON/JSONB for flexible event payloads to accommodate different event types.
  • Timestamp with timezone and actor identification (user ID, system, API key).
  • Indexing strategy for efficient queries (e.g., composite index on entity_type, entity_id, timestamp).
  • Consider partitioning by time for scalability and archiving old data.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How do you recompute the current invoice state, including subtotal, tax, total, and balance, purely from the audit trail?

System DesignTechnical Trade-offs
Author's notes

Fold over the event log in chronological order and accumulate state.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the audit trail's structure and the event types it contains, then describe a deterministic fold over events to derive the invoice state. Emphasize idempotency, ordering, and how to handle corrections or reversals without mutating history.

Pro tip: Mention that this is essentially event sourcing with a projection, and that you'd make the recomputation idempotent and replayable so it can be used for debugging, backfills, or consistency checks against the live state.

1. Clarify the audit trail schema and event types

Ask what events are recorded (e.g., invoice created, line item added, tax calculated, payment applied, credit issued) and whether they are immutable and ordered. This determines the fold logic and what state transitions are possible.

2. Define the state model and invariants

Specify the target state: subtotal, tax, total, balance, and any other fields. State invariants like total = subtotal + tax and balance = total - payments - credits, and note that these must hold after every event.

3. Design the fold/reduce algorithm

Process events in order, applying each event to the state. For example, line item added increases subtotal; tax calculated sets tax; payment applied reduces balance. Handle reversals or corrections as separate events that adjust the state.

4. Address ordering, idempotency, and concurrency

Use event sequence numbers or timestamps to ensure correct order. Make the fold idempotent by deduplicating events or using versioning. Discuss how to handle out-of-order events or concurrent writes, possibly with a snapshot plus incremental events.

5. Discuss trade-offs and operational concerns

Compare full replay vs. snapshotting for performance, and mention how to validate the recomputed state against the live state. Consider storage, latency, and how to handle large audit trails.

Key Points to Mention

  • Event sourcing and projections: the audit trail is the source of truth, and the invoice state is a derived projection.
  • Idempotency and determinism: the same events must always produce the same state, and replaying should not duplicate effects.
  • Ordering guarantees: events must be processed in the correct sequence, often using a monotonically increasing sequence number.
  • Handling corrections and reversals: use compensating events rather than mutating history, and ensure the fold accounts for them.
  • Snapshotting for performance: periodically persist a snapshot of the state to avoid replaying the entire history.
  • Consistency checks: compare the recomputed state with the live state to detect bugs or data corruption.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

How should refunds interact with the 'paid' status? Walk through the status transitions when a partial refund is applied to a fully paid invoice.

System DesignData Modeling
Author's notes

Paid goes to partially_paid if the refund doesn't cover the full amount, and back to unpaid if it does.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that refunds are separate transactions that adjust the effective amount paid, not the invoice status directly. Then walk through the state machine: a fully paid invoice remains 'paid' after a partial refund, but the refundable amount decreases; if the refund is full, the invoice may transition to 'refunded' or 'void' depending on business rules. Emphasize idempotency, audit trails, and how downstream systems (e.g., accounting, revenue recognition) should interpret the status.

Pro tip: Mention that refunds should be modeled as immutable ledger entries linked to the original payment, and that the invoice status should be derived from the net paid amount to avoid inconsistencies. This shows you think about data integrity and event sourcing, which is highly valued at Stripe.

1. Define the semantics of 'paid' status

Clarify that 'paid' means the invoice has been fully settled by one or more payments. It does not imply that no refunds have occurred; it reflects the original payment completion.

2. Model refunds as separate transactions

Explain that a refund is a new transaction that reverses a portion of the payment. It should not mutate the original payment record but create a linked refund record for auditability.

3. Determine the impact on invoice status

For a partial refund, the invoice remains 'paid' because the net amount paid is still positive and the invoice was fully settled. For a full refund, the invoice may transition to 'refunded' or 'void' based on business rules.

4. Walk through the state transitions

Describe the sequence: invoice created -> payment applied -> status 'paid' -> partial refund applied -> status remains 'paid' but refundable amount decreases. If full refund, status changes to 'refunded'.

5. Address edge cases and consistency

Discuss handling multiple partial refunds, refunds after chargebacks, and ensuring idempotency. Mention that status should be derived from the net paid amount to avoid race conditions.

Key Points to Mention

  • Refunds are separate transactions and should not overwrite the original payment record.
  • Partial refund does not change the invoice status from 'paid' because the invoice was fully settled.
  • Full refund may transition the invoice to 'refunded' or 'void', depending on whether the invoice is considered canceled.
  • Use an immutable ledger or event log to track payments and refunds for audit and reconciliation.
  • Derive the invoice status from the net paid amount (sum of payments minus sum of refunds) to ensure consistency.
  • Consider idempotency keys for refund operations to prevent duplicate refunds.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.