Class: GoCardlessPro::Webhook

Inherits:
Object
  • Object
show all
Defined in:
lib/gocardless_pro/webhook.rb

Defined Under Namespace

Classes: InvalidSignatureError

Class Method Summary collapse

Class Method Details

.parse(options = {}) ⇒ Array<GoCardlessPro::Resources::Event>

Validates that a webhook was genuinely sent by GoCardless using .signature_valid?, and then parses it into an array of GoCardlessPro::Resources::Event objects representing each event included in the webhook

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :request_body (String)

    the request body

  • :signature_header (String)

    the signature included in the request, found in the Webhook-Signature header

  • :webhook_endpoint_secret (String)

    the webhook endpoint secret for your webhook endpoint, as configured in your GoCardless Dashboard

Returns:

Raises:

  • (InvalidSignatureError)

    if the signature header specified does not match the signature computed using the request body and webhook endpoint secret

  • (ArgumentError)

    if a required keyword argument is not provided or is not of the required type



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/gocardless_pro/webhook.rb', line 36

def parse(options = {})
  validate_options!(options)

  unless signature_valid?(request_body: options[:request_body],
                          signature_header: options[:signature_header],
                          webhook_endpoint_secret: options[:webhook_endpoint_secret])
    raise InvalidSignatureError, "This webhook doesn't appear to be a genuine " \
                                  'webhook from GoCardless, because the signature ' \
                                  "header doesn't match the signature computed" \
                                  ' with your webhook endpoint secret.'
  end

  events = JSON.parse(options[:request_body])['events']

  events.map { |event| Resources::Event.new(event) }
end

.parse_with_meta(options = {}) ⇒ WebhookParseResult

Validates that a webhook was genuinely sent by GoCardless using .signature_valid?, and then parses it into a WebhookParseResult containing both the events and the webhook ID from the meta field

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :request_body (String)

    the request body

  • :signature_header (String)

    the signature included in the request, found in the Webhook-Signature header

  • :webhook_endpoint_secret (String)

    the webhook endpoint secret for your webhook endpoint, as configured in your GoCardless Dashboard

Returns:

Raises:

  • (InvalidSignatureError)

    if the signature header specified does not match the signature computed using the request body and webhook endpoint secret

  • (ArgumentError)

    if a required keyword argument is not provided or is not of the required type



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gocardless_pro/webhook.rb', line 67

def parse_with_meta(options = {})
  validate_options!(options)

  unless signature_valid?(request_body: options[:request_body],
                          signature_header: options[:signature_header],
                          webhook_endpoint_secret: options[:webhook_endpoint_secret])
    raise InvalidSignatureError, "This webhook doesn't appear to be a genuine " \
                                  'webhook from GoCardless, because the signature ' \
                                  "header doesn't match the signature computed" \
                                  ' with your webhook endpoint secret.'
  end

  parsed = JSON.parse(options[:request_body])
  events = parsed['events'].map { |event| Resources::Event.new(event) }
  webhook_id = parsed.dig('meta', 'webhook_id')

  WebhookParseResult.new(events, webhook_id)
end

.signature_valid?(options = {}) ⇒ Boolean

Validates that a webhook was genuinely sent by GoCardless by computing its signature using the body and your webhook endpoint secret, and comparing that with the signature included in the Webhook-Signature header

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :request_body (String)

    the request body

  • :signature_header (String)

    the signature included in the request, found in the Webhook-Signature header

  • :webhook_endpoint_secret (String)

    the webhook endpoint secret for your webhook endpoint, as configured in your GoCardless Dashboard

Returns:

  • (Boolean)

    whether the webhook's signature is valid

Raises:

  • (ArgumentError)

    if a required keyword argument is not provided or is not of the required type



98
99
100
101
102
103
104
105
106
# File 'lib/gocardless_pro/webhook.rb', line 98

def signature_valid?(options = {})
  validate_options!(options)

  computed_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
                                               options[:webhook_endpoint_secret],
                                               options[:request_body])

  secure_compare(options[:signature_header], computed_signature)
end