Packages
stripity_stripe
2.14.1
3.3.2
3.3.1
3.2.0
3.1.1
3.1.0
3.0.0
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.1
2.15.0
2.14.1
2.14.0
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.0
2.7.2
2.7.1
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.3
2.2.2
2.2.1
2.2.0
2.1.0
2.0.1
2.0.0
2.0.0-alpha.11
2.0.0-alpha.10
2.0.0-alpha.9
2.0.0-alpha.8
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.6.2
1.6.1
1.6.0
1.4.0
1.3.0
1.2.0
1.1.0
0.5.0
0.4.0
0.3.0
0.2.0
A Stripe client for Elixir.
Current section
Files
Jump to
Current section
Files
lib/stripe/webhook_plug.ex
defmodule Stripe.WebhookPlug do
@moduledoc """
Helper `Plug` to process webhook events and send them to a custom handler.
## Installation
To handle webhook events, you must first configure your application's endpoint.
Add the following to `endpoint.ex`, **before** `Plug.Parsers` is loaded.
```elixir
plug Stripe.WebhookPlug,
at: "/webhook/stripe",
handler: MyAppWeb.StripeHandler,
secret: "whsec_******"
```
If you have not yet added a webhook to your Stripe account, you can do so
by visiting `Developers > Webhooks` in the Stripe dashboard. Use the route
you configured in the endpoint above and copy the webhook secret into your
app's configuration.
### Supported options
- `at`: The URL path your application should listen for Stripe webhooks on.
Configure this to match whatever you set in the webhook.
- `handler`: Custom event handler module that accepts `Stripe.Event` structs
and processes them within your application. You must create this module.
- `secret`: Webhook secret starting with `whsec_` obtained from the Stripe
dashboard. This can also be a function or a tuple for runtime configuration.
- `tolerance`: Maximum age (in seconds) allowed for the webhook event.
See `Stripe.Webhook.construct_event/4` for more information.
## Handling events
You will need to create a custom event handler module to handle events.
Your event handler module should implement the `Stripe.WebhookHandler`
behavior, defining a `handle_event/1` function which takes a `Stripe.Event`
struct and returns either `{:ok, term}` or `:ok`. This will mark the event as
successfully processed. Alternatively handler can signal an error by returning
`:error` or `{:error, reason}` tuple, where reason is an atom or a string.
HTTP status code 400 will be used for errors.
### Example
```elixir
# lib/myapp_web/stripe_handler.ex
defmodule MyAppWeb.StripeHandler do
@behaviour Stripe.WebhookHandler
@impl true
def handle_event(%Stripe.Event{type: "charge.succeeded"} = event) do
# TODO: handle the charge.succeeded event
end
@impl true
def handle_event(%Stripe.Event{type: "invoice.payment_failed"} = event) do
# TODO: handle the invoice.payment_failed event
end
# Return HTTP 200 for unhandled events
@impl true
def handle_event(_event), do: :ok
end
```
## Configuration
You can configure the webhook secret in your app's own config file.
For example:
```elixir
config :myapp,
# [...]
stripe_webhook_secret: "whsec_******"
```
You may then include the secret in your endpoint:
```elixir
plug Stripe.WebhookPlug,
at: "/webhook/stripe",
handler: MyAppWeb.StripeHandler,
secret: Application.get_env(:myapp, :stripe_webhook_secret)
```
### Runtime configuration
If you're loading config dynamically at runtime (eg with `runtime.exs`
or an OTP app) you must pass a tuple or function as the secret.
```elixir
# With a tuple
plug Stripe.WebhookPlug,
at: "/webhook/stripe",
handler: MyAppWeb.StripeHandler,
secret: {Application, :get_env, [:myapp, :stripe_webhook_secret]}
# Or, with a function
plug Stripe.WebhookPlug,
at: "/webhook/stripe",
handler: MyAppWeb.StripeHandler,
secret: fn -> Application.get_env(:myapp, :stripe_webhook_secret) end
```
"""
import Plug.Conn
alias Plug.Conn
@behaviour Plug
@impl true
def init(opts) do
path_info = String.split(opts[:at], "/", trim: true)
opts
|> Enum.into(%{})
|> Map.put_new(:path_info, path_info)
end
@impl true
def call(
%Conn{method: "POST", path_info: path_info} = conn,
%{
path_info: path_info,
secret: secret,
handler: handler
} = opts
) do
secret = parse_secret!(secret)
with [signature] <- get_req_header(conn, "stripe-signature"),
{:ok, payload, _} = Conn.read_body(conn),
{:ok, %Stripe.Event{} = event} <- construct_event(payload, signature, secret, opts),
:ok <- handle_event!(handler, event) do
send_resp(conn, 200, "Webhook received.") |> halt()
else
{:handle_error, reason} -> send_resp(conn, 400, reason) |> halt()
_ -> send_resp(conn, 400, "Bad request.") |> halt()
end
end
@impl true
def call(%Conn{path_info: path_info} = conn, %{path_info: path_info}) do
send_resp(conn, 400, "Bad request.") |> halt()
end
@impl true
def call(conn, _), do: conn
defp construct_event(payload, signature, secret, %{tolerance: tolerance}) do
Stripe.Webhook.construct_event(payload, signature, secret, tolerance)
end
defp construct_event(payload, signature, secret, _opts) do
Stripe.Webhook.construct_event(payload, signature, secret)
end
defp handle_event!(handler, %Stripe.Event{} = event) do
case handler.handle_event(event) do
{:ok, _} ->
:ok
:ok ->
:ok
{:error, reason} when is_binary(reason) ->
{:handle_error, reason}
{:error, reason} when is_atom(reason) ->
{:handle_error, Atom.to_string(reason)}
:error ->
{:handle_error, ""}
resp ->
raise """
#{inspect(handler)}.handle_event/1 returned an invalid response. Expected {:ok, term}, :ok, {:error, reason} or :error
Got: #{inspect(resp)}
Event data: #{inspect(event)}
"""
end
end
defp parse_secret!({m, f, a}), do: apply(m, f, a)
defp parse_secret!(fun) when is_function(fun), do: fun.()
defp parse_secret!(secret) when is_binary(secret), do: secret
defp parse_secret!(secret) do
raise """
The Stripe webhook secret is invalid. Expected a string, tuple, or function.
Got: #{inspect(secret)}
If you're setting the secret at runtime, you need to pass a tuple or function.
For example:
plug Stripe.WebhookPlug,
at: "/webhook/stripe",
handler: MyAppWeb.StripeHandler,
secret: {Application, :get_env, [:myapp, :stripe_webhook_secret]}
"""
end
end