From 0187ad34e9e2e9d6876667c086f65f890f6884d2 Mon Sep 17 00:00:00 2001 From: Matthew Mueller Date: Tue, 12 May 2026 17:12:49 -0500 Subject: [PATCH] fix: accept string request field in stripeEventSchema Older Stripe API versions send `request` as a plain string (e.g. "req_xxxxx") rather than an object. The schema rejected these, silently dropping webhook records. Accept both formats via z.union. Co-Authored-By: Claude Opus 4.6 (1M context) Committed-By-Agent: claude --- packages/source-stripe/src/spec.test.ts | 49 ++++++++++++++++++++++++- packages/source-stripe/src/spec.ts | 11 ++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/packages/source-stripe/src/spec.test.ts b/packages/source-stripe/src/spec.test.ts index d2b1e9ec9..ae920da80 100644 --- a/packages/source-stripe/src/spec.test.ts +++ b/packages/source-stripe/src/spec.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { z } from 'zod' -import spec, { configSchema, streamStateSpec } from './spec.js' +import spec, { configSchema, stripeEventSchema, streamStateSpec } from './spec.js' import { BUNDLED_API_VERSION, SUPPORTED_API_VERSIONS } from '@stripe/sync-openapi' describe('configSchema api_version field', () => { @@ -70,3 +70,50 @@ describe('streamStateSpec JSON Schema round-trip', () => { expect(zodFromJson.safeParse(stateInProgress).success).toBe(true) }) }) + +describe('stripeEventSchema', () => { + it('accepts request as a plain string (older API versions)', () => { + const event = { + id: 'evt_test', + object: 'event', + api_version: '2020-08-27', + created: 1234567890, + data: { object: {} }, + livemode: false, + pending_webhooks: 0, + request: 'req_xxxxx', + type: 'charge.created', + } + expect(stripeEventSchema.safeParse(event).success).toBe(true) + }) + + it('accepts request as an object (modern API versions)', () => { + const event = { + id: 'evt_test', + object: 'event', + api_version: '2020-08-27', + created: 1234567890, + data: { object: {} }, + livemode: false, + pending_webhooks: 0, + request: { id: 'req_xxxxx', idempotency_key: null }, + type: 'charge.created', + } + expect(stripeEventSchema.safeParse(event).success).toBe(true) + }) + + it('accepts request as null', () => { + const event = { + id: 'evt_test', + object: 'event', + api_version: '2020-08-27', + created: 1234567890, + data: { object: {} }, + livemode: false, + pending_webhooks: 0, + request: null, + type: 'charge.created', + } + expect(stripeEventSchema.safeParse(event).success).toBe(true) + }) +}) diff --git a/packages/source-stripe/src/spec.ts b/packages/source-stripe/src/spec.ts index 769fe1898..b1e442643 100644 --- a/packages/source-stripe/src/spec.ts +++ b/packages/source-stripe/src/spec.ts @@ -113,10 +113,13 @@ export const stripeEventSchema = z.object({ "Number of webhooks that haven't been successfully delivered (for example, to return a 20x response) to the URLs you specify." ), request: z - .object({ - id: z.string().nullable(), - idempotency_key: z.string().nullable(), - }) + .union([ + z.string(), + z.object({ + id: z.string().nullable(), + idempotency_key: z.string().nullable(), + }), + ]) .nullable() .describe('Information on the API request that triggers the event.'), type: z