From 6e57b2481169fcdc0b11769e38c66bdf0131bfd1 Mon Sep 17 00:00:00 2001 From: Rebeka Dekany <50901361+rebekadekany@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:54:30 +0200 Subject: [PATCH] Record a metric for Stripe API request statuses to monitor rate limiting (#35779) * Record a metric for Stripe API request statuses to monitor rate limiting * Add on() stub to Stripe mock in StripeClient unit tests * Record the Stripe rate-limit reason for 429 responses * Add acceptance test for Stripe rate-limit reason metric GitOrigin-RevId: 1674b389f5a7fee403f980b6e813a3e2df81063c --- .../acceptance/src/mocks/MockStripeApi.mjs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/services/web/test/acceptance/src/mocks/MockStripeApi.mjs b/services/web/test/acceptance/src/mocks/MockStripeApi.mjs index 97488a6997..51ee09fbc0 100644 --- a/services/web/test/acceptance/src/mocks/MockStripeApi.mjs +++ b/services/web/test/acceptance/src/mocks/MockStripeApi.mjs @@ -245,6 +245,17 @@ class MockStripeApi extends AbstractMockApi { this.promotionCodes = [] // Deterministic ids so tests don't depend on Date.now()/randomness. this.idCounter = 0 + // When rateLimited is set, every request is answered with a 429. + this.rateLimited = false + this.rateLimitedReason = null + } + + // Make every request fail with a 429, as Stripe does when we hit a rate-limit + // bucket. Pass a `Stripe-Rate-Limited-Reason` value, or null for a header-less + // 429 (an object-lock timeout). + rateLimit(reason) { + this.rateLimited = true + this.rateLimitedReason = reason ?? null } _nextId(prefix) { @@ -391,6 +402,18 @@ class MockStripeApi extends AbstractMockApi { } applyRoutes() { + this.app.use((req, res, next) => { + if (!this.rateLimited) return next() + // Stripe-Should-Retry: false so the SDK fails fast instead of retrying. + res.set('Stripe-Should-Retry', 'false') + if (this.rateLimitedReason) { + res.set('Stripe-Rate-Limited-Reason', this.rateLimitedReason) + } + res.status(429).json({ + error: { type: 'rate_limit_error', message: 'Too many requests' }, + }) + }) + this.app.post('/v1/customers', (req, res) => { const { body } = parseReq(req, createCustomerSchema) const id = this._nextId('cus')