checkout_sdk 2.8.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26bd6c4871771d8d22c647b6724c577af0434c881b9af1433959cb1e4fdf98c8
4
- data.tar.gz: b18c0c03cef39d44080aadbf798f76910c87fc84506ecb4d6dfcb9b6c67a5d76
3
+ metadata.gz: 9849ed54d294ed9bea5fdb04bb5378fb55b942a2adb0ffe0653cb5c628cc0882
4
+ data.tar.gz: 7bd35374ce13e3feb72eb37a16930316ab6a01ce2cc10c49245f3e5a2daf6229
5
5
  SHA512:
6
- metadata.gz: abbd929efceca48389c89b804075ee1c49a18a2a99532e03445ce061221a69c07545dbd1271cbb209435ca60da1209ad3c30c585d35b15a1db1d29832023fee8
7
- data.tar.gz: 2bb659ec58fa5022fd17278f9d4467ab9f18b8ca47f657240c2d8da0b76724bc13d136ab865d68d90d01b81791e2a32fb886258eaa3d9edc34f3f90b5a7fcf38
6
+ metadata.gz: b6f8c31a1c66aecb8b8fad6345a2b693f133b8bee59376920445caa7779db02a3f18882170318870c5f86fb728d898a4baffc32a0b71159d8b1fa4e17087c144
7
+ data.tar.gz: 6bdaf1a3c38c96bcafe0f52135ff77edda7a51d66946df1bc7d4b4d8e9e5729bf85f017e0a3a755567466e9132b4464428b7861ee58e56040fbf74294bc97598
data/README.md CHANGED
@@ -172,6 +172,10 @@ api = CheckoutSdk.builder
172
172
  .build
173
173
  ```
174
174
 
175
+ If your connection uses the `raise_error` middleware, configure it as the default client does:
176
+ `conn.response :raise_error, include_request: false` (the option needs Faraday 2.7.11 or later;
177
+ the SDK leaves the request data out of the errors it raises in any case).
178
+
175
179
  You don't need to specify the URL for Faraday constructor the SDK grabs the belong URI from `CheckoutSdk::Environment`
176
180
  however if you want to
177
181
  use specific URI's without a proxy you can create the `Environment` object as follows.
@@ -52,10 +52,12 @@ module CheckoutSdk
52
52
  json_body = CheckoutSdk::JsonSerializer.to_custom_hash(body).to_json
53
53
 
54
54
  begin
55
- @log.info "#{method}: /#{path}"
55
+ @log.info "#{method}: /#{CheckoutUtils.loggable_path(path)}"
56
56
  response = @client.run_request(method, path, json_body, headers)
57
57
  rescue Faraday::ClientError => e
58
- raise CheckoutApiException, e.response
58
+ raise CheckoutApiException.new(e.response), cause: nil
59
+ rescue Faraday::Error => e
60
+ raise CheckoutUtils.without_request_data(e), cause: nil
59
61
  end
60
62
 
61
63
  parse_response(response)
@@ -140,10 +142,12 @@ module CheckoutSdk
140
142
  form = build_multipart_request(file_request, file)
141
143
 
142
144
  begin
143
- @log.info "post: /#{path}"
145
+ @log.info "post: /#{CheckoutUtils.loggable_path(path)}"
144
146
  response = @multipart_client.run_request(:post, path, form, headers)
145
147
  rescue Faraday::ClientError => e
146
- raise CheckoutApiException, e.response
148
+ raise CheckoutApiException.new(e.response), cause: nil
149
+ rescue Faraday::Error => e
150
+ raise CheckoutUtils.without_request_data(e), cause: nil
147
151
  end
148
152
 
149
153
  parse_response(response)
@@ -21,11 +21,86 @@ module CheckoutSdk
21
21
  http_metadata
22
22
  end
23
23
 
24
+ MAX_MESSAGE_DETAILS_LENGTH = 256
25
+ private_constant :MAX_MESSAGE_DETAILS_LENGTH
26
+
27
+ # Returns the request path in a form safe for a single log line: the query string is left out
28
+ # and control characters are escaped.
29
+ #
30
+ # @param [String, nil] path
31
+ # @return [String]
32
+ def self.loggable_path(path)
33
+ escape_control_characters(utf8_text(path).split('?', 2).first.to_s)
34
+ end
35
+
36
+ # Builds an exception message from a prefix and a response body. A JSON object body adds its
37
+ # "key: value" pairs as details; any other non-empty body is added as is only when
38
+ # include_text_body is true. Details are shortened and their control characters escaped.
39
+ #
40
+ # @param [String] prefix
41
+ # @param [String, nil] body
42
+ # @param [Boolean] include_text_body
43
+ # @return [String]
44
+ def self.error_message(prefix, body, include_text_body: false)
45
+ text = utf8_text(body).strip
46
+ parsed = json_object(text)
47
+ details = parsed&.map { |key, value| "#{key}: #{value}" }&.join(', ')
48
+ details = text if details.nil? && include_text_body
49
+ return prefix if details.nil? || details.empty?
50
+
51
+ details = "#{details[0, MAX_MESSAGE_DETAILS_LENGTH]}..." if details.length > MAX_MESSAGE_DETAILS_LENGTH
52
+ "#{prefix} Details: #{escape_control_characters(details)}."
53
+ end
54
+
55
+ # Returns a copy of the Faraday error, of the same class and with the same backtrace and wrapped
56
+ # exception, that keeps only the response status, headers and body and whose message names the
57
+ # method and URL without the query string. Errors without a response are returned unchanged.
58
+ #
59
+ # @param [Faraday::Error] error
60
+ # @return [Faraday::Error]
61
+ def self.without_request_data(error)
62
+ return error if error.response.nil?
63
+
64
+ metadata = map_to_http_metadata(error.response)
65
+ rebuilt = error.exception(response_error_message(metadata.status_code, error.response))
66
+ rebuilt.instance_variable_set(:@response,
67
+ { status: metadata.status_code, headers: metadata.headers, body: metadata.body })
68
+ rebuilt
69
+ end
70
+
71
+ def self.response_error_message(status, response)
72
+ message = "the server responded with status #{status}"
73
+ request = response.is_a?(Hash) ? response[:request] : nil
74
+ return message unless request.is_a?(Hash) && request[:url]
75
+
76
+ "#{message} for #{request[:method].to_s.upcase} #{loggable_path(request[:url].to_s)}"
77
+ end
78
+ private_class_method :response_error_message
79
+
80
+ def self.json_object(text)
81
+ parsed = JSON.parse(text)
82
+ parsed.is_a?(Hash) ? parsed : nil
83
+ rescue JSON::ParserError
84
+ nil
85
+ end
86
+ private_class_method :json_object
87
+
88
+ # Escapes control characters and Unicode line and paragraph separators in UTF-8 text.
89
+ def self.escape_control_characters(text)
90
+ text.gsub(/[[:cntrl:]\p{Zl}\p{Zp}]/) { |char| char.dump[1..-2] }
91
+ end
92
+ private_class_method :escape_control_characters
93
+
94
+ def self.utf8_text(text)
95
+ text.to_s.dup.force_encoding(Encoding::UTF_8).scrub
96
+ end
97
+ private_class_method :utf8_text
98
+
24
99
  # @return [Faraday::Connection]
25
100
  def self.build_multipart_client
26
101
  Faraday.new do |f|
27
102
  f.request :multipart
28
- f.response :raise_error
103
+ f.response :raise_error, include_request: false
29
104
  end
30
105
  end
31
106
 
@@ -33,7 +108,7 @@ module CheckoutSdk
33
108
  def self.build_default_client
34
109
  Faraday.new do |f|
35
110
  f.response :follow_redirects
36
- f.response :raise_error
111
+ f.response :raise_error, include_request: false
37
112
  end
38
113
  end
39
114
  end
@@ -6,6 +6,16 @@ module CheckoutSdk
6
6
  class CheckoutArgumentException < CheckoutException; end
7
7
 
8
8
  class CheckoutAuthorizationException < CheckoutException
9
+ # @return [CheckoutSdk::HttpMetadata, nil] response status, headers and body when raised for an OAuth token response
10
+ attr_reader :http_metadata
11
+
12
+ # @param [String, nil] message
13
+ # @param [CheckoutSdk::HttpMetadata, nil] http_metadata
14
+ def initialize(message = nil, http_metadata = nil)
15
+ @http_metadata = http_metadata
16
+ super(message)
17
+ end
18
+
9
19
  def self.invalid_authorization(authorization_type)
10
20
  new("Operation requires #{authorization_type} authorization type.")
11
21
  end
@@ -13,6 +23,22 @@ module CheckoutSdk
13
23
  def self.invalid_key(key_type)
14
24
  new("#{key_type} is required for this operation.")
15
25
  end
26
+
27
+ # Builds the error for an unsuccessful OAuth token response from the response data only.
28
+ #
29
+ # @param [CheckoutSdk::HttpMetadata] http_metadata
30
+ # @return [CheckoutSdk::CheckoutAuthorizationException]
31
+ def self.token_request_failed(http_metadata)
32
+ prefix = "The OAuth token response status code (#{http_metadata.status_code}) does not indicate success."
33
+ new(CheckoutUtils.error_message(prefix, http_metadata.body, include_text_body: true), http_metadata)
34
+ end
35
+
36
+ # Builds the error for an OAuth token request that failed without a response.
37
+ #
38
+ # @return [CheckoutSdk::CheckoutAuthorizationException]
39
+ def self.token_request_without_response
40
+ new('The OAuth token request failed without a response.')
41
+ end
16
42
  end
17
43
 
18
44
  class CheckoutApiException < CheckoutException
@@ -35,12 +61,8 @@ module CheckoutSdk
35
61
  end
36
62
 
37
63
  def build_error_message
38
- message = "The API response status code (#{http_metadata.status_code}) does not indicate success."
39
- if @error_details && !@error_details.to_h.empty?
40
- details = @error_details.to_h.map { |key, value| "#{key}: #{value}" }.join(', ')
41
- message += " Details: #{details}."
42
- end
43
- message
64
+ prefix = "The API response status code (#{http_metadata.status_code}) does not indicate success."
65
+ CheckoutUtils.error_message(prefix, http_metadata.body)
44
66
  end
45
67
  end
46
68
  end
@@ -20,7 +20,7 @@ module CheckoutSdk
20
20
 
21
21
  # @param [String,nil] version
22
22
  def retrieve_all_event_types(version = nil)
23
- query = "version=#{version}" unless version.nil?
23
+ query = URI.encode_www_form(version: version) unless version.nil?
24
24
  api_client.invoke_get(EVENT_TYPES, sdk_authorization, query)
25
25
  end
26
26
 
@@ -65,17 +65,7 @@ module CheckoutSdk
65
65
  scope: @scopes.join(' ')
66
66
  }
67
67
 
68
- headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
69
- body = URI.encode_www_form data
70
-
71
- begin
72
- @log.info "post: #{authorization_uri}"
73
- response = @oauth_http_client.run_request(:post, @authorization_uri, body, headers)
74
- rescue Faraday::ClientError => e
75
- raise CheckoutAuthorizationException, e.response
76
- rescue Faraday::ConnectionFailed => e
77
- raise CheckoutArgumentException, e.wrapped_exception
78
- end
68
+ response = request_access_token(URI.encode_www_form(data))
79
69
 
80
70
  if !response.body.nil? && response.body != ''
81
71
  oauth_response = JSON.parse(response.body, object_class: OpenStruct)
@@ -90,6 +80,23 @@ module CheckoutSdk
90
80
 
91
81
  private
92
82
 
83
+ # @param [String] body
84
+ # @return [Faraday::Response]
85
+ def request_access_token(body)
86
+ headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
87
+ @log.info "post: #{CheckoutUtils.loggable_path(authorization_uri)}"
88
+ @oauth_http_client.run_request(:post, @authorization_uri, body, headers)
89
+ rescue Faraday::ClientError => e
90
+ raise CheckoutAuthorizationException.token_request_without_response if e.response.nil?
91
+
92
+ raise CheckoutAuthorizationException.token_request_failed(CheckoutUtils.map_to_http_metadata(e.response)),
93
+ cause: nil
94
+ rescue Faraday::ConnectionFailed => e
95
+ raise CheckoutArgumentException, e.wrapped_exception
96
+ rescue Faraday::Error => e
97
+ raise CheckoutUtils.without_request_data(e), cause: nil
98
+ end
99
+
93
100
  # @param [String] client_id
94
101
  # @param [String] client_secret
95
102
  # @param [Environment] environment
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CheckoutSdk
4
- VERSION = '2.8.0'
4
+ VERSION = '2.8.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkout_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Checkout
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-11-02 00:00:00.000000000 Z
11
+ date: 2026-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.60'
69
+ - !ruby/object:Gem::Dependency
70
+ name: csv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: faraday
71
85
  requirement: !ruby/object:Gem::Requirement