A Plug that verifies GoCardlessClient webhook signatures and parses events.
Parsed events are stored in conn.private[:gocardless_events] for downstream
handlers to consume. The raw body is stored in conn.private[:gocardless_raw_body].
On signature failure the plug halts the connection and returns HTTP 401. On JSON parse failure it returns HTTP 400.
Setup in Phoenix
First, ensure the raw body is preserved by configuring Plug.Parsers with
a custom body reader (required because Plug consumes the body by default):
# In your endpoint.ex:
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason,
body_reader: {GoCardlessClient.Webhooks.Plug, :read_body, []}Then add a pipeline and route in your router:
pipeline :gocardless_webhooks do
plug GoCardlessClient.Webhooks.Plug, secret: System.get_env("GOCARDLESS_WEBHOOK_SECRET")
end
scope "/webhooks" do
pipe_through :gocardless_webhooks
post "/gocardless", MyApp.WebhookController, :handle
endIn your controller
def handle(conn, _params) do
events = conn.private[:gocardless_events]
Enum.each(events, fn event ->
case {event["resource_type"], event["action"]} do
{"payments", "paid_out"} -> handle_payment_paid_out(event)
{"mandates", "active"} -> handle_mandate_active(event)
{"billing_requests", "fulfilled"} -> handle_br_fulfilled(event)
_ -> :ok
end
end)
send_resp(conn, 200, "")
endOptions
:secret(required) — your webhook endpoint secret from the GoCardlessClient dashboard.:max_body_bytes— maximum allowed body size in bytes (default: 10 MB).
Summary
Functions
Custom body reader for use with Plug.Parsers.
Functions
@spec read_body( Plug.Conn.t(), keyword() ) :: {:ok, binary(), Plug.Conn.t()} | {:more, binary(), Plug.Conn.t()} | {:error, term()}
Custom body reader for use with Plug.Parsers.
Reads and caches the raw body in conn.private[:gocardless_raw_body] before
Plug.Parsers consumes it, allowing GoCardlessClient.Webhooks.Plug to access it later.
Add to your endpoint.ex:
plug Plug.Parsers,
parsers: [:json],
json_decoder: Jason,
body_reader: {GoCardlessClient.Webhooks.Plug, :read_body, []}