PinStripe.WebhookSignature (PinStripe v0.4.0)

View Source

Verifies Stripe webhook signatures.

Stripe signs webhook events and includes the signature in the stripe-signature header. This module verifies that signature to ensure the webhook came from Stripe.

See: https://docs.stripe.com/webhooks#verify-official-libraries

Configuration

Configure your webhook secret in your application config:

config :pin_stripe,
  stripe_webhook_secret: "whsec_..."

Usage

payload = ~s({"id": "evt_test", "type": "customer.created"})
signature = get_req_header(conn, "stripe-signature")
secret = Application.fetch_env!(:pin_stripe, :stripe_webhook_secret)

case WebhookSignature.verify(payload, signature, secret) do
  :ok ->
    # Process webhook
  {:error, reason} ->
    # Reject webhook
end

Summary

Functions

Signs payload with timestamp and secret.

Verifies payload against signature and secret.

Functions

sign(payload, timestamp, secret)

@spec sign(binary(), integer(), binary()) :: binary()

Signs payload with timestamp and secret.

Useful for testing webhook handling.

Examples

iex> payload = ~s({"type": "test"})
iex> timestamp = System.system_time(:second)
iex> secret = "whsec_test"
iex> signature = PinStripe.WebhookSignature.sign(payload, timestamp, secret)
iex> String.starts_with?(signature, "t=")
true

verify(payload, signature, secret)

@spec verify(binary(), binary(), binary()) :: :ok | {:error, binary()}

Verifies payload against signature and secret.

Returns :ok if the signature is valid and not expired. Returns {:error, reason} otherwise.

The signature must be less than 300 seconds (5 minutes) old to prevent replay attacks.

Examples

iex> payload = ~s({"type": "test"})
iex> timestamp = System.system_time(:second)
iex> secret = "whsec_test"
iex> signature = PinStripe.WebhookSignature.sign(payload, timestamp, secret)
iex> PinStripe.WebhookSignature.verify(payload, signature, secret)
:ok