Class: Pay::Webhooks::AbacatepayController

Inherits:
ActionController::API
  • Object
show all
Defined in:
app/controllers/pay/webhooks/abacatepay_controller.rb

Instance Method Summary collapse

Instance Method Details

#createObject

Authenticates a webhook delivery per the official AbacatePay scheme (docs.abacatepay.com/pages/webhooks/security).

AbacatePay combines two complementary mechanisms — neither alone is sufficient, but either is enough to mark a delivery as genuine for this gem (we accept the strongest available):

1. **`webhookSecret` query parameter** — AbacatePay appends the
   per-webhook secret you configured in the dashboard to the URL:
   `?webhookSecret=...`. This authenticates the **origin** because
   only AbacatePay knows your secret. Compared against
   `Pay::Abacatepay.webhook_secret`.

2. **`X-Webhook-Signature` header (HMAC-SHA256, base64)** — protects
   **body integrity**. Computed by AbacatePay over the raw body
   using their fixed `PUBLIC_KEY` (in the SDK as a constant). The
   key is public, so this alone does NOT prove origin — it only
   proves the body wasn't tampered with in transit.

Sandbox compatibility: the current AbacatePay sandbox occasionally delivers ‘webhookSecret` inside the JSON body instead of the URL. We accept that as a third path (compared against the same configured secret) so dev/staging environments work without painel reconfiguration.

The request is rejected with 401 if none of the three pass.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/controllers/pay/webhooks/abacatepay_controller.rb', line 29

def create
  payload = request.body.read

  unless authenticated?(payload)
    return head(:unauthorized)
  end

  event_hash = JSON.parse(payload)
  event_type = event_hash["event"] || event_hash["type"]
  event_id = event_hash["id"] || event_hash.dig("data", "id")

  return head(:ok) if already_recorded?(event_type, event_id)

  queue_event(event_type, event_hash)
  head :ok
rescue JSON::ParserError
  head :bad_request
end