GoCardlessClient.Webhooks (GoCardlessClient v2.0.0)

Copy Markdown View Source

Webhook signature verification, event parsing, and helper predicates.

Use this module in your web server to securely receive and process GoCardless webhook events. For the webhook delivery log API, see GoCardlessClient.Resources.Webhooks.

Security

Always verify the Webhook-Signature header before processing any event. GoCardless signs each request with your webhook secret using HMAC-SHA256. This prevents attackers from sending forged webhook events.

Example (Phoenix controller)

def webhook(conn, _params) do
  secret = Application.fetch_env!(:my_app, :gocardless_webhook_secret)
  raw_body = conn.assigns[:raw_body]
  signature = get_req_header(conn, "webhook-signature") |> List.first()

  case GoCardlessClient.Webhooks.parse(raw_body, signature, secret) do
    {:ok, events} ->
      Enum.each(events, &dispatch_event/1)
      send_resp(conn, 200, "OK")

    {:error, :invalid_signature} ->
      send_resp(conn, 498, "Invalid signature")

    {:error, reason} ->
      send_resp(conn, 400, "Bad request: #{reason}")
  end
end

Generating idempotency keys

key = GoCardlessClient.Webhooks.idempotency_key()

Known GoCardless IP ranges

Use gocardless_ip?/1 to validate the request origin as an additional (not primary) security measure. Always prefer signature verification.

Summary

Functions

Returns true if the event has the given action string.

Returns true if the event is for a billing request resource.

Returns true if the event is for a creditor resource.

Returns true if the event is for a customer resource.

Returns true if the event is for an export resource.

Returns true if the IP address is within GoCardless's known outbound ranges.

Generates a cryptographically random idempotency key.

Returns true if the event is for an instalment schedule resource.

Returns true if the event is for a mandate resource.

Returns true if the event is for an outbound payment resource.

Parses and verifies a webhook payload.

Returns true if the event is for a payment account transaction resource.

Returns true if the event is for a payment resource.

Returns true if the event is for a payout resource.

Returns true if the event is for a refund resource.

Returns true if the event is for a scheme identifier resource.

Returns true if the event is for a subscription resource.

Verifies a webhook signature using HMAC-SHA256.

Functions

action?(arg1, expected)

@spec action?(map(), String.t()) :: boolean()

Returns true if the event has the given action string.

iex> GoCardlessClient.Webhooks.action?(%{"action" => "paid_out"}, "paid_out")
true

billing_request_event?(arg1)

@spec billing_request_event?(map()) :: boolean()

Returns true if the event is for a billing request resource.

creditor_event?(arg1)

@spec creditor_event?(map()) :: boolean()

Returns true if the event is for a creditor resource.

customer_event?(arg1)

@spec customer_event?(map()) :: boolean()

Returns true if the event is for a customer resource.

export_event?(arg1)

@spec export_event?(map()) :: boolean()

Returns true if the event is for an export resource.

gocardless_ip?(ip)

@spec gocardless_ip?(String.t()) :: boolean()

Returns true if the IP address is within GoCardless's known outbound ranges.

Use as an additional (not primary) security check. Signature verification with verify/3 should always be your primary security measure.

idempotency_key()

@spec idempotency_key() :: String.t()

Generates a cryptographically random idempotency key.

Returns a 32-character lowercase hex string (128 bits of entropy). Use this when creating payments, billing requests, or any write operation.

opts = [idempotency_key: GoCardlessClient.Webhooks.idempotency_key()]

instalment_schedule_event?(arg1)

@spec instalment_schedule_event?(map()) :: boolean()

Returns true if the event is for an instalment schedule resource.

mandate_event?(arg1)

@spec mandate_event?(map()) :: boolean()

Returns true if the event is for a mandate resource.

outbound_payment_event?(arg1)

@spec outbound_payment_event?(map()) :: boolean()

Returns true if the event is for an outbound payment resource.

parse(body, signature, secret)

@spec parse(String.t() | nil, String.t() | nil, String.t()) ::
  {:ok, [map()]} | {:error, atom()}

Parses and verifies a webhook payload.

Returns {:ok, [event]} on success, or {:error, reason} on failure.

Possible errors:

  • :empty_payload — body is nil or empty string
  • :payload_too_large — body exceeds 10MB
  • :invalid_signature — signature does not match
  • :invalid_json — body is not valid JSON

payment_account_transaction_event?(arg1)

@spec payment_account_transaction_event?(map()) :: boolean()

Returns true if the event is for a payment account transaction resource.

payment_event?(arg1)

@spec payment_event?(map()) :: boolean()

Returns true if the event is for a payment resource.

payout_event?(arg1)

@spec payout_event?(map()) :: boolean()

Returns true if the event is for a payout resource.

refund_event?(arg1)

@spec refund_event?(map()) :: boolean()

Returns true if the event is for a refund resource.

scheme_identifier_event?(arg1)

@spec scheme_identifier_event?(map()) :: boolean()

Returns true if the event is for a scheme identifier resource.

subscription_event?(arg1)

@spec subscription_event?(map()) :: boolean()

Returns true if the event is for a subscription resource.

verify(body, signature, secret)

@spec verify(String.t(), String.t() | nil, String.t()) ::
  :ok | {:error, :invalid_signature}

Verifies a webhook signature using HMAC-SHA256.

Returns :ok or {:error, :invalid_signature}. Uses constant-time comparison to prevent timing attacks.