Class: Malipopay::Webhooks::Verifier

Inherits:
Object
  • Object
show all
Defined in:
lib/malipopay/webhooks/verifier.rb

Constant Summary collapse

TOLERANCE_IN_SECONDS =

5 minutes

300

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(secret) ⇒ Verifier

Returns a new instance of Verifier.



11
12
13
# File 'lib/malipopay/webhooks/verifier.rb', line 11

def initialize(secret)
  @secret = secret
end

Class Method Details

.sign(payload, secret, timestamp: nil) ⇒ String

Generate an HMAC signature for a payload

Parameters:

  • payload (String)

    Raw payload string

  • secret (String)

    Webhook secret

  • timestamp (String, nil) (defaults to: nil)

    Optional timestamp to include in signature

Returns:

  • (String)

    Hex-encoded HMAC-SHA256 signature



51
52
53
54
# File 'lib/malipopay/webhooks/verifier.rb', line 51

def self.sign(payload, secret, timestamp: nil)
  signed_payload = timestamp ? "#{timestamp}.#{payload}" : payload
  OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload)
end

Instance Method Details

#construct_event(payload, signature, timestamp: nil) ⇒ Hash

Verify and parse a webhook event

Parameters:

  • payload (String)

    Raw request body

  • signature (String)

    Signature from header

  • timestamp (String, nil) (defaults to: nil)

    Timestamp from header

Returns:

  • (Hash)

    Parsed event data

Raises:



38
39
40
41
42
43
44
# File 'lib/malipopay/webhooks/verifier.rb', line 38

def construct_event(payload, signature, timestamp: nil)
  unless verify(payload, signature, timestamp: timestamp)
    raise Malipopay::AuthenticationError.new("Invalid webhook signature")
  end

  JSON.parse(payload)
end

#verify(payload, signature, timestamp: nil) ⇒ Boolean

Verify a webhook signature

Parameters:

  • payload (String)

    Raw request body

  • signature (String)

    Signature from X-Malipopay-Signature header

  • timestamp (String, nil) (defaults to: nil)

    Timestamp from X-Malipopay-Timestamp header

Returns:

  • (Boolean)

    Whether the signature is valid



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/malipopay/webhooks/verifier.rb', line 20

def verify(payload, signature, timestamp: nil)
  return false if signature.nil? || signature.empty?

  if timestamp
    ts = timestamp.to_i
    return false if (Time.now.to_i - ts).abs > TOLERANCE_IN_SECONDS
  end

  expected = self.class.sign(payload, @secret, timestamp: timestamp)
  secure_compare(expected, signature)
end