yield-sdk 0.5.1 → 0.7.0

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +51 -0
  3. data/README.md +25 -10
  4. data/Rakefile +11 -2
  5. data/Steepfile +20 -0
  6. data/docs/index.md +12 -0
  7. data/docs/modules/customer.md +64 -0
  8. data/docs/modules/order.md +93 -0
  9. data/docs/modules/self.md +48 -0
  10. data/lib/yield/sdk/api/api_client.rb +54 -37
  11. data/lib/yield/sdk/api/api_error.rb +39 -0
  12. data/lib/yield/sdk/api/api_error_details.rb +9 -0
  13. data/lib/yield/sdk/api/api_result.rb +23 -6
  14. data/lib/yield/sdk/base_client.rb +6 -3
  15. data/lib/yield/sdk/client.rb +7 -4
  16. data/lib/yield/sdk/modules/customer/customer_base_client.rb +27 -0
  17. data/lib/yield/sdk/modules/customer/customer_client.rb +19 -0
  18. data/lib/yield/sdk/modules/customer/payloads/customer_credit_line_info.rb +22 -0
  19. data/lib/yield/sdk/modules/customer/payloads/customer_list_payload.rb +20 -0
  20. data/lib/yield/sdk/modules/customer/payloads/customer_row.rb +28 -0
  21. data/lib/yield/sdk/modules/order/order_base_client.rb +4 -1
  22. data/lib/yield/sdk/modules/order/payloads/order.rb +21 -11
  23. data/lib/yield/sdk/modules/order/payloads/order_create_payload.rb +1 -1
  24. data/lib/yield/sdk/modules/order/payloads/order_customer_info.rb +11 -5
  25. data/lib/yield/sdk/modules/order/payloads/order_status.rb +1 -1
  26. data/lib/yield/sdk/modules/self/payloads/self_info.rb +9 -5
  27. data/lib/yield/sdk/modules/self/payloads/self_organization_info.rb +9 -5
  28. data/lib/yield/sdk/types/cursor_payload.rb +19 -0
  29. data/lib/yield/sdk/types/money.rb +6 -3
  30. data/lib/yield/sdk/types/money_payload.rb +7 -3
  31. data/lib/yield/sdk/types/page.rb +51 -0
  32. data/lib/yield/sdk/utils/type_utils.rb +71 -0
  33. data/lib/yield/sdk/version.rb +3 -3
  34. data/lib/yield/sdk.rb +2 -1
  35. data/sig/vendor/faraday.rbs +23 -0
  36. data/sig/yield/sdk/api/api_client.rbs +26 -0
  37. data/sig/yield/sdk/api/api_error.rbs +13 -0
  38. data/sig/yield/sdk/api/api_error_details.rbs +14 -0
  39. data/sig/yield/sdk/api/api_result.rbs +19 -0
  40. data/sig/yield/sdk/base_client.rbs +13 -0
  41. data/sig/yield/sdk/client.rbs +13 -0
  42. data/sig/yield/sdk/modules/customer/customer_base_client.rbs +11 -0
  43. data/sig/yield/sdk/modules/customer/customer_client.rbs +11 -0
  44. data/sig/yield/sdk/modules/customer/payloads/customer_credit_line_info.rbs +13 -0
  45. data/sig/yield/sdk/modules/customer/payloads/customer_list_payload.rbs +16 -0
  46. data/sig/yield/sdk/modules/customer/payloads/customer_row.rbs +16 -0
  47. data/sig/yield/sdk/modules/order/payloads/order.rbs +30 -0
  48. data/sig/yield/sdk/modules/order/payloads/order_create_payload.rbs +15 -0
  49. data/sig/yield/sdk/modules/order/payloads/order_customer_info.rbs +15 -0
  50. data/sig/yield/sdk/modules/order/payloads/order_status.rbs +10 -0
  51. data/sig/yield/sdk/modules/order/self_base_client.rbs +12 -0
  52. data/sig/yield/sdk/modules/order/self_client.rbs +12 -0
  53. data/sig/yield/sdk/modules/self/payloads/self_info.rbs +14 -0
  54. data/sig/yield/sdk/modules/self/payloads/self_organization_info.rbs +14 -0
  55. data/sig/yield/sdk/modules/self/self_base_client.rbs +11 -0
  56. data/sig/yield/sdk/modules/self/self_client.rbs +11 -0
  57. data/sig/yield/sdk/types/cursor_payload.rbs +14 -0
  58. data/sig/yield/sdk/types/money.rbs +12 -0
  59. data/sig/yield/sdk/types/money_payload.rbs +15 -0
  60. data/sig/yield/sdk/types/page.rbs +20 -0
  61. data/sig/yield/sdk/utils/type_utils.rbs +26 -0
  62. data/sig/yield/sdk/version.rbs +8 -0
  63. data/sig/yield/sdk.rbs +0 -2
  64. metadata +66 -7
  65. data/.standard.yml +0 -3
  66. data/lib/yield/sdk/utils.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af8b2c1dec3366d6eeb12c8fc24e583bae64b1268b511b4c920e654e3b9a95df
4
- data.tar.gz: 388ab9535c4a2147654b26c0b4f52f51adf2d00f7c545d67bbb36db1c8b2d5d6
3
+ metadata.gz: edf5ef23bf25b6d5b424ce3f6e9d0a3a40b045176901bf2de01078b86d34cce6
4
+ data.tar.gz: 8fa6343f723d3f4051f15b5e606e887775f819f0889438e6866fca59bdbb4e58
5
5
  SHA512:
6
- metadata.gz: 6e693cd40f47786c4e2d974069ae1ac129ee1df334a7983b339f8b587082f6b6e569cf830b9d64da8b1a5de9eda4018cf1823bdb5c6ff1365e53a5e4109ef792
7
- data.tar.gz: 7d116dd36ce3fbe42be6a01f883bf810110f148a55ba7fd9e80b7fe5640c74a205ff9efaa0dc16eb39f7c425b3a27808f9c3cf0a1b13416400514b810c245cec
6
+ metadata.gz: 74d0b5a43f40bd586ebc345c7b8590c48bfd97d4c684236868516260f626efe78f25c73b2f21bf7ab6f152d5dc83f2fae7ad2e44b8d17f1ac5ea36d8df3daf19
7
+ data.tar.gz: 300efc9e709e883246445d924e004517408ab263e122b1cf46384e74228cca88623a23c5b92ea5adb2f973c36de97ffd0949b1ad65be9ad6919ae8d2b21a74b2
data/.rubocop.yml ADDED
@@ -0,0 +1,51 @@
1
+ plugins:
2
+ - rubocop-minitest
3
+ - rubocop-rake
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 3.0
7
+ NewCops: enable
8
+
9
+ Gemspec/RequireMFA:
10
+ Enabled: false # FIXME
11
+
12
+ Style/AccessorGrouping:
13
+ Enabled: false
14
+
15
+ Style/Documentation:
16
+ Enabled: false
17
+
18
+ Style/MultipleComparison:
19
+ Enabled: false
20
+
21
+ Style/StringLiterals:
22
+ Enabled: true
23
+ EnforcedStyle: double_quotes
24
+
25
+ Style/StringLiteralsInInterpolation:
26
+ Enabled: true
27
+ EnforcedStyle: double_quotes
28
+
29
+ Style/TrailingCommaInArguments:
30
+ Enabled: true
31
+ EnforcedStyleForMultiline: comma
32
+
33
+ Style/TrailingCommaInArrayLiteral:
34
+ Enabled: true
35
+ EnforcedStyleForMultiline: comma
36
+
37
+ Style/TrailingCommaInHashLiteral:
38
+ Enabled: true
39
+ EnforcedStyleForMultiline: comma
40
+
41
+ Layout/LineLength:
42
+ Enabled: false
43
+
44
+ Metrics/AbcSize:
45
+ Enabled: false
46
+
47
+ Metrics/ClassLength:
48
+ Enabled: false
49
+
50
+ Metrics/MethodLength:
51
+ Enabled: false
data/README.md CHANGED
@@ -1,27 +1,42 @@
1
- # The official Yield SDK for Ruby
1
+ Yield SDK for Ruby [![Gem Version](https://img.shields.io/gem/v/yield-sdk)](https://rubygems.org/gems/yield-sdk)
2
+ ==================
2
3
 
3
- ## Installation
4
+ The official [Yield](https://www.paywithyield.com) SDK for Ruby.
5
+
6
+
7
+ Documentation
8
+ -------------
9
+
10
+ - [API reference](https://github.com/yield-tech/sdk-ruby/blob/main/docs/index.md)
11
+
12
+
13
+ Installation
14
+ ------------
4
15
 
5
16
  ```sh
6
17
  gem install yield-sdk
7
18
  ```
8
19
 
9
- ## Usage
20
+
21
+ Usage
22
+ -----
10
23
 
11
24
  ```ruby
12
25
  require "yield/sdk"
13
26
 
14
- # for security, never commit the actual key in your code
15
- client = Yield::SDK::Client.new(ENV["YIELD_API_KEY"])
27
+ # For security, don't save the actual key in your code or repo
28
+ client = Yield::SDK::Client.new(ENV.fetch("YIELD_API_KEY"))
16
29
 
17
- # fetch an existing order
30
+ # Fetch an existing order
18
31
  order = client.order.fetch("ord_...")
19
32
  p order.customer.registered_name
20
33
 
21
- # or create a new one
34
+ # Or create a new one
22
35
  new_order = client.order.create({
23
- customer_id: "org_...",
24
- total_amount: "PHP 1234.50",
25
- note: "Test order from the Ruby SDK!"
36
+ customer_id: "org_...",
37
+ total_amount: "PHP 1234.50",
38
+ note: "Test order from the Ruby SDK!"
26
39
  })
27
40
  ```
41
+
42
+ For more details, check out our [API reference](https://github.com/yield-tech/sdk-ruby/blob/main/docs/index.md).
data/Rakefile CHANGED
@@ -9,6 +9,15 @@ Rake::TestTask.new(:test) do |t|
9
9
  t.test_files = FileList["test/**/test_*.rb"]
10
10
  end
11
11
 
12
- require "standard/rake"
12
+ require "rubocop/rake_task"
13
13
 
14
- task default: %i[test standard]
14
+ RuboCop::RakeTask.new
15
+
16
+ require "steep/rake_task"
17
+
18
+ Steep::RakeTask.new do |t|
19
+ t.check.severity_level = :error
20
+ t.watch.verbose
21
+ end
22
+
23
+ task default: %i[test rubocop steep]
data/Steepfile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ target :lib do
6
+ signature "sig"
7
+
8
+ check "lib"
9
+ # ignore "lib/.../*.rb"
10
+
11
+ library "base64"
12
+ library "bigdecimal"
13
+ library "date"
14
+ library "json"
15
+ library "openssl"
16
+ library "time"
17
+ library "uri"
18
+
19
+ configure_code_diagnostics(D::Ruby.strict)
20
+ end
data/docs/index.md ADDED
@@ -0,0 +1,12 @@
1
+ API Reference
2
+ =============
3
+
4
+ **Contents:**
5
+ - General overview (TBD)
6
+ - Error handling (TBD)
7
+ - Types (TBD)
8
+ - Pagination (TBD)
9
+ - Modules
10
+ - [Customer module](./modules/customer.md)
11
+ - [Order module](./modules/order.md)
12
+ - [Self module](./modules/self.md)
@@ -0,0 +1,64 @@
1
+ [*← Return to index*](../index.md)
2
+
3
+ Customer module
4
+ ===============
5
+
6
+ **Endpoints:**
7
+ - ![query](https://img.shields.io/badge/QUERY-green) [`list(params)`](#-listparams)
8
+
9
+ **Objects:**
10
+ - [`CustomerRow`](#customerrow)
11
+ - [`CustomerCreditLineInfo`](#customercreditlineinfo)
12
+
13
+
14
+ Endpoints
15
+ ---------
16
+
17
+ ### ![query](https://img.shields.io/badge/QUERY-green) `list(params)`
18
+
19
+ Provides the list of customers under your account, ordered by creation time (newest first).
20
+
21
+ ```ruby
22
+ params = { field: value, ... }
23
+ customers = client.customer.list(params)
24
+
25
+ # or pass them as keyword arguments
26
+ customers = client.customer.list(field: value, ...)
27
+
28
+ # or since the parameters are all optional
29
+ customers = client.customer.list
30
+ ```
31
+
32
+ **Returns:** `Page` of [`CustomerRow`](#customerrow)
33
+
34
+ **Parameters:**
35
+
36
+ - `params`: `Hash` — See the fields right below.
37
+
38
+ | Field | Required? | Type | Description |
39
+ | --------------- | --------- | ------------ | ------------------------------------------------------- |
40
+ | `limit` | Optional | `Integer` | The maximum number of results to return. Default: `10`. |
41
+ | `after` | Optional | `CursorLike` | See docs on pagination. |
42
+ | `customer_code` | Optional | `String` | Filter by customer code. |
43
+
44
+
45
+ Objects
46
+ -------
47
+
48
+ ### `CustomerRow`
49
+
50
+ | Field | Type | Description |
51
+ | ----------------- | ------------------------------------------------------------ | --------------------------------------------- |
52
+ | `id` | `String` | The ID of the customer. |
53
+ | `registered_name` | `String` | The official registered name of the customer. |
54
+ | `trade_name` | `String` \| `nil` | The trade name of the customer. |
55
+ | `customer_code` | `String` \| `nil` | The customer code assigned to this customer. |
56
+ | `credit_line` | [`CustomerCreditLineInfo`](#customercreditlineinfo) \| `nil` | The credit line of the customer. |
57
+
58
+
59
+ ### `CustomerCreditLineInfo`
60
+
61
+ | Field | Type | Description |
62
+ | ------------------ | ------- | ------------------------------------------ |
63
+ | `credit_limit` | `Money` | The credit limit. |
64
+ | `amount_available` | `Money` | The amount available for this credit line. |
@@ -0,0 +1,93 @@
1
+ [*← Return to index*](../index.md)
2
+
3
+ Order module
4
+ ============
5
+
6
+ **Endpoints:**
7
+ - ![query](https://img.shields.io/badge/QUERY-green) [`fetch(id)`](#-fetchid)
8
+ - ![command](https://img.shields.io/badge/COMMAND-orange) [`create(params)`](#-createparams)
9
+
10
+ **Objects:**
11
+ - [`Order`](#order)
12
+ - [`OrderStatus`](#orderstatus)
13
+ - [`OrderCustomerInfo`](#ordercustomerinfo)
14
+
15
+
16
+ Endpoints
17
+ ---------
18
+
19
+ ### ![query](https://img.shields.io/badge/QUERY-green) `fetch(id)`
20
+
21
+ Provides information about the order specified.
22
+
23
+ ```ruby
24
+ order = client.order.fetch(id)
25
+ ```
26
+
27
+ **Returns:** [Order](#order)
28
+
29
+ **Parameters:**
30
+
31
+ - `id`: `String` — The ID of the order.
32
+
33
+
34
+ ### ![command](https://img.shields.io/badge/COMMAND-orange) `create(params)`
35
+
36
+ Creates a new order.
37
+
38
+ ```ruby
39
+ params = { field: value, ... }
40
+ order = client.order.create(params)
41
+ ```
42
+
43
+ **Returns:** [`Order`](#order) — The newly created order.
44
+
45
+ **Parameters:**
46
+
47
+ - `params`: `Hash` — See the fields right below.
48
+
49
+ | Field | Required? | Type | Description |
50
+ | -------------- | --------- | ----------- | ------------------------------------------------------------------------------ |
51
+ | `customer_id` | Required* | `String` | The (Yield) customer ID that this order will belong to. |
52
+ | `total_amount` | Required | `MoneyLike` | The total amount of the order. |
53
+ | `note` | Required* | `String` | A note shown to the customer during checkout, such as details about the order. |
54
+
55
+ \* These fields may become optional in a future release.
56
+
57
+
58
+ Objects
59
+ -------
60
+
61
+ ### `Order`
62
+
63
+ | Field | Type | Description |
64
+ | --------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
65
+ | `id` | `String` | The ID of the order. |
66
+ | `order_number` | `String` | The order number. |
67
+ | `status` | [`OrderStatus`](#orderstatus) | The status of the order. |
68
+ | `customer` | [`OrderCustomerInfo`](#ordercustomerinfo) \| `nil` | The customer this order belongs to. |
69
+ | `date` | `Date` | The date of the order. |
70
+ | `total_amount` | `Money` | The total amount of the order. |
71
+ | `note` | `String` \| `nil` | A note shown to the customer during checkout, such as details about the order. |
72
+ | `payment_link` | `String` \| `nil` | The payment link for the customer to confirm this order. May be `nil` if no longer available. |
73
+ | `creation_time` | `Time` | The timestamp when this order was created. |
74
+
75
+
76
+ ### `OrderStatus`
77
+
78
+ | Value | Description |
79
+ | ------------ | --------------------------------------------------------------------------------------- |
80
+ | `:pending` | The initial status for newly created orders. The customer has yet to confirm the order. |
81
+ | `:confirmed` | The customer has confirmed the order. |
82
+ | `:fulfilled` | The order has been marked as fulfilled. |
83
+ | `:cancelled` | The order has been cancelled. |
84
+
85
+
86
+ ### `OrderCustomerInfo`
87
+
88
+ | Field | Type | Description |
89
+ | ----------------- | ----------------- | --------------------------------------------- |
90
+ | `id` | `String` | The ID of the customer. |
91
+ | `registered_name` | `String` | The official registered name of the customer. |
92
+ | `trade_name` | `String` \| `nil` | The trade name of the customer. |
93
+ | `customer_code` | `String` \| `nil` | The customer code assigned to this customer. |
@@ -0,0 +1,48 @@
1
+ [*← Return to index*](../index.md)
2
+
3
+ Self module
4
+ ===========
5
+
6
+ **Endpoints:**
7
+ - ![query](https://img.shields.io/badge/QUERY-green) [`info`](#-info)
8
+
9
+ **Objects:**
10
+ - [`SelfInfo`](#selfinfo)
11
+ - [`SelfOrganizationInfo`](#selforganizationinfo)
12
+
13
+
14
+ Endpoints
15
+ ---------
16
+
17
+ ### ![query](https://img.shields.io/badge/QUERY-green) `info`
18
+
19
+ Provides information about the API key used to call this endpoint.
20
+
21
+ ```ruby
22
+ info = client.self.info
23
+ ```
24
+
25
+ **Returns:** [`SelfInfo`](#selfinfo)
26
+
27
+ **Parameters:** None
28
+
29
+
30
+ Objects
31
+ -------
32
+
33
+ ### `SelfInfo`
34
+
35
+ | Field | Type | Description |
36
+ | -------------- | ----------------------------------------------- | ------------------------------------- |
37
+ | `id` | `String` | The ID of the API key. |
38
+ | `name` | `String` | Name of the API key. |
39
+ | `organization` | [`SelfOrganizationInfo`](#selforganizationinfo) | The organization this key belongs to. |
40
+
41
+
42
+ ### `SelfOrganizationInfo`
43
+
44
+ | Field | Type | Description |
45
+ | ----------------- | ----------------- | ------------------------------------------------- |
46
+ | `id` | `String` | The ID of the organization. |
47
+ | `registered_name` | `String` | The official registered name of the organization. |
48
+ | `trade_name` | `String` \| `nil` | The trade name of the organization. |
@@ -7,6 +7,7 @@ require "openssl"
7
7
  require "time"
8
8
  require "uri"
9
9
 
10
+ require_relative "../utils/type_utils"
10
11
  require_relative "../version"
11
12
  require_relative "api_result"
12
13
 
@@ -14,47 +15,74 @@ module Yield
14
15
  module SDK
15
16
  module API
16
17
  class Client
18
+ def initialize(api_key, base_url: nil, conn: nil)
19
+ @base_url = base_url || "https://integrate.withyield.com/api/v1"
20
+ @api_key_token, @api_key_hmac_key = Client.extract_api_key(api_key)
21
+ @conn = conn || Faraday.new
22
+ @client_version = SDK.client_version
23
+ end
24
+
25
+ def self.extract_api_key(key)
26
+ # "$" is the old separator, supported for backwards compatibility
27
+ key_parts = key.gsub("$", ":").split(":")
28
+ raise ArgumentError, "Invalid Yield API key" if key_parts.length != 3
29
+
30
+ token = "#{key_parts[0]}$#{key_parts[1]}"
31
+ hmac_key = Base64.urlsafe_decode64(key_parts[2])
32
+ [token, hmac_key]
33
+ end
34
+
35
+ def self.build_signature(hmac_key, timestamp, path, body = nil)
36
+ parts = body.nil? ? [timestamp, path] : [timestamp, path, body]
37
+ message = parts.join("\n")
38
+ sig_bytes = OpenSSL::HMAC.digest("sha512", hmac_key, message)
39
+ Base64.urlsafe_encode64(sig_bytes, padding: false)
40
+ end
41
+
17
42
  def self.process_response(response, &from_payload_block)
18
43
  status_code = response.status
19
44
  request_id = response["X-Request-Id"]
20
45
 
21
- if !response.success?
46
+ unless response.success?
22
47
  error_type = "unexpected_error"
23
- body = nil
48
+ error_body = nil
24
49
 
25
50
  begin
26
51
  body = JSON.parse(response.body)
27
- error_type = body["error"] if body["error"].is_a?(String)
28
- rescue
52
+ if body.is_a?(Hash)
53
+ error_type = body["error"] if body["error"].is_a?(String)
54
+ error_body = body
55
+ end
56
+ rescue StandardError
29
57
  # ignore
30
58
  end
31
59
 
32
- return Result.failure(status_code, request_id, error_type)
60
+ return Result.failure(status_code, request_id, error_type, error_body, nil)
33
61
  end
34
62
 
35
63
  begin
36
- data = from_payload_block.call(JSON.parse(response.body, symbolize_names: true))
37
- rescue
38
- return Result.failure(status_code, request_id, "unexpected_payload")
64
+ body = JSON.parse(response.body, symbolize_names: true)
65
+ payload = TypeUtils.expect_record(body)
66
+ data = from_payload_block.call(payload)
67
+ rescue StandardError => e
68
+ return Result.failure(status_code, request_id, "invalid_response", nil, e)
39
69
  end
40
70
 
41
71
  Result.success(status_code, request_id, data)
42
72
  end
43
73
 
44
- def initialize(api_key, base_url: nil, faraday_conn: nil)
45
- @base_url = base_url || "https://integrate.withyield.com/api/v1"
46
-
47
- key_parts = api_key.split("$")
48
- raise ArgumentError, "Invalid Yield API key" if key_parts.length != 3
49
- @api_key_token = "#{key_parts[0]}$#{key_parts[1]}"
50
- @api_key_hmac_key = Base64.urlsafe_decode64(key_parts[2])
51
-
52
- @faraday_conn = faraday_conn || Faraday.new
53
- @client_version = SDK.client_version
54
- end
55
-
56
74
  def run_query(path, params = nil)
57
- full_path = params.nil? ? path : "#{path}?#{URI.encode_www_form(params)}"
75
+ full_path = path
76
+ unless params.nil?
77
+ # Faraday sorts query parameters by default,
78
+ # so we need to sort it as well for the signature to match.
79
+ # We can disable this globally, but that may be undesirable for some users.
80
+ # See: https://lostisland.github.io/faraday/#/customization/index?id=order-of-parameters
81
+ params = params.compact.to_a.sort_by! { |k, _v| k }
82
+ query_string = URI.encode_www_form(params)
83
+ full_path += "?#{query_string}" unless query_string.empty?
84
+ end
85
+
58
86
  call_endpoint(:get, full_path, nil)
59
87
  end
60
88
 
@@ -62,30 +90,19 @@ module Yield
62
90
  call_endpoint(:post, path, payload)
63
91
  end
64
92
 
65
- def build_signature(path, body = nil, now = Time.now)
66
- timestamp = now.utc.iso8601
67
-
68
- parts = body.nil? ? [timestamp, path] : [timestamp, path, body]
69
- message = parts.join("\n")
70
-
71
- signature_bytes = OpenSSL::HMAC.digest("sha512", @api_key_hmac_key, message)
72
- signature_b64 = Base64.urlsafe_encode64(signature_bytes, padding: false)
73
-
74
- "#{@api_key_token}$#{timestamp}$#{signature_b64}"
75
- end
76
-
77
93
  private
78
94
 
79
95
  def call_endpoint(method, path, payload)
80
- headers = {"X-Yield-Client" => @client_version}
96
+ headers = { "X-Yield-Client" => @client_version }
81
97
 
82
98
  body = payload&.to_json
83
99
  headers["Content-Type"] = "application/json" if body
84
100
 
85
- signature = build_signature(path, body)
86
- headers["Authorization"] = "Yield-Sig #{signature}"
101
+ timestamp = Time.now.utc.iso8601
102
+ signature = Client.build_signature(@api_key_hmac_key, timestamp, path, body)
103
+ headers["Authorization"] = "Yield-Sig #{@api_key_token}$#{timestamp}$#{signature}"
87
104
 
88
- @faraday_conn.run_request(method, @base_url + path, body, headers)
105
+ @conn.run_request(method, @base_url + path, body, headers)
89
106
  end
90
107
  end
91
108
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Yield
6
+ module SDK
7
+ module API
8
+ class Error < StandardError
9
+ attr_reader :status_code
10
+ attr_reader :request_id
11
+ attr_reader :details
12
+
13
+ def initialize(status_code, request_id, error)
14
+ @status_code = status_code
15
+ @request_id = request_id
16
+ @details = error
17
+
18
+ error_info = error.type
19
+ if error.type == "validation_error" && !error.body.nil?
20
+ issues = JSON.generate(error.body["issues"])
21
+ error_info = "#{error_info} #{issues}"
22
+ end
23
+
24
+ unless error.exception.nil?
25
+ message = JSON.generate(error.exception.message)
26
+ error_info = "#{error_info} #{message}"
27
+ end
28
+
29
+ extra_info = [
30
+ "status_code=#{status_code}",
31
+ "request_id=#{request_id || "<none>"}",
32
+ ].join("; ")
33
+
34
+ super("Yield API error: #{error_info} [#{extra_info}]")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yield
4
+ module SDK
5
+ module API
6
+ ErrorDetails = Data.define(:type, :body, :exception)
7
+ end
8
+ end
9
+ end
@@ -1,24 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "api_error"
4
+ require_relative "api_error_details"
5
+
3
6
  module Yield
4
7
  module SDK
5
8
  module API
6
- class Result < Data.define(:status_code, :request_id, :data, :error)
9
+ class Result
10
+ attr_reader :status_code
11
+ attr_reader :request_id
12
+ attr_reader :error
13
+
14
+ def initialize(status_code:, request_id:, data:, error:)
15
+ @status_code = status_code
16
+ @request_id = request_id
17
+ @data = data
18
+ @error = error
19
+ end
20
+
7
21
  def ok?
8
22
  error.nil?
9
23
  end
10
24
 
11
25
  def data
12
- raise Error, "Yield API error: #{error} [status_code=#{status_code}]" unless ok?
13
- super
26
+ raise Error.new(status_code, request_id, error), cause: error.exception unless error.nil?
27
+
28
+ @data
14
29
  end
15
30
 
16
31
  def self.success(status_code, request_id, data)
17
- new(status_code:, request_id:, data:, error: nil)
32
+ new(status_code: status_code, request_id: request_id, data: data, error: nil)
18
33
  end
19
34
 
20
- def self.failure(status_code, request_id, error)
21
- new(status_code:, request_id:, data: nil, error:)
35
+ def self.failure(status_code, request_id, error_type, error_body, exception)
36
+ error = ErrorDetails.new(error_type, error_body, exception)
37
+
38
+ new(status_code: status_code, request_id: request_id, data: nil, error: error)
22
39
  end
23
40
  end
24
41
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "api/api_client"
4
+ require_relative "modules/customer/customer_base_client"
4
5
  require_relative "modules/order/order_base_client"
5
6
  require_relative "modules/self/self_base_client"
6
7
 
@@ -9,14 +10,16 @@ module Yield
9
10
  class BaseClient
10
11
  attr_reader :api
11
12
 
13
+ attr_reader :customer
12
14
  attr_reader :self
13
15
  attr_reader :order
14
16
 
15
- def initialize(api_key, base_url: nil, faraday_conn: nil)
16
- @api = API::Client.new(api_key, base_url:, faraday_conn:)
17
+ def initialize(api_key, base_url: nil, conn: nil)
18
+ @api = API::Client.new(api_key, base_url: base_url, conn: conn)
17
19
 
18
- @self = Self::BaseClient.new(@api)
20
+ @customer = Customer::BaseClient.new(@api)
19
21
  @order = Order::BaseClient.new(@api)
22
+ @self = Self::BaseClient.new(@api)
20
23
  end
21
24
  end
22
25
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base_client"
4
+ require_relative "modules/customer/customer_client"
4
5
  require_relative "modules/order/order_client"
5
6
  require_relative "modules/self/self_client"
6
7
 
@@ -9,14 +10,16 @@ module Yield
9
10
  class Client
10
11
  attr_reader :base
11
12
 
12
- attr_reader :self
13
+ attr_reader :customer
13
14
  attr_reader :order
15
+ attr_reader :self
14
16
 
15
- def initialize(api_key, base_url: nil, faraday_conn: nil)
16
- @base = BaseClient.new(api_key, base_url:, faraday_conn:)
17
+ def initialize(api_key, base_url: nil, conn: nil)
18
+ @base = BaseClient.new(api_key, base_url: base_url, conn: conn)
17
19
 
18
- @self = Self::Client.new(@base.self)
20
+ @customer = Customer::Client.new(@base.customer)
19
21
  @order = Order::Client.new(@base.order)
22
+ @self = Self::Client.new(@base.self)
20
23
  end
21
24
  end
22
25
  end