Verifies and decodes Dodo Payments webhooks using the Standard Webhooks signing scheme.
Always pass the exact request body bytes. Parsing and re-encoding JSON changes those bytes and makes an otherwise valid signature fail.
with {:ok, event} <-
DodoPayments.Webhooks.verify(raw_body, headers, webhook_secret) do
handle_event(event)
endA list of active secrets enables non-disruptive secret rotation. All secrets are checked without exposing which one matched.
Verification is deliberately stateless. Dodo may retry or replay a delivery,
and webhook events are not guaranteed to arrive in creation order. A
successful verification therefore does not claim that the event is new or
that it is the next event for a resource. Persist event.webhook_id in a
durable inbox with a unique constraint before acknowledging the request, and
make the business operation idempotent at the resource or order level as
well. Do not use the webhook timestamp as a generic last-write-wins ordering
key. Acknowledge only after processing has completed or the delivery has
been durably enqueued; an ETS or process-local cache is not a durable replay
or ordering mechanism.
Summary
Functions
@spec verify(binary(), map() | [{term(), term()}], binary() | [binary()], keyword()) :: {:ok, DodoPayments.Webhooks.Event.t()} | {:error, DodoPayments.Webhooks.VerificationError.t()}
Verifies the signature and returns a decoded, lossless event.
@spec verify!(binary(), map() | [{term(), term()}], binary() | [binary()], keyword()) :: DodoPayments.Webhooks.Event.t()
Like verify/4, but raises VerificationError when verification fails.
@spec verify_signature( binary(), map() | [{term(), term()}], binary() | [binary()], keyword() ) :: :ok | {:error, DodoPayments.Webhooks.VerificationError.t()}
Verifies authenticity without decoding the JSON event body.