Skip to content

Commit 199807e

Browse files
fix(source-stripe): accept string request field in stripeEventSchema (#375)
Older Stripe API versions send `event.request` as a plain string (the request ID), not the `{ id, idempotency_key }` object used by modern versions. When sdb-webhook-srv forwards these raw events as source_input messages, Zod rejects the string at parse time, silently dropping the record from the sync pipeline. Widen the schema to accept both formats via z.union. Committed-By-Agent: claude Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e049872 commit 199807e

2 files changed

Lines changed: 36 additions & 5 deletions

File tree

packages/source-stripe/src/spec.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it, expect } from 'vitest'
22
import { z } from 'zod'
3-
import spec, { configSchema, streamStateSpec } from './spec.js'
3+
import spec, { configSchema, stripeEventSchema, streamStateSpec } from './spec.js'
44
import { BUNDLED_API_VERSION, SUPPORTED_API_VERSIONS } from '@stripe/sync-openapi'
55

66
describe('configSchema api_version field', () => {
@@ -34,6 +34,34 @@ describe('configSchema api_version field', () => {
3434
})
3535
})
3636

37+
describe('stripeEventSchema', () => {
38+
const baseEvent = {
39+
id: 'evt_123',
40+
object: 'event' as const,
41+
api_version: '2024-06-20',
42+
created: 1715500000,
43+
type: 'invoice.created',
44+
livemode: true,
45+
pending_webhooks: 0,
46+
data: { object: { id: 'in_123', object: 'invoice' } },
47+
}
48+
49+
it('accepts request as a plain string (older API versions)', () => {
50+
const event = { ...baseEvent, request: 'req_xxxxx' }
51+
expect(stripeEventSchema.safeParse(event).success).toBe(true)
52+
})
53+
54+
it('accepts request as an object (modern API versions)', () => {
55+
const event = { ...baseEvent, request: { id: 'req_xxxxx', idempotency_key: null } }
56+
expect(stripeEventSchema.safeParse(event).success).toBe(true)
57+
})
58+
59+
it('accepts request as null', () => {
60+
const event = { ...baseEvent, request: null }
61+
expect(stripeEventSchema.safeParse(event).success).toBe(true)
62+
})
63+
})
64+
3765
describe('streamStateSpec JSON Schema round-trip', () => {
3866
it('accounted_range survives toJSONSchema → fromJSONSchema round-trip', () => {
3967
// The engine converts streamStateSpec to JSON Schema (spec export) then back

packages/source-stripe/src/spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,13 @@ export const stripeEventSchema = z.object({
113113
"Number of webhooks that haven't been successfully delivered (for example, to return a 20x response) to the URLs you specify."
114114
),
115115
request: z
116-
.object({
117-
id: z.string().nullable(),
118-
idempotency_key: z.string().nullable(),
119-
})
116+
.union([
117+
z.string(),
118+
z.object({
119+
id: z.string().nullable(),
120+
idempotency_key: z.string().nullable(),
121+
}),
122+
])
120123
.nullable()
121124
.describe('Information on the API request that triggers the event.'),
122125
type: z

0 commit comments

Comments
 (0)