Module: EasyLabs::Webhooks
- Defined in:
- lib/easylabs/webhooks.rb
Overview
Webhook signature verifier and the canonical event-type catalog. Mirrors ‘EasyWebhooks` from `@easylabs/node`.
The Easy API signs every outbound webhook delivery with HMAC-SHA256 over the raw request body using the per-endpoint signing secret (returned only once on ‘client.webhooks.register`). The signature arrives in the `x-easy-webhook-signature` header in the format `sha256=<hex>`.
Constant Summary collapse
- SIGNATURE_PREFIX =
"sha256="- EVENT_TYPES =
Frozen list of every event the API can emit. Mirrors ‘EASY_EVENT_TYPES` in @easylabs/common.
%w[ payment.created payment.updated refund.created refund.updated authorization.created authorization.updated authorization.voided subscription.created subscription.updated subscription.deleted subscription.paused subscription.resumed subscription.trial_will_end subscription.pending_update_applied subscription.pending_update_expired invoice.created invoice.finalized invoice.paid invoice.payment_failed invoice.upcoming invoice.voided invoice.marked_uncollectible revenue_recovery.action_completed coupon.created coupon.updated coupon.deleted promotion_code.created promotion_code.updated promotion_code.deleted identity.created identity.updated settlement.created dispute.created dispute.updated checkout.session.completed checkout.session.crypto_confirmed test.webhook ].freeze
Class Method Summary collapse
-
.construct_event(payload:, signature:, secret:) ⇒ Hash
Verify the signature on a webhook delivery and parse the JSON payload.
- .raise_invalid(code, message) ⇒ Object private
Class Method Details
.construct_event(payload:, signature:, secret:) ⇒ Hash
Verify the signature on a webhook delivery and parse the JSON payload. Raises InvalidRequestError (with a specific ‘code`) when the signature is missing, malformed, or does not match — never returns silently on a bad signature.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/easylabs/webhooks.rb', line 89 def construct_event(payload:, signature:, secret:) if signature.nil? || signature.empty? raise_invalid("WEBHOOK_SIGNATURE_MISSING", "Missing webhook signature header") end unless signature.start_with?(SIGNATURE_PREFIX) raise_invalid( "WEBHOOK_SIGNATURE_FORMAT_INVALID", "Webhook signature must be prefixed with '#{SIGNATURE_PREFIX}'" ) end provided_hex = signature[SIGNATURE_PREFIX.length..] unless provided_hex.match?(/\A[0-9a-f]+\z/i) && provided_hex.length.even? raise_invalid( "WEBHOOK_SIGNATURE_FORMAT_INVALID", "Webhook signature is not valid hex" ) end expected_hex = OpenSSL::HMAC.hexdigest("SHA256", secret.to_s, payload.to_s) provided_bytes = [provided_hex].pack("H*") expected_bytes = [expected_hex].pack("H*") unless provided_bytes.bytesize == expected_bytes.bytesize && OpenSSL.fixed_length_secure_compare(provided_bytes, expected_bytes) raise_invalid( "WEBHOOK_SIGNATURE_MISMATCH", "Webhook signature does not match expected value" ) end begin JSON.parse(payload, symbolize_names: true) rescue JSON::ParserError raise_invalid("WEBHOOK_BODY_INVALID_JSON", "Webhook body is not valid JSON") end end |
.raise_invalid(code, message) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
131 132 133 |
# File 'lib/easylabs/webhooks.rb', line 131 def raise_invalid(code, ) raise EasyLabs::InvalidRequestError.new(, status: 400, code: code) end |