AnedotWebhooks

A mountable Rails engine for receiving Anedot webhook callbacks. Verifies request authenticity via HMAC-SHA256 and publishes events via ActiveSupport::Notifications.

Installation

Add to your Gemfile:

gem "anedot_webhooks"

Configuration

Mount the engine and configure your webhook secret:

# config/routes.rb
mount AnedotWebhooks::Engine, at: "/anedot_webhooks"
# config/initializers/anedot_webhooks.rb
AnedotWebhooks.configure do |config|
  config.webhook_secret = ENV["ANEDOT_WEBHOOK_SECRET"]
end

Set the Anedot webhook URL to https://yourapp.com/anedot_webhooks/events.

Subscribing to events

Catch-all

ActiveSupport::Notifications.subscribe("anedot_webhooks.event") do |*, payload|
  event = payload[:event]  # AnedotWebhooks::Event
  Rails.logger.info "Received #{event.name}: #{event.payload["id"]}"
end

Specific event type

ActiveSupport::Notifications.subscribe("anedot_webhooks.donation_completed") do |*, payload|
  event = payload[:event]
  Donation.record!(event.payload)
end

Available event types

Event Description
submission_created New submission created
submission_pledged Pledge submission created
donation_completed Successful donation processed
donation_ach_returned ACH/check return
donation_chargeback Chargeback initiated
donation_chargeback_reversed Chargeback reversed
donation_partially_refunded Partial refund
donation_refunded Full refund
donation_voided Donation voided
donation_settled Settlement completed
commitment_created New recurring commitment
commitment_updated Commitment modified
commitment_failed_to_process Recurring charge failed

Async handling with ActiveJob

Subclass AnedotWebhooks::WebhookJob and override process:

class HandleDonation < AnedotWebhooks::WebhookJob
  def process(event)
    Donation.create!(event.payload)
  end
end

Then enqueue from a subscriber:

ActiveSupport::Notifications.subscribe("anedot_webhooks.donation_completed") do |*, payload|
  event = payload[:event]
  HandleDonation.perform_later(event.name, event.payload)
end

Security

All requests are verified via HMAC-SHA256 using your webhook secret. Requests with an invalid or missing X-Request-Signature header are rejected with 401 Unauthorized before any processing occurs.

Important: Configure a request body size limit at the web server layer (e.g., client_max_body_size 1m in nginx) to limit exposure to unauthenticated large-payload requests. The gem does not enforce a body size limit.

Development

bundle exec rspec       # tests
bin/rubocop             # lint
bin/rubocop -a          # lint with autocorrect
bin/brakeman            # security scan