revolut-connect 0.1.5 → 0.1.7

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: 9010d58ab099e0739336f7645c70fa7f859aeb8742109a3f48459262221302b8
4
- data.tar.gz: d53d5e9b0a475252d5b00eaf9984a8557c67b0e31d03eb80ea4f4f39fdacc0e8
3
+ metadata.gz: b0b5d6b9801d9238ff7de1397f5398dd26651944e8d82de4f80eb497cbd89878
4
+ data.tar.gz: d9057ed5cbcc377551854526e4be16fcc11ad5bbcb0419ec7e6806e36d81ef52
5
5
  SHA512:
6
- metadata.gz: 638eea99c2401f8fcdf5053f4fcc664975c6e677bf67eca9a83747337bac1389138da6e8a4a8f9632552d1ef848084e6f003a324c3577a2875810989971c77e6
7
- data.tar.gz: 4e6e92b18f575b031861890f8e7f93b0ea6070e0c9c7321a515e01e3ad52f72ce8a6250b9e9d969757fa5e5268b4bffff8423d6a104a32d3d3c13da7b4bb020b
6
+ metadata.gz: 68f7f02ffc2a8dba0c99ae87953ca8d02f880dde5343478678ff85fb88d0b736840b60260a82b893d750369214a7ae7b1cbe0f73e5643e9cf67d3e7973ceda8d
7
+ data.tar.gz: 778f06a9e1fe00e69450f8b74213461df563aa51ba51f95850f42cf75a6bca64a7cd7626b2ef44a69a7b8c0b1c15f5ccb163168170a32dcce99911bc4a15dd55
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in revolut-connect.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
8
+ gem "rake", "~> 13.2"
9
9
 
10
10
  group :development, :test do
11
11
  gem "byebug", "~> 11.1"
@@ -14,13 +14,13 @@ group :development, :test do
14
14
  gem "pry-rescue", "~> 1.5"
15
15
  gem "pry-stack_explorer", "~> 0.6.1"
16
16
  gem "dotenv"
17
- gem "standard", "~> 1.3"
17
+ gem "standard", "~> 1.36"
18
18
  gem "timecop"
19
19
  end
20
20
 
21
21
  group :test do
22
22
  gem "rspec", "~> 3.0"
23
- gem "webmock", "~> 3.23.0"
23
+ gem "webmock", "~> 3.23.1"
24
24
  gem "simplecov"
25
25
  gem "simplecov-cobertura"
26
26
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- revolut-connect (0.1.5)
4
+ revolut-connect (0.1.7)
5
5
  faraday (>= 2)
6
6
  faraday-retry (>= 2)
7
7
  jwt (>= 1)
@@ -9,13 +9,13 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- addressable (2.8.6)
13
- public_suffix (>= 2.0.2, < 6.0)
12
+ addressable (2.8.7)
13
+ public_suffix (>= 2.0.2, < 7.0)
14
14
  ast (2.4.2)
15
15
  base64 (0.2.0)
16
- bigdecimal (3.1.6)
17
- binding_of_caller (1.0.0)
18
- debug_inspector (>= 0.0.1)
16
+ bigdecimal (3.1.8)
17
+ binding_of_caller (1.0.1)
18
+ debug_inspector (>= 1.2.0)
19
19
  byebug (11.1.3)
20
20
  coderay (1.1.3)
21
21
  crack (1.0.0)
@@ -23,26 +23,28 @@ GEM
23
23
  rexml
24
24
  debug_inspector (1.2.0)
25
25
  diff-lcs (1.5.1)
26
- docile (1.4.0)
27
- dotenv (3.1.0)
28
- faraday (2.9.0)
26
+ docile (1.4.1)
27
+ dotenv (3.1.2)
28
+ faraday (2.10.0)
29
29
  faraday-net_http (>= 2.0, < 3.2)
30
- faraday-net_http (3.1.0)
30
+ logger
31
+ faraday-net_http (3.1.1)
31
32
  net-http
32
- faraday-retry (2.2.0)
33
+ faraday-retry (2.2.1)
33
34
  faraday (~> 2.0)
34
35
  hashdiff (1.1.0)
35
36
  interception (0.5)
36
- json (2.7.1)
37
- jwt (2.8.1)
37
+ json (2.7.2)
38
+ jwt (2.8.2)
38
39
  base64
39
40
  language_server-protocol (3.17.0.3)
40
41
  lint_roller (1.1.0)
41
- method_source (1.0.0)
42
+ logger (1.6.0)
43
+ method_source (1.1.0)
42
44
  net-http (0.4.1)
43
45
  uri
44
- parallel (1.24.0)
45
- parser (3.3.0.5)
46
+ parallel (1.25.1)
47
+ parser (3.3.4.0)
46
48
  ast (~> 2.4.1)
47
49
  racc
48
50
  pry (0.14.2)
@@ -57,26 +59,27 @@ GEM
57
59
  pry-stack_explorer (0.6.1)
58
60
  binding_of_caller (~> 1.0)
59
61
  pry (~> 0.13)
60
- public_suffix (5.0.4)
61
- racc (1.7.3)
62
+ public_suffix (6.0.1)
63
+ racc (1.8.0)
62
64
  rainbow (3.1.1)
63
- rake (13.1.0)
64
- regexp_parser (2.9.0)
65
- rexml (3.2.6)
65
+ rake (13.2.1)
66
+ regexp_parser (2.9.2)
67
+ rexml (3.3.2)
68
+ strscan
66
69
  rspec (3.13.0)
67
70
  rspec-core (~> 3.13.0)
68
71
  rspec-expectations (~> 3.13.0)
69
72
  rspec-mocks (~> 3.13.0)
70
73
  rspec-core (3.13.0)
71
74
  rspec-support (~> 3.13.0)
72
- rspec-expectations (3.13.0)
75
+ rspec-expectations (3.13.1)
73
76
  diff-lcs (>= 1.2.0, < 2.0)
74
77
  rspec-support (~> 3.13.0)
75
- rspec-mocks (3.13.0)
78
+ rspec-mocks (3.13.1)
76
79
  diff-lcs (>= 1.2.0, < 2.0)
77
80
  rspec-support (~> 3.13.0)
78
81
  rspec-support (3.13.1)
79
- rubocop (1.61.0)
82
+ rubocop (1.64.1)
80
83
  json (~> 2.3)
81
84
  language_server-protocol (>= 3.17.0)
82
85
  parallel (~> 1.10)
@@ -84,14 +87,14 @@ GEM
84
87
  rainbow (>= 2.2.2, < 4.0)
85
88
  regexp_parser (>= 1.8, < 3.0)
86
89
  rexml (>= 3.2.5, < 4.0)
87
- rubocop-ast (>= 1.30.0, < 2.0)
90
+ rubocop-ast (>= 1.31.1, < 2.0)
88
91
  ruby-progressbar (~> 1.7)
89
92
  unicode-display_width (>= 2.4.0, < 3.0)
90
- rubocop-ast (1.31.1)
91
- parser (>= 3.3.0.4)
92
- rubocop-performance (1.20.2)
93
+ rubocop-ast (1.31.3)
94
+ parser (>= 3.3.1.0)
95
+ rubocop-performance (1.21.1)
93
96
  rubocop (>= 1.48.1, < 2.0)
94
- rubocop-ast (>= 1.30.0, < 2.0)
97
+ rubocop-ast (>= 1.31.1, < 2.0)
95
98
  ruby-progressbar (1.13.0)
96
99
  simplecov (0.22.0)
97
100
  docile (~> 1.1)
@@ -102,28 +105,30 @@ GEM
102
105
  simplecov (~> 0.19)
103
106
  simplecov-html (0.12.3)
104
107
  simplecov_json_formatter (0.1.4)
105
- standard (1.34.0)
108
+ standard (1.39.2)
106
109
  language_server-protocol (~> 3.17.0.2)
107
110
  lint_roller (~> 1.0)
108
- rubocop (~> 1.60)
111
+ rubocop (~> 1.64.0)
109
112
  standard-custom (~> 1.0.0)
110
- standard-performance (~> 1.3)
113
+ standard-performance (~> 1.4)
111
114
  standard-custom (1.0.2)
112
115
  lint_roller (~> 1.0)
113
116
  rubocop (~> 1.50)
114
- standard-performance (1.3.1)
117
+ standard-performance (1.4.0)
115
118
  lint_roller (~> 1.1)
116
- rubocop-performance (~> 1.20.2)
117
- timecop (0.9.8)
119
+ rubocop-performance (~> 1.21.0)
120
+ strscan (3.1.0)
121
+ timecop (0.9.10)
118
122
  unicode-display_width (2.5.0)
119
123
  uri (0.13.0)
120
- webmock (3.23.0)
124
+ webmock (3.23.1)
121
125
  addressable (>= 2.8.0)
122
126
  crack (>= 0.3.2)
123
127
  hashdiff (>= 0.4.0, < 2.0.0)
124
128
 
125
129
  PLATFORMS
126
130
  x86_64-darwin-23
131
+ x86_64-linux
127
132
 
128
133
  DEPENDENCIES
129
134
  byebug (~> 11.1)
@@ -132,14 +137,14 @@ DEPENDENCIES
132
137
  pry-byebug (~> 3.10)
133
138
  pry-rescue (~> 1.5)
134
139
  pry-stack_explorer (~> 0.6.1)
135
- rake (~> 13.0)
140
+ rake (~> 13.2)
136
141
  revolut-connect!
137
142
  rspec (~> 3.0)
138
143
  simplecov
139
144
  simplecov-cobertura
140
- standard (~> 1.3)
145
+ standard (~> 1.36)
141
146
  timecop
142
- webmock (~> 3.23.0)
147
+ webmock (~> 3.23.1)
143
148
 
144
149
  BUNDLED WITH
145
150
  2.4.22
data/README.md CHANGED
@@ -19,20 +19,20 @@ _:warning: For now this connector only supports the [Business API](https://devel
19
19
 
20
20
  - `Account`
21
21
  - `Counterparty`
22
+ - `ForeignExchange`
22
23
  - `Payment`
23
24
  - `Transaction`
24
25
  - `TransferReason`
25
26
  - `Webhook`
27
+ - `Simulation`
26
28
 
27
29
  ## :construction: Roadmap
28
30
 
29
31
  ### Business API
30
32
 
31
33
  - `Card` resource
32
- - `ForeignExchange` resource
33
34
  - `PaymentDraft` resource
34
35
  - `PayoutLink` resource
35
- - `Simulation` resource
36
36
  - `TeamMember` resource
37
37
  - `Transfer` resource
38
38
 
@@ -101,7 +101,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
101
101
  And then, when you need to load the auth again:
102
102
 
103
103
  ```rb
104
- Revolut::Auth.load(auth_to_persist)
104
+ Revolut::Auth.load(JSON.parse(auth_to_persist))
105
105
  ```
106
106
 
107
107
  You can also store this json in an environment variable and the gem will auto load it:
@@ -149,8 +149,8 @@ Revolut.configure do |config|
149
149
  # Useful for observability tools like Helicone: https://www.helicone.ai/
150
150
  # Default: {}
151
151
  config.global_headers = {
152
- "Helicone-Auth": "Bearer {HELICONE_API_KEY}"
153
- "helicone-stream-force-format" => "true",
152
+ "Helicone-Auth": "Bearer {HELICONE_API_KEY}",
153
+ "helicone-stream-force-format" => "true"
154
154
  }
155
155
 
156
156
  # Optional: Set the environment to be production or sandbox.
@@ -218,6 +218,35 @@ retrieved_counterparty = Revolut::Counterparty.retrieve(created_counterparty.id)
218
218
  deleted = Revolut::Counterparty.delete(retrieved_counterparty.id)
219
219
  ```
220
220
 
221
+ #### ForeignExchange
222
+
223
+ <https://developer.revolut.com/docs/business/foreign-exchange>
224
+
225
+ ```rb
226
+ # Exchange currencies
227
+ exchange = Revolut::ForeignExchange.exchange(
228
+ request_id: "49c6a48b-6b58-40a0-b974-0b8c4888c8a9", # The ID of the request, provided by you. It helps you identify the transaction in your system.
229
+ from: {
230
+ account_id: "8fe12333-5b27-4ad5-896c-38a25673fcc8",
231
+ currency: "USD"
232
+ },
233
+ to: {
234
+ account_id: "b4a3bcd2-c1dd-47cc-ac50-40cdb5856d42",
235
+ currency: "GBP",
236
+ amount: 10
237
+ },
238
+ reference: "exchange"
239
+ )
240
+
241
+
242
+ # Retrieve information on exchange rates between currencies
243
+ rate = Revolut::ForeignExchange.rate(
244
+ from: "EUR",
245
+ to: "USD",
246
+ amount: 100
247
+ )
248
+ ```
249
+
221
250
  #### Payments
222
251
 
223
252
  <https://developer.revolut.com/docs/business/create-payment>
@@ -247,6 +276,99 @@ transaction = Revolut::Payment.retrieve(payment.id)
247
276
  deleted = Revolut::Payment.delete(transaction.id)
248
277
  ```
249
278
 
279
+ #### Simulations
280
+
281
+ <https://developer.revolut.com/docs/business/simulations>
282
+
283
+ ```rb
284
+ # Update a transaction
285
+ transaction = Revolut::Simulation.update_transaction("a6ea39d7-62c9-481c-8ba6-8a887a44c486", action: :complete)
286
+
287
+ # Top up an account
288
+ transaction = Revolut::Simulation.top_up_account("e042f1fe-f721-49cc-af82-db7a6c46944f",
289
+ amount: 100,
290
+ currency: "GBP",
291
+ reference: "Test Top-up",
292
+ state: "completed"
293
+ )
294
+ ```
295
+
296
+ #### Webhooks
297
+
298
+ <https://developer.revolut.com/docs/business/webhooks-v-2>
299
+
300
+ ```rb
301
+ # Create a webhook
302
+ webhook = Revolut::Webhook.create(
303
+ url: "https://www.example.com",
304
+ events: [
305
+ "TransactionCreated",
306
+ "PayoutLinkCreated"
307
+ ]
308
+ )
309
+
310
+ # List webhooks
311
+ webhooks = Revolut::Webhook.list
312
+
313
+ # Retrieve a webhook
314
+ webhook = Revolut::Webhook.retrieve(webhook.id)
315
+
316
+ # Update a webhook
317
+ webhook = Revolut::Webhook.update(webhook.id, url: "https://www.example.com/")
318
+
319
+ # Delete a webhook
320
+ deleted = Revolut::Webhook.delete(webhook.id)
321
+
322
+ # Rotate webhook secret
323
+ rotated = Revolut::Webhook.rotate_signing_secret(webhook.id)
324
+
325
+ # Retrieve list of failing events
326
+ failed_events = Revolut::Webhook.failed_events(webhook.id)
327
+ ```
328
+
329
+ #### Webhooks in web applications (Rails example)
330
+
331
+ In order to start listening to revolut webhook events you'll first have to create a webhook in your revolut instance so that revolut knows that it needs to start pushing events to a certain URL:
332
+
333
+ ```rb
334
+ webhook = Revolut::Webhook.create(url: "http://{your web app webhook URL}")
335
+ puts webhook.signing_secret
336
+ ```
337
+
338
+ You can then copy the signing secret and store it in the env variables as follows:
339
+
340
+ ```text
341
+ REVOLUT_WEBHOOK_SECRET={copy your signing secret here}
342
+ ```
343
+
344
+ Then, in the revolut webhook controller, you can do the following to coerce revolut events (rails example):
345
+
346
+ ```rb
347
+ class Webhooks::RevolutController < ApplicationController
348
+
349
+ def run
350
+ coerced = Revolut::WebhookEvent.construct_from(request, ENV["REVOLUT_WEBHOOK_SECRET"])
351
+ case coerced.event
352
+ when "TransactionStateChanged"
353
+ # handle event
354
+ when "PayoutLinkCreated"
355
+ # handle event
356
+ ...
357
+ end
358
+ rescue Revolut::SignatureVerificationError => e
359
+ # Do something when the signature verification fails
360
+ head :bad_request
361
+ end
362
+
363
+ end
364
+ ```
365
+
366
+ The rails routes would look something like this:
367
+
368
+ ```rb
369
+ post "webhooks/revolut", to: "webhooks/revolut#run"
370
+ ```
371
+
250
372
  ## Development
251
373
 
252
374
  You can use `bin/console` to access an interactive console. This will preload environment variables from a `.env` file.
@@ -7,6 +7,10 @@ module Revolut
7
7
  "accounts"
8
8
  end
9
9
 
10
+ # Retrieves the bank details for a specific account.
11
+ #
12
+ # @param id [String] The ID of the account.
13
+ # @return [Array<Revolut::BankAccount>] An array of bank account objects.
10
14
  def self.bank_details(id)
11
15
  response = http_client.get("/#{resources_name}/#{id}/bank-details")
12
16
 
@@ -0,0 +1,28 @@
1
+ require "forwardable"
2
+
3
+ module Revolut
4
+ class ForeignExchange < Resource
5
+ shallow
6
+
7
+ class << self
8
+ extend Forwardable
9
+
10
+ def resource_name
11
+ "exchange"
12
+ end
13
+
14
+ def exchange(**attrs)
15
+ response = http_client.post("/#{resource_name}", data: attrs)
16
+
17
+ new(response.body)
18
+ end
19
+
20
+ # Delegate rate to the rate resource
21
+ def_delegators :rate_resource, :rate
22
+
23
+ def rate_resource
24
+ Revolut::Rate
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ module Revolut
2
+ class Rate < Resource
3
+ shallow
4
+
5
+ def self.resource_name
6
+ "rate"
7
+ end
8
+
9
+ def self.rate(**)
10
+ response = http_client.get("/#{resource_name}", **)
11
+
12
+ new(response.body)
13
+ end
14
+ end
15
+ end
@@ -57,12 +57,12 @@ module Revolut
57
57
  @coerce_with ||= attrs
58
58
  end
59
59
 
60
- protected
61
-
62
60
  def http_client
63
61
  @http_client ||= Revolut::Client.instance
64
62
  end
65
63
 
64
+ protected
65
+
66
66
  def resource_name
67
67
  resources_name
68
68
  end
@@ -88,16 +88,10 @@ module Revolut
88
88
 
89
89
  def check_not_allowed
90
90
  method = caller(1..1).first.match(/`(\w+)'/)[1].to_sym
91
- raise Revolut::Error, "`#{method}` is not allowed on this resource" if not_allowed_to.include?(method) || only.any? && !only.include?(method)
91
+ raise Revolut::UnsupportedOperationError, "`#{method}` operation is not allowed on this resource" if not_allowed_to.include?(method) || only.any? && !only.include?(method)
92
92
  end
93
93
  end
94
94
 
95
- def to_json
96
- @_raw.to_json
97
- end
98
-
99
- protected
100
-
101
95
  def initialize(attrs = {})
102
96
  @_raw = attrs
103
97
 
@@ -121,5 +115,9 @@ module Revolut
121
115
 
122
116
  instance_variables.each { |iv| self.class.send(:attr_accessor, iv.to_s[1..].to_sym) }
123
117
  end
118
+
119
+ def to_json
120
+ @_raw.to_json
121
+ end
124
122
  end
125
123
  end
@@ -0,0 +1,41 @@
1
+ module Revolut
2
+ # Reference: https://developer.revolut.com/docs/business/counterparties
3
+ class Simulation < Resource
4
+ shallow
5
+
6
+ def self.resources_name
7
+ "sandbox"
8
+ end
9
+
10
+ # Updates a transaction in the sandbox environment.
11
+ #
12
+ # @param id [String] The ID of the transaction to update.
13
+ # @param action [Symbol] The action to perform on the transaction.
14
+ # @return [Revolut::Transaction] The updated transaction object.
15
+ # @raise [Revolut::UnsupportedOperationError] If the method is called in a non-sandbox environment or if the action is not supported.
16
+ def self.update_transaction(id, action:)
17
+ raise Revolut::UnsupportedOperationError, "#update_transaction is meant to be run only in sandbox environments" unless Revolut.sandbox?
18
+ raise Revolut::UnsupportedOperationError, "The action `#{action}` is not supported" unless %i[complete revert declined fail].include?(action)
19
+
20
+ response = http_client.post("/#{resources_name}/transactions/#{id}/#{action}")
21
+
22
+ Revolut::Transaction.new(response.body)
23
+ end
24
+
25
+ # Adds funds to the specified account in the sandbox environment.
26
+ #
27
+ # @param id [String] The ID of the account to top up.
28
+ # @param data [Hash] Additional data for the top-up request.
29
+ # @option data [Float] :amount The amount to top up the account by.
30
+ # @option data [String] :currency The currency of the top-up amount.
31
+ # @return [Revolut::Transaction] The transaction object representing the top-up.
32
+ # @raise [Revolut::UnsupportedOperationError] If the method is called outside of the sandbox environment.
33
+ def self.top_up_account(id, **data)
34
+ raise Revolut::UnsupportedOperationError, "#top_up_account is meant to be run only in sandbox environments" unless Revolut.sandbox?
35
+
36
+ response = http_client.post("/#{resources_name}/topup", data: data.merge(account_id: id))
37
+
38
+ Revolut::Transaction.new(response.body)
39
+ end
40
+ end
41
+ end
@@ -14,12 +14,12 @@ module Revolut
14
14
  def self.construct_from(request, signing_secret)
15
15
  json = request.body.read
16
16
  timestamp = request.headers["Revolut-Request-Timestamp"]
17
- header_signature = request.headers["Revolut-Signature"]
17
+ header_signatures = request.headers["Revolut-Signature"].split(",")
18
18
  payload_to_sign = "v1.#{timestamp}.#{json}"
19
19
  digest = OpenSSL::Digest.new("sha256")
20
20
  signature_digest = "v1=" + OpenSSL::HMAC.hexdigest(digest, signing_secret, payload_to_sign)
21
21
 
22
- if signature_digest == header_signature
22
+ if header_signatures.include? signature_digest
23
23
  new(JSON.parse(json))
24
24
  else
25
25
  raise Revolut::SignatureVerificationError, "Signature verification failed"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Revolut
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/revolut.rb CHANGED
@@ -19,6 +19,8 @@ module Revolut
19
19
 
20
20
  class SignatureVerificationError < Error; end
21
21
 
22
+ class UnsupportedOperationError < Error; end
23
+
22
24
  class Configuration
23
25
  attr_accessor :request_timeout, :global_headers, :environment, :token_duration, :scope, :auth_json, :api_version
24
26
  attr_writer :client_id, :signing_key, :iss, :authorize_redirect_uri
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: revolut-connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Mochetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-08 00:00:00.000000000 Z
11
+ date: 2024-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -82,8 +82,11 @@ files:
82
82
  - lib/revolut/resources/auth.rb
83
83
  - lib/revolut/resources/bank_account.rb
84
84
  - lib/revolut/resources/counterparty.rb
85
+ - lib/revolut/resources/foreign_exchange.rb
85
86
  - lib/revolut/resources/payment.rb
87
+ - lib/revolut/resources/rate.rb
86
88
  - lib/revolut/resources/resource.rb
89
+ - lib/revolut/resources/simulation.rb
87
90
  - lib/revolut/resources/transaction.rb
88
91
  - lib/revolut/resources/transfer_reason.rb
89
92
  - lib/revolut/resources/webhook.rb