{"record":{"id":"ad6afdbb041e3d37","repo":"hcengineering/platform","slug":"missing-signature","errorCode":null,"errorMessage":"Missing signature","messagePattern":"Missing signature","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"services/payment/pod-payment/src/providers/stripe/webhook.ts","lineNumber":51,"sourceCode":"  webhookSecret: string,\n  stripeApiKey: string,\n  req: Request,\n  res: Response\n): Promise<void> {\n  try {\n    // Body is a Buffer from express.raw() middleware\n    const rawBody = req.body as Buffer\n    const sig = req.headers['stripe-signature'] as string\n\n    if (!(rawBody instanceof Buffer) || rawBody.length === 0) {\n      ctx.error('Invalid webhook body')\n      res.status(400).json({ error: 'Invalid body' })\n      return\n    }\n\n    if (sig === undefined) {\n      ctx.error('Missing Stripe signature header')\n      res.status(400).json({ error: 'Missing signature' })\n      return\n    }\n\n    // Create Stripe instance for webhook verification\n    const stripe = new Stripe(stripeApiKey, { apiVersion: '2025-02-24.acacia' })\n\n    // Verify webhook signature and parse event\n    let event: Stripe.Event\n    try {\n      event = stripe.webhooks.constructEvent(rawBody, sig, webhookSecret)\n    } catch (err: any) {\n      ctx.error('Invalid Stripe webhook signature', { err })\n      res.status(403).json({ error: 'Invalid signature' })\n      return\n    }\n\n    // Route to appropriate handler based on event type\n    switch (event.type) {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/payment/pod-payment/src/providers/stripe/webhook.ts#L33-L69","documentation":"The handler responds 400 'Missing signature' when the stripe-signature header is absent. Without it constructEvent cannot verify the request, so it is rejected before verification is attempted.","triggerScenarios":"Caller posts directly to the endpoint (curl/health check) without a stripe-signature header; Stripe CLI forwarded to the wrong path; a proxy strips the custom header; event delivered by something other than Stripe.","commonSituations":"Manual smoke tests hitting the webhook URL; ingress rules or middleware that drop unknown headers; misconfigured Stripe CLI forward target.","solutions":["Only let Stripe (or stripe listen / stripe CLI) call this endpoint; the signature header is added automatically by Stripe","If testing manually, include a valid Stripe-Signature header generated via stripe CLI","Check ingress/proxy header stripping (e.g. nginx underscores_in_headers or WAF rules)","Verify the webhook endpoint URL registered in Stripe matches this route"],"exampleFix":"// before\ncurl -X POST http://host/webhooks/stripe -d '{...}'\n// after\nstripe listen --forward-to localhost:3000/webhooks/stripe\nstripe trigger customer.subscription.created","handlingStrategy":"validation","validationCode":"const sig = req.headers['stripe-signature'] as string | undefined\nif (sig === undefined) {\n  throw new Error('stripe-signature header required; only Stripe should call this endpoint')\n}","typeGuard":"function hasStripeSignature(headers: Record<string, unknown>): headers is Record<string, string> & { 'stripe-signature': string } {\n  return typeof headers['stripe-signature'] === 'string'\n}","tryCatchPattern":null,"preventionTips":["Only call the endpoint via Stripe or stripe CLI","Keep the route unauthenticated and signature-verified","Verify proxies don't strip custom headers","Check endpoint URL in Stripe dashboard matches the route"],"tags":["http","webhook","signature","validation"],"backgroundTag":"webhook-signature-header-missing","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}