Stripe webhook receiver (unauthenticated; signature is the auth)
Verify, dedup, project, acknowledge (§5.6).
No auth dependency by design. Nothing derived from the request is trusted
before verify_and_parse_webhook returns, and nothing derived from it is
ever logged — a rejected delivery is unauthenticated attacker-controlled
input, so the failure path logs the provider and the verdict, never the
body, the headers, or the SDK’s exception text.
That holds for the verified payload too, which is why neither failure
handler below passes exc_info: a SQLAlchemy StatementError renders its
bound parameters, and the ledger insert binds raw — the whole event. Log
exception classes and the identifiers we already hold, never a rendered
exception. (_rollback_quietly keeps its traceback: a rollback executes no
statement, so its errors carry no bound parameters to leak.)
Responses
200
Successful Response
{"status": "processed"} / {"status": "duplicate"} (§5.6 steps 2, 4),
or {"status": "unresolvable"} — an event too old to still be a
checkout race, whose subscription this deployment will never learn
(_UNMAPPABLE_EVENT_MAX_AGE; BUG-200).
Declared here rather than in app/schemas/billing.py on purpose: this
module shares no request/response vocabulary with the authenticated
billing routes, and the only consumer is a gateway that reads the status
code, not the body.