Class: ClientHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/amazon-pay-api-sdk-ruby/client_helper.rb

Overview

ClientHelper class provides utility functions for API interactions

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ ClientHelper

Initialize with configuration settings



13
14
15
16
17
18
19
20
21
22
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 13

def initialize(config)
  validate_config(config)
  @region = fetch(:region, config)
  @public_key_id = fetch(:public_key_id, config) 
  @private_key = fetch(:private_key, config)
  @amazon_signature_algorithm = Constants::AMAZON_SIGNATURE_ALGORITHM 
  @salt_length = 32 
  environment = determine_environment(config)
  @base_url = "https://#{endpoint}/#{environment}/#{Constants::API_VERSION}/"
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



10
11
12
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 10

def base_url
  @base_url
end

Instance Method Details

#authorization_header(signed_headers) ⇒ Object

Build authorization header from signed headers



117
118
119
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 117

def authorization_header(signed_headers)
  "#{@amazon_signature_algorithm} PublicKeyId=#{@public_key_id}, #{signed_headers}"
end

#build_canonical_request(method, uri, query, canonical_headers, payload) ⇒ Object

Build the canonical request string



100
101
102
103
104
105
106
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 100

def build_canonical_request(method, uri, query, canonical_headers, payload)
  headers_string = canonical_headers.map { |k, v| "#{k}:#{v}" }.join("\n")
  signed_headers = canonical_headers.keys.join(';')
  hashed_payload = hex_and_hash(payload)

  "#{method}\n#{uri.path}\n#{query}\n#{headers_string}\n\n#{signed_headers}\n#{hashed_payload}"
end

#build_uri(url_fragment, query) ⇒ Object

Build the full URI for the API request



157
158
159
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 157

def build_uri(url_fragment, query)
  URI.parse("#{@base_url}#{url_fragment}#{query.empty? ? '' : "?#{query}"}")
end

#canonicalize_headers(headers) ⇒ Object

Canonicalize headers by converting keys to lowercase and sorting them



95
96
97
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 95

def canonicalize_headers(headers)
  headers.transform_keys(&:downcase).sort.to_h
end

#create_request(method, uri, payload) ⇒ Object

AmazonPayClient dependency methods Create a new HTTP request



138
139
140
141
142
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 138

def create_request(method, uri, payload)
  request = http_method(method).new(uri)
  request.body = payload.is_a?(String) ? payload : JSON.generate(payload)
  request
end

#determine_environment(config) ⇒ Object

Determine the environment based on the public key or the config setting



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 25

def determine_environment(config)
  @live = Constants::LIVE[0...-1].downcase
  @sandbox = Constants::SANDBOX[0...-1].downcase
  if @public_key_id.start_with?(Constants::LIVE)
    :live
  elsif @public_key_id.start_with?(Constants::SANDBOX)
    :sandbox
  else
    fetch(:sandbox, config) ? :sandbox : :live
  end
end

#endpointObject

Get endpoint URL based on region



43
44
45
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 43

def endpoint
  Constants::API_ENDPOINTS[@region] || raise(ArgumentError, "Unknown region: '#{@region}'. Valid regions are: #{Constants::API_ENDPOINTS.keys.join(', ')}.")
end

#fetch(key, config) ⇒ Object

Fetch value from config hash



38
39
40
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 38

def fetch(key, config)
  config[key] || config[key.to_s]
end

#formatted_timestampObject

Format the current timestamp



127
128
129
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 127

def formatted_timestamp
  Time.now.utc.iso8601.delete(':-')
end

#hex_and_hash(data) ⇒ Object

Compute SHA256 hash of the given data



122
123
124
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 122

def hex_and_hash(data)
  Digest::SHA256.hexdigest(data)
end

#http_method(method) ⇒ Object

Get HTTP method object based on method string



48
49
50
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 48

def http_method(method)
  Constants::METHOD_TYPES[method] || raise(ArgumentError, "Unknown HTTP method: '#{method}'. Valid methods are: #{Constants::METHOD_TYPES.keys.join(', ')}.")
end

#normalize_headers(headers) ⇒ Object

Normalize headers by converting keys to strings and stripping values



90
91
92
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 90

def normalize_headers(headers)
  headers.transform_keys(&:to_s).transform_values(&:strip)
end

#prepare_headers(user_headers, uri, payload) ⇒ Object

Prepare headers for the API request



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 77

def prepare_headers(user_headers, uri, payload)
  headers = normalize_headers(user_headers)
  headers[Constants::ACCEPT] = headers[Constants::CONTENT_TYPE] = Constants::APPLICATION_JSON
  headers[Constants::X_AMZ_PAY_REGION] = @region
  headers[Constants::X_AMZ_PAY_DATE] = formatted_timestamp
  headers[Constants::X_AMZ_PAY_HOST] = uri.host
  headers[Constants::CONTENT_LENGTH] = payload.bytesize.to_s unless payload.empty?
  headers[Constants::X_AMZ_PAY_SDK_TYPE] = Constants::SDK_TYPE
  headers[Constants::X_AMZ_PAY_SDK_VERSION] = Constants::SDK_VERSION
  headers
end

#send_request(uri, request) ⇒ Object

Send the HTTP request



150
151
152
153
154
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 150

def send_request(uri, request)
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == Constants::HTTPS) do |http|
    http.request(request)
  end
end

#set_request_headers(request, signed_headers) ⇒ Object

Set headers for the HTTP request



145
146
147
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 145

def set_request_headers(request, signed_headers)
  signed_headers.each { |k, v| request[k] = v }
end

#sign(string_to_sign) ⇒ Object

Sign the given string using the private key



65
66
67
68
69
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 65

def sign(string_to_sign)
  hashed_request = "#{@amazon_signature_algorithm}\n#{hex_and_hash(string_to_sign)}"
  rsa = OpenSSL::PKey::RSA.new(@private_key)
  Base64.strict_encode64(rsa.sign_pss(Constants::HASH_ALGORITHM, hashed_request, salt_length: @salt_length, mgf1_hash: Constants::HASH_ALGORITHM))
end

#sign_headers(canonical_request, canonical_headers) ⇒ Object

Sign the canonical request headers



109
110
111
112
113
114
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 109

def sign_headers(canonical_request, canonical_headers)
  hashed_request = "#{@amazon_signature_algorithm}\n#{hex_and_hash(canonical_request)}"
  rsa = OpenSSL::PKey::RSA.new(@private_key)
  signature = Base64.strict_encode64(rsa.sign_pss(Constants::HASH_ALGORITHM, hashed_request, salt_length: @salt_length, mgf1_hash: Constants::HASH_ALGORITHM))
  "SignedHeaders=#{canonical_headers.keys.join(';')}, Signature=#{signature}"
end

#signed_headers(method, uri, payload, user_headers, query) ⇒ Object

Generate signed headers for the API request



53
54
55
56
57
58
59
60
61
62
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 53

def signed_headers(method, uri, payload, user_headers, query)
  headers = prepare_headers(user_headers, uri, payload) 
  canonical_headers = canonicalize_headers(headers) 
  canonical_request = build_canonical_request(method, uri, query, canonical_headers, payload) 
  signed_headers = sign_headers(canonical_request, canonical_headers)
  
  # Add authorization header
  headers[Constants::AUTHORIZATION] = authorization_header(signed_headers) 
  headers
end

#to_query(query_params) ⇒ Object

Convert query parameters to URL query string



72
73
74
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 72

def to_query(query_params)
  URI.encode_www_form(query_params.sort.to_h)
end

#url_encode(value) ⇒ Object

URL encode the given value



132
133
134
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 132

def url_encode(value)
  URI.encode_www_form_component(value).gsub('%7E', '~')
end

#validate_config(config) ⇒ Object

This method checks if all required configuration keys are present in the given config. If any required key is missing, it raises a StandardError with a message listing the missing keys.



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/amazon-pay-api-sdk-ruby/client_helper.rb', line 163

def validate_config(config)
  # Define the list of required keys.
  required_keys = %i[region public_key_id private_key]

  # Identify which required keys are missing from the config hash.
  missing_keys = required_keys.select { |key| config[key].nil? }

  # If there are missing keys, raise an error with a descriptive message.
  unless missing_keys.empty?
    raise StandardError, "Missing required config keys: #{missing_keys.join(', ')}"
  end
end