Ested SonarQube, Semgrep, and Checkmarx on our payment service. none caught the database race condition that caused duplicate charges.
Interview Experience
we run a SaaS platform with about 40k users. payment processing is handled by a Node.js microservice running 3 instances behind a load balancer, using Stripe webhooks and Postgres. last month we had 7
Full Details
we run a SaaS platform with about 40k users. payment processing is handled by a Node.js microservice running 3 instances behind a load balancer, using Stripe webhooks and Postgres. last month we had 7 cases of duplicate subscription charges over 2 weeks. took us 3 days to find the root cause. our entire static analysis stack - SonarQube, Semgrep, and a $35k/year Checkmarx enterprise license - found nothing. what happened is: // POST /webhooks/stripe async function handlePaymentSuccess(req, res) { const event = req.body; const session = event.data.object; const userId = session.metadata.user_id; const planId = session.metadata.plan_id; // Check if we already processed this session const existing = await db.query( 'SELECT id FROM subscriptions WHERE stripe_session_id = $1', [session.id] ); if (existing.rows.length > 0) { console.log('Session already processed:', session.id);
return res.json({ received: true }); } // Create subscription record await db.query( \INSERT INTO subscriptions (user_id, plan_id, stripe_session_id, status)` VALUES ($1, $2, $3, 'active')\,` [userId, planId, session.id] ); // Update user account await db.query( 'UPDATE users SET plan = $1, status = $2 WHERE id = $3', [planId, 'active', userId] ); res.json({ received: true }); } standard check-then-insert pattern. looks fine. what broke Stripe's documentation states: "Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout." we had a slow database query (table lock from a migration running in the background). response took about 8 seconds. Stripe timed out and retried the webhook. When Stripe retries an event, they generate a new signature and timestamp for the new delivery attempt, but the event ID remains the same. 10:23:15.120 - Instance A receives webhook (event_abc123) 10:23:15.140 - Instance A: SELECT... WHERE stripe_session_id = 'cs_xyz'
Result 0 rows 10:23:17.200 - Instance B receives retry (same event_abc123) 10:23:17.220 - Instance B: SELECT... WHERE stripe_session_id = 'cs_xyz'
Result 0 rows ← Instance A hasn't committed yet 10:23:23.100 - Instance A: INSERT subscriptions... 10:23:23.110 - Instance A:
returns 200 to Stripe 10:23:23.150 - Instance B: INSERT subscriptions... ← duplicate! 10:23:23.160 - Instance B:
returns 200 to Stripe classic time-of-check-to-time-of-use (TOCTOU) race condition at the database level across distributed service instances. why it happened: * multiple service instances (standard microservice setup) * Stripe webhook retry hits a different instance * Postgres READ COMMITTED isolation level (the default) allows both transactions to read before either commits - both see zero rows. * both proceed to INSERT * no database constraint to prevent duplicates happened 7 times over 2 weeks because it requires specific timing - webhook retry arriving while first request is still processing but hasn't committed. sonarqube 10.4: * code smells (use const, extract strings) * cognitive complexity: * bugs: 0 * quality gate:
PASSED ✓ * missed the race condition completely semgrep 1.50: * suggested helmet middleware * SQL injection false positive (parameterized queries) * caught one missing await in different file * style warnings didn't work - semgrep is syntax-based, can't model concurrent execution checkmarx sast… * "insufficient logging" * "missing input validation" * SQL injection false positives * error handling alert * concurrency issues found: 0 why they all failed: race conditions materialize from timing of requests, pattern-based static analysis can't reason about concurrent execution. static analyzers see: single execution path, syntax patterns they don't see: multiple instances, interleaving queries, transaction timing, network retries literally paying over 50k/year. and cant catch a simple textbook TOCTOU race condition that a single UNIQUE constraint would have prevented.
About This Question
This is a candidate experience report from a stripe interview for a swe role reported in 2026.
It covers the following topics: Ml, Strings, Sql, Stack Queue, Os, System Design, Stack .
Difficulty rating: Easy
Topics
More Stripe Interview Questions
About Stripe Interview Reports
This question was reported by a candidate who interviewed at Stripe. LeakCode aggregates interview reports from 10+ sources, including 1Point3Acres, Glassdoor, LeetCode Discuss, Blind, Reddit, Indeed, and Nowcoder. Each report is translated where necessary, deduplicated against existing entries, and tagged by company, role, round type, and reporting date.
Use this question as one calibration data point, not a memorization target. Companies typically rotate their question pools every 2-4 months; the exact wording of a 2024 question may differ from what you encounter today. The underlying pattern, difficulty level, and follow-up depth at Stripe are the higher-signal extractions to take from this report.
For broader preparation context, the Stripe interview process typically includes a recruiter screen, one or two technical phone screens, and a 4-5 round on-site loop covering coding, system design (at L4+ levels), and behavioral. Reports tagged on LeakCode show the round-by-round distribution and typical difficulty calibration. To browse questions filtered by round type and seniority, use the company hub linked above.
How To Practice This Type of Question
Solve similar problems on LeetCode under timed conditions (25-35 minutes per medium difficulty). The goal is pattern recognition: recognize the underlying technique (sliding window, two-pointer, BFS, memoized recursion, etc.) within 60-90 seconds of reading. Strong candidates verbalize their hypothesis out loud before coding, then iterate based on feedback. Weak candidates dive into implementation immediately, lose time on the wrong approach, and run out of time for follow-ups.
Companies update their question pools every 2-4 months. The exact wording of any given question may have been retired by the time you interview. Focus your prep on the pattern, not the specific problem. The patterns that appear in Stripe reports consistently are the ones worth investing in; one-off niche problems are not.
During Your Stripe Round
Apply the standard interview round template: clarify requirements (2-3 minutes), state your approach out loud and confirm direction with the interviewer (3-5 minutes), code with narration (15-25 minutes), test with concrete examples including edge cases (5 minutes), discuss optimization or trade-offs if time permits (5 minutes). This template is universally accepted across FAANG and adjacent companies; deviating from it produces weaker interviewer feedback signal.
The single most predictive failure mode in Stripe reports tagged "no hire": not asking clarifying questions. Interviewers are explicitly trained to weight this. Strong candidates ask 3-5 clarifying questions even on problems that look obvious; weak candidates dive into code immediately. The clarifying-question check is often the first signal recorded in the interviewer's written notes.