Class: PayuPl::Webhooks::Validator

Inherits:
Object
  • Object
show all
Defined in:
lib/payu_pl/webhooks/validator.rb

Overview

Validates PayU webhook signatures and parses payloads

Examples:

Basic usage in Rails controller

result = PayuPl::Webhooks::Validator.new(request).validate_and_parse
if result.success?
  payload = result.data
  # Process payload...
else
  # Handle error: result.error
end

With custom secret key

validator = PayuPl::Webhooks::Validator.new(request, second_key: 'custom_secret')
result = validator.validate_and_parse

With custom logger

validator = PayuPl::Webhooks::Validator.new(request, logger: Logger.new(STDOUT))
result = validator.validate_and_parse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, second_key: nil, logger: nil) ⇒ Validator

Initialize a new webhook validator

Parameters:

  • request (Rack::Request, ActionDispatch::Request) —

    The request object

  • second_key (String, nil) (defaults to: nil) —

    The PayU second key for signature verification Defaults to PayuPl.configuration.second_key or ENV

  • logger (Logger, nil) (defaults to: nil) —

    Optional logger for debugging



36
37
38
39
40
# File 'lib/payu_pl/webhooks/validator.rb', line 36

def initialize(request, second_key: nil, logger: nil)
  @request = request
  @second_key = second_key || fetch_second_key
  @logger = logger
end

Instance Attribute Details

#logger ⇒ Object (readonly)

Returns the value of attribute logger.



28
29
30
# File 'lib/payu_pl/webhooks/validator.rb', line 28

def logger
  @logger
end

#request ⇒ Object (readonly)

Returns the value of attribute request.



28
29
30
# File 'lib/payu_pl/webhooks/validator.rb', line 28

def request
  @request
end

#second_key ⇒ Object (readonly)

Returns the value of attribute second_key.



28
29
30
# File 'lib/payu_pl/webhooks/validator.rb', line 28

def second_key
  @second_key
end

Instance Method Details

#parse_payload ⇒ Hash

Parses the webhook payload

Returns:

  • (Hash) —

    The parsed JSON payload



105
106
107
108
109
# File 'lib/payu_pl/webhooks/validator.rb', line 105

def parse_payload
  raw_payload = read_body
  log_raw_payload(raw_payload)
  JSON.parse(raw_payload)
end

#validate_and_parse ⇒ PayuPl::Webhooks::Result

Validates the webhook signature and parses the payload

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/payu_pl/webhooks/validator.rb', line 45

def validate_and_parse
  log_header
  
  begin
    verify_signature!
    log_signature_passed
    
    payload = parse_payload
    log_payload_parsed(payload)
    
    Result.success(payload)
  rescue StandardError => e
    log_error(e)
    Result.failure(e.message)
  ensure
    log_footer
  end
end

#verify_signature! ⇒ Boolean

Validates only the signature without parsing the payload

Returns:

  • (Boolean) —

    true if signature is valid

Raises:

  • (RuntimeError) —

    if signature is invalid or missing



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/payu_pl/webhooks/validator.rb', line 68

def verify_signature!
  header = request.env['HTTP_OPENPAYU_SIGNATURE']
  raise 'Missing OpenPayU signature header' unless header

  log_signature_header(header)

  signature_parts = parse_signature_header(header)
  incoming_signature = signature_parts['signature']
  algorithm = (signature_parts['algorithm'] || 'SHA256').downcase

  body = read_body

  expected_signature = compute_expected_signature(algorithm, body)

  log_signature_comparison(algorithm, incoming_signature, expected_signature)

  # For MD5, PayU might use either body+key or key+body
  # Try both approaches
  if algorithm == 'md5'
    alternative_signature = Digest::MD5.hexdigest(second_key + body)
    
    unless secure_compare(expected_signature, incoming_signature) ||
           secure_compare(alternative_signature, incoming_signature)
      raise "Signature verification failed for algorithm #{algorithm}"
    end
  else
    unless secure_compare(expected_signature, incoming_signature)
      raise "Signature verification failed for algorithm #{algorithm}"
    end
  end

  true
end