Column.WebhookHandler behaviour (Column v1.0.0)

Copy Markdown View Source

Behaviour and plug helper for handling incoming Column webhook deliveries.

Usage with Plug/Phoenix

Implement the Column.WebhookHandler behaviour in your handler module:

defmodule MyApp.ColumnWebhookHandler do
  use Column.WebhookHandler,
    secret: System.get_env("COLUMN_WEBHOOK_SECRET")

  @impl Column.WebhookHandler
  def handle_event("transfer.ach.settled", payload, _meta) do
    transfer = payload["data"]
    MyApp.Payments.mark_settled(transfer["id"])
    :ok
  end

  def handle_event("transfer.ach.returned", payload, _meta) do
    MyApp.Payments.mark_returned(payload["data"]["id"])
    :ok
  end

  def handle_event(_type, _payload, _meta) do
    # Ignore unhandled events — always return :ok
    :ok
  end
end

Then add it to your router (Phoenix example):

# router.ex
post "/webhooks/column", MyApp.ColumnWebhookHandler, []

Or use as a Plug in a pipeline:

plug MyApp.ColumnWebhookHandler

Behaviour callbacks

Your module must implement:

  • handle_event(type, payload, meta) — called for each verified webhook. Return :ok to acknowledge, {:error, reason} to signal failure (Column will retry the delivery).

Signature verification

use Column.WebhookHandler injects plug pipeline code that:

  1. Reads the raw request body (before JSON parsing destroys byte-exactness)
  2. Validates the column-signature HMAC-SHA256 header
  3. Rejects with 401 if invalid
  4. Decodes the JSON body and dispatches to handle_event/3
  5. Returns 200 on :ok, 500 on {:error, _}

Important: You must configure your Plug/Phoenix endpoint to cache raw body for webhook routes. In Phoenix:

# endpoint.ex
plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  body_reader: {MyApp.CacheBodyReader, :read_body, []},
  json_decoder: Phoenix.json_library()

Summary

Callbacks

Handle a verified Column webhook event.

Callbacks

handle_event(type, payload, meta)

@callback handle_event(type :: String.t(), payload :: map(), meta :: map()) ::
  :ok | {:error, term()}

Handle a verified Column webhook event.

  • type — the Column event type string, e.g. "transfer.ach.settled"
  • payload — the full decoded JSON payload map
  • meta — map with :request_id and :delivery_id keys