@@ -28,12 +28,12 @@ type WebhookParsedEvent = {
2828 suspectPayloadType : boolean ;
2929} ;
3030type WebhookTestHeaderOptions = {
31- timestamp : number ;
31+ timestamp ? : number ;
3232 payload : string ;
3333 secret : string ;
34- scheme : string ;
35- signature : string ;
36- cryptoProvider : CryptoProvider ;
34+ scheme ? : string ;
35+ signature ? : string ;
36+ cryptoProvider ? : CryptoProvider ;
3737} ;
3838
3939// export type WebhookEvent = Record<string, unknown>;
@@ -43,16 +43,16 @@ type WebhookSignatureObject = {
4343 encodedPayload : WebhookPayload ,
4444 encodedHeader : WebhookHeader ,
4545 secret : string ,
46- tolerance : number ,
47- cryptoProvider : CryptoProvider ,
46+ tolerance ? : number ,
47+ cryptoProvider ? : CryptoProvider ,
4848 receivedAt ?: number
4949 ) => boolean ;
5050 verifyHeaderAsync : (
5151 encodedPayload : WebhookPayload ,
5252 encodedHeader : WebhookHeader ,
5353 secret : string ,
54- tolerance : number ,
55- cryptoProvider : CryptoProvider ,
54+ tolerance ? : number ,
55+ cryptoProvider ? : CryptoProvider ,
5656 receivedAt ?: number
5757 ) => Promise < boolean > ;
5858} ;
@@ -159,7 +159,7 @@ export function createWebhooks(
159159 : JSON . parse ( payload ) ;
160160 if ( jsonPayload && jsonPayload . object === 'v2.core.event' ) {
161161 throw new Error (
162- 'You passed an event notification to stripe.webhooks.constructEvent, which expects a webhook payload. Use stripe.parseEventNotification instead.'
162+ 'You passed an event notification to stripe.webhooks.constructEvent, which expects a webhook payload. Use stripe.parseEventNotificationAsync instead.'
163163 ) ;
164164 }
165165 return jsonPayload ;
@@ -211,8 +211,8 @@ export function createWebhooks(
211211 encodedPayload : WebhookPayload ,
212212 encodedHeader : WebhookHeader ,
213213 secret : string ,
214- tolerance : number ,
215- cryptoProvider : CryptoProvider ,
214+ tolerance ? : number ,
215+ cryptoProvider ? : CryptoProvider ,
216216 receivedAt ?: number
217217 ) : boolean {
218218 const {
@@ -233,12 +233,17 @@ export function createWebhooks(
233233 secret
234234 ) ;
235235
236+ /**
237+ * TODO(MAJOR): https://go/j/DEVSDK-3087
238+ * Passing in 0 by default skips timestamp tolerance verifications. Although it is mostly used in test,
239+ * we should change the default behavior to pass DEFAULT_TOLERANCE instead of 0 in the next major.
240+ */
236241 validateComputedSignature (
237242 payload ,
238243 header ,
239244 details ,
240245 expectedSignature ,
241- tolerance ,
246+ tolerance || 0 ,
242247 suspectPayloadType ,
243248 secretContainsWhitespace ,
244249 receivedAt
@@ -251,8 +256,8 @@ export function createWebhooks(
251256 encodedPayload : WebhookPayload ,
252257 encodedHeader : WebhookHeader ,
253258 secret : string ,
254- tolerance : number ,
255- cryptoProvider : CryptoProvider ,
259+ tolerance ? : number ,
260+ cryptoProvider ? : CryptoProvider ,
256261 receivedAt ?: number
257262 ) : Promise < boolean > {
258263 const {
@@ -274,12 +279,17 @@ export function createWebhooks(
274279 secret
275280 ) ;
276281
282+ /**
283+ * TODO(MAJOR): https://go/j/DEVSDK-3087
284+ * Passing in 0 by default skips timestamp tolerance verifications. Although it is mostly used in test,
285+ * we should change the default behavior to pass DEFAULT_TOLERANCE instead of 0 in the next major.
286+ */
277287 return validateComputedSignature (
278288 payload ,
279289 header ,
280290 details ,
281291 expectedSignature ,
282- tolerance ,
292+ tolerance || 0 ,
283293 suspectPayloadType ,
284294 secretContainsWhitespace ,
285295 receivedAt
@@ -374,6 +384,31 @@ export function createWebhooks(
374384 } ;
375385 }
376386
387+ /**
388+ * Validates that at least one signature in the parsed header matches the
389+ * expected signature, and that the event timestamp is within the allowed
390+ * {@link tolerance} window (in seconds). Set `tolerance` to `0` to skip
391+ * timestamp verification.
392+ *
393+ * TODO(MAJOR): https://go/j/DEVSDK-3087 - Change this default behavior to use DEFAULT_TOLERANCE instead of 0.
394+ * By default, validateComputedSignature doesn't perform timestamp verification.
395+ *
396+ * This method is mostly meant for tests or offline processing where the delivery time
397+ * of the event isn't important.
398+ * Integrations that process webhooks as they come in should use constructEvent method instead.
399+ *
400+ * @param payload The decoded webhook payload string.
401+ * @param header The decoded `stripe-signature` header value.
402+ * @param details Parsed header containing timestamp and signatures.
403+ * @param expectedSignature HMAC signature computed from the payload and secret.
404+ * @param tolerance Maximum allowed age of the event in seconds. Use 0 to skip timestamp tolerance verification.
405+ * @param suspectPayloadType Whether the payload was not a string or Buffer.
406+ * @param secretContainsWhitespace Whether the signing secret contains whitespace.
407+ * @param receivedAt - Timestamp for age calculation
408+ * @returns `true` if the signature and timestamp are valid.
409+ *
410+ * @throws {StripeSignatureVerificationError } If verification fails.
411+ */
377412 function validateComputedSignature (
378413 payload : string ,
379414 header : string ,
@@ -436,11 +471,12 @@ export function createWebhooks(
436471
437472 function parseHeader (
438473 header : WebhookHeader ,
439- scheme : string
474+ scheme ? : string
440475 ) : WebhookParsedHeader | null {
441476 if ( typeof header !== 'string' ) {
442477 return null ;
443478 }
479+ scheme = scheme || signature . EXPECTED_SCHEME ;
444480
445481 return header . split ( ',' ) . reduce < WebhookParsedHeader > (
446482 ( accum , item ) => {
@@ -478,7 +514,13 @@ export function createWebhooks(
478514
479515 function prepareOptions (
480516 opts : WebhookTestHeaderOptions
481- ) : WebhookTestHeaderOptions & {
517+ ) : Omit <
518+ WebhookTestHeaderOptions ,
519+ 'timestamp' | 'scheme' | 'cryptoProvider'
520+ > & {
521+ timestamp : number ;
522+ scheme : string ;
523+ cryptoProvider : CryptoProvider ;
482524 payloadString : string ;
483525 generateHeaderString : ( signature : string ) => string ;
484526 } {
@@ -489,7 +531,8 @@ export function createWebhooks(
489531 }
490532
491533 const timestamp =
492- Math . floor ( opts . timestamp ) || Math . floor ( Date . now ( ) / 1000 ) ;
534+ ( opts . timestamp && Math . floor ( opts . timestamp ) ) ||
535+ Math . floor ( Date . now ( ) / 1000 ) ;
493536 const scheme = opts . scheme || signature . EXPECTED_SCHEME ;
494537 const cryptoProvider = opts . cryptoProvider || getCryptoProvider ( ) ;
495538 const payloadString = `${ timestamp } .${ opts . payload } ` ;
0 commit comments