## TL;DR

Signature verification fails for three usual reasons: you verify against a parsed JSON body instead of the raw bytes, you use the wrong endpoint secret, or a proxy altered the payload. Always verify against the exact raw request body with the secret shown for that endpoint in the dashboard. Fix the body handling first; it is the cause nine times out of ten.

## Error

```text
```text
No signatures found matching the expected signature for payload
```
```

## Steps

1. Confirm your handler reads the raw request body bytes before any JSON parsing.
   Expected: Frameworks that parse first destroy the exact bytes the signature covers.
2. Check you use the endpoint-specific signing secret from the dashboard, not the API key.
   Expected: Wrong-secret failures are eliminated.
3. Verify no proxy, WAF, or body-parser middleware rewrites the payload (whitespace, encoding).
   Expected: The bytes Stripe signed are the bytes you verify.
4. Test with the Stripe CLI listener, which signs correctly, to isolate your code from infra.
   Expected: You learn whether the bug is code or infrastructure.
5. Log verification failures with a payload hash (never the full payload) for ongoing diagnosis.
   Expected: Future failures are diagnosable without leaking data.

## When to use

- Invoice webhooks fail signature checks
- Events work via CLI but not in production
- A proxy sits in front of your endpoint

## When not to use

- Events never arrive (delivery/retry concern, not signatures)
- Handlers process but business logic is wrong
- You use a third-party receiver that verifies for you

## Compatibility

Stripe webhook signing; official SDK construct_event helpers. Stripe CLI for local testing.

## Variant phrasings

### ### Stripe webhook signature mismatch invoice

### ### construct_event fails raw body

### ### webhook endpoint secret wrong Stripe

## Root cause

The signature is an HMAC over the exact raw bytes Stripe sent; any transformation (JSON re-serialization, whitespace normalization, charset conversion) changes the bytes and breaks the HMAC. Body-parser middleware is the classic culprit because it helpfully parses before your code sees the raw stream.

## Edge cases

- Rolling the endpoint secret invalidates in-flight retries signed with the old one; keep both during rotation
- Multiple endpoints each have their own secret; mismatched pairing fails silently confusing
- Replayed events verify fine (same bytes); idempotency is a separate concern

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_7KUMqw4UJzzLTagzgxeYPQ
