belvo 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'belvo/http'
4
+ require 'belvo/exceptions'
5
+ require 'belvo/resources'
6
+
7
+ module Belvo
8
+ # Allows easy access to Belvo API servers.
9
+ class Client
10
+ # Current Belvo API session
11
+ # @return [APISession]
12
+ attr_reader :session
13
+
14
+ # @param secret_key_id [String]
15
+ # @param secret_key_password [String]
16
+ # @param url [String, nil] API host URL, can be set from the environment
17
+ # using the variable name BELVO_API_URL
18
+ # @return [APISession] Authenticated Belvo API session
19
+ def initialize(secret_key_id, secret_key_password, url = nil)
20
+ (belvo_api_url = url) || ENV['BELVO_API_URL']
21
+ raise BelvoAPIError, 'You need to provide a URL.' if belvo_api_url.nil?
22
+
23
+ @session = Belvo::APISession.new(belvo_api_url)
24
+
25
+ return if @session.login(secret_key_id, secret_key_password)
26
+
27
+ raise BelvoAPIError, 'Login failed.'
28
+ end
29
+
30
+ # Provides access to Links resource
31
+ # @return [Link]
32
+ def links
33
+ @links = Link.new @session
34
+ end
35
+
36
+ # Provides access to Accounts resource
37
+ # @return [Account]
38
+ def accounts
39
+ @accounts = Account.new @session
40
+ end
41
+
42
+ # Provides access to Transactions resource
43
+ # @return [Transaction]
44
+ def transactions
45
+ @transactions = Transaction.new @session
46
+ end
47
+
48
+ # Provides access to Owners resource
49
+ # @return [Owner]
50
+ def owners
51
+ @owners = Owner.new @session
52
+ end
53
+
54
+ # Provides access to Balances resource
55
+ # @return [Balance]
56
+ def balances
57
+ @balances = Balance.new @session
58
+ end
59
+
60
+ # Provides access to Statements resource
61
+ # @return [Statement]
62
+ def statements
63
+ @statements = Statement.new @session
64
+ end
65
+
66
+ # Provides access to Invoices resource
67
+ # @return [Invoice]
68
+ def invoices
69
+ @invoices = Invoice.new @session
70
+ end
71
+
72
+ # Provides access to TaxReturns resource
73
+ # @return [TaxReturn]
74
+ def tax_returns
75
+ @tax_returns = TaxReturn.new @session
76
+ end
77
+
78
+ # Provides access to Instituions resource
79
+ # @return [Institution]
80
+ def institutions
81
+ @institutions = Institution.new @session
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belvo
4
+ # Belvo API base error
5
+ class BelvoAPIError < StandardError
6
+ # @param message [String] Error short description
7
+ def initialize(message)
8
+ super(message)
9
+ end
10
+ end
11
+
12
+ # Generic request error, any response with status different than 2xx.
13
+ class RequestError < BelvoAPIError
14
+ # HTTP code returned by Belvo API
15
+ # @return [Integer]
16
+ attr_reader :status_code
17
+
18
+ # Error message returned by Belvo API
19
+ # @return [JSON]
20
+ attr_reader :detail
21
+
22
+ # @param message [String] Error short description
23
+ # @param status_code [Integer] HTTP code
24
+ # @param detail [JSON] Detailed error(s) description
25
+ def initialize(message, status_code, detail)
26
+ super(message)
27
+ @status_code = status_code
28
+ @detail = detail
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'typhoeus'
6
+ require 'typhoeus/adapters/faraday'
7
+
8
+ require 'belvo/version'
9
+ require 'belvo/exceptions'
10
+
11
+ module Belvo
12
+ # Describes a Belvo API session
13
+ class APISession
14
+ # @param url [String] Belvo API host url
15
+ # @return [Faraday::Connection]
16
+ def initialize(url)
17
+ @url = format('%<url>s/api/', url: url)
18
+ @session = Faraday::Connection.new(url: @url) do |faraday|
19
+ faraday.adapter :typhoeus
20
+ faraday.response :json
21
+ faraday.headers = {
22
+ 'Content-Type' => 'application/json',
23
+ 'User-Agent' => format(
24
+ 'belvo-ruby (%<version>s)',
25
+ version: Belvo::VERSION
26
+ )
27
+ }
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Raise an error if the response code is not 2XX.
34
+ # @param response [Faraday::Response]
35
+ # @return [Faraday::Response]
36
+ def raise_for_status(response)
37
+ unless response.success?
38
+ raise RequestError.new(
39
+ 'Request error',
40
+ response.status,
41
+ response.body.to_s
42
+ )
43
+ end
44
+ response
45
+ end
46
+
47
+ # Set and validate authentication credentials
48
+ # @return [Boolean] True if credentials are valid to authenticate
49
+ # to Belvo API else False.
50
+ def authenticate
51
+ @session.basic_auth(@key_id, @key_password)
52
+ response = @session.get('')
53
+ response.success?
54
+ end
55
+
56
+ # Perform GET request to a Belvo API endpoint. Needed for Detail and List.
57
+ # @param path [String] API Endpoint
58
+ # @param params [Hash] Params to be send as querystring
59
+ # @return [Faraday::Response]
60
+ # @raise [RequestError] If response code is different than 2XX
61
+ def get(path, params: nil)
62
+ params = {} if params.nil?
63
+ response = @session.get(path) do |req|
64
+ req.params = params
65
+ end
66
+ raise_for_status response
67
+ end
68
+
69
+ public
70
+
71
+ # Authenticate to Belvo API using the given credentials.
72
+ # @param secret_key_id [String]
73
+ # @param secret_key_password [String]
74
+ # @return [Boolean] True if credentials are valid to authenticate
75
+ # to Belvo API else False.
76
+ def login(secret_key_id, secret_key_password)
77
+ @key_id = secret_key_id
78
+ @key_password = secret_key_password
79
+
80
+ authenticate
81
+ end
82
+
83
+ # List results from an API endpoint. It will yield all results following
84
+ # pagination's next page, if any.
85
+ # @param path [String] API endpoint
86
+ # @param params [Hash] Params to be send as querystring
87
+ # @yield [Hash] Each result
88
+ # @raise [RequestError] If response code is different than 2XX
89
+ def list(path, params: nil)
90
+ params = {} if params.nil?
91
+ loop do
92
+ response = get(path, params: params)
93
+ response.body['results'].each do |item|
94
+ yield item if block_given?
95
+ end
96
+
97
+ break unless response.body['next']
98
+
99
+ path = response.body['next']
100
+ params = nil
101
+ end
102
+ end
103
+
104
+ # Show specific resource details
105
+ # @param path [String] API endpoint
106
+ # @param id [String] Resource UUID
107
+ # @param params [Hash] Params to be send as querystring
108
+ # @return [Hash] Resource details
109
+ # @raise [RequestError] If response code is different than 2XX
110
+ def detail(path, id, params: nil)
111
+ params = {} if params.nil?
112
+
113
+ resource_path = format('%<path>s%<id>s/', path: path, id: id)
114
+ response = get(resource_path, params)
115
+
116
+ raise_for_status response
117
+ response.body
118
+ end
119
+
120
+ # Perform a POST request to an API endpoint
121
+ # @param path [String] API endpoint
122
+ # @param data [object] JSON parseable object
123
+ # @return [Hash] Response details
124
+ # @raise [RequestError] If response code is different than 2XX
125
+ def post(path, data)
126
+ response = @session.post(path, data.to_json)
127
+ raise_for_status response
128
+ response.body
129
+ end
130
+
131
+ # Perform a PUT request to an specific resource
132
+ # @param path [String] API endpoint
133
+ # @param id [String] Resource UUID
134
+ # @param data [object] JSON parseable object
135
+ # @return [Hash] Response details
136
+ # @raise [RequestError] If response code is different than 2XX
137
+ def put(path, id, data)
138
+ url = format('%<path>s%<id>s/', path: path, id: id)
139
+ response = @session.put(url, data.to_json)
140
+ raise_for_status response
141
+ response.body
142
+ end
143
+
144
+ # Perform a PATCH request to an API endpoint
145
+ # @param path [String] API endpoint
146
+ # @param data [object] JSON parseable object
147
+ # @return [Hash] Response details
148
+ # @raise [RequestError] If response code is different than 2XX
149
+ def patch(path, data)
150
+ response = @session.patch(path, data.to_json)
151
+ raise_for_status response
152
+ response.body
153
+ end
154
+
155
+ # Delete existing resource
156
+ # @param path [String] API endpoint
157
+ # @param id [String] Resource UUID
158
+ # @return [Boolean] true if resource is successfully deleted else false
159
+ def delete(path, id)
160
+ resource_path = format('%<path>s%<id>s/', path: path, id: id)
161
+ response = @session.delete(resource_path)
162
+ response.success?
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday/options'
4
+
5
+ module Belvo
6
+ # @!class LinkOptions < Faraday::Options
7
+ # Contains the configurable properties for a Link
8
+ # @!attribute access_mode [rw] Link access mode (SINGLE or RECURRENT)
9
+ # @!attribute token [rw] OTP token required by the institution
10
+ # @!attribute encryption_key [rw] Custom encryption key
11
+ class LinkOptions < Faraday::Options.new(
12
+ :access_mode,
13
+ :token,
14
+ :encryption_key
15
+ )
16
+ end
17
+
18
+ # @!class AccountOptions < Faraday::Options
19
+ # Contains the configurable properties for an Account
20
+ # @!attribute save_data [rw] Should data be persisted or not.
21
+ # @!attribute token [rw] OTP token required by the institution
22
+ # @!attribute encryption_key [rw] Custom encryption key
23
+ class AccountOptions < Faraday::Options.new(
24
+ :save_data,
25
+ :token,
26
+ :encryption_key
27
+ )
28
+ end
29
+
30
+ # @!class TransactionOptions < Faraday::Options
31
+ # Contains configurable properties for a Transaction
32
+ # @!attribute date_to [rw] Date string (YYYY-MM-DD)
33
+ # @!attribute account [rw] Account ID (UUID)
34
+ # @!attribute save_data [rw] Should data be persisted or not.
35
+ # @!attribute token [rw] OTP token required by the institution
36
+ # @!attribute encryption_key [rw] Custom encryption key
37
+ class TransactionOptions < Faraday::Options.new(
38
+ :date_to,
39
+ :account,
40
+ :token,
41
+ :encryption_key,
42
+ :save_data
43
+ )
44
+ end
45
+
46
+ # @!class OwnerOptions < Faraday::Options
47
+ # Contains configurable properties of an Owner
48
+ # @!attribute save_data [rw] Should data be persisted or not.
49
+ # @!attribute token [rw] OTP token required by the institution
50
+ # @!attribute encryption_key [rw] Custom encryption key
51
+ class OwnerOptions < Faraday::Options.new(:token, :encryption_key, :save_data)
52
+ end
53
+
54
+ # @!class BalanceOptions < Faraday::Options
55
+ # Contains configurable properties of a Balance
56
+ # @!attribute date_to [rw] Date string (YYYY-MM-DD)
57
+ # @!attribute account [rw] Account ID (UUID)
58
+ # @!attribute save_data [rw] Should data be persisted or not.
59
+ # @!attribute token [rw] OTP token required by the institution
60
+ # @!attribute encryption_key [rw] Custom encryption key
61
+ class BalanceOptions < Faraday::Options.new(
62
+ :date_to,
63
+ :account,
64
+ :token,
65
+ :encryption_key,
66
+ :save_data
67
+ )
68
+ end
69
+
70
+ # @!class StatementOptions < Faraday::Options
71
+ # Contains configurable properties of a Statement
72
+ # @!attribute save_data [rw] Should data be persisted or not.
73
+ # @!attribute token [rw] OTP token required by the institution
74
+ # @!attribute encryption_key [rw] Custom encryption key
75
+ # @!attribute attach_pdf [rw] Should the PDF file be included in the
76
+ # response or not.
77
+ class StatementOptions < Faraday::Options.new(
78
+ :token,
79
+ :encryption_key,
80
+ :save_data,
81
+ :attach_pdf
82
+ )
83
+ end
84
+
85
+ # @!class InvoiceOptions < Faraday::Options
86
+ # Contains configurable properties of an Invoice
87
+ # @!attribute save_data [rw] Should data be persisted or not.
88
+ # @!attribute token [rw] OTP token required by the institution
89
+ # @!attribute encryption_key [rw] Custom encryption key
90
+ # @!attribute attach_xml [rw] Should the XML file be included in the
91
+ # response or not.
92
+ class InvoiceOptions < Faraday::Options.new(
93
+ :save_data,
94
+ :token,
95
+ :encryption_key,
96
+ :attach_xml
97
+ )
98
+ end
99
+
100
+ # @!class TaxReturnOptions < Faraday::Options
101
+ # Contains configurable properties of a TaxReturn
102
+ # @!attribute save_data [rw] Should data be persisted or not.
103
+ # @!attribute token [rw] OTP token required by the institution
104
+ # @!attribute encryption_key [rw] Custom encryption key
105
+ # @!attribute attach_pdf [rw] Should the PDF file be included in the
106
+ # response or not.
107
+ class TaxReturnOptions < Faraday::Options.new(
108
+ :token,
109
+ :encryption_key,
110
+ :save_data,
111
+ :attach_pdf
112
+ )
113
+ end
114
+ end
@@ -0,0 +1,343 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'belvo/options'
5
+
6
+ module Belvo
7
+ # Represents a consumable REST resource from Belvo API
8
+ class Resource
9
+ # Resource API endpoint
10
+ # @return [String] the API endpoint
11
+ attr_reader :endpoint
12
+
13
+ # @param session [APISession] current Belvo API session
14
+ def initialize(session)
15
+ @session = session
16
+ end
17
+
18
+ # Remove nil values from a request body
19
+ # @param body [Hash] request body
20
+ # @return [Hash] body without nil values
21
+ def clean(body:)
22
+ body.delete_if { |_key, value| value.nil? }
23
+ end
24
+
25
+ # List all results
26
+ # @return [Array]
27
+ # @raise [RequestError] If response code is different than 2XX
28
+ def list
29
+ results = []
30
+ @session.list(@endpoint) { |item| results.push item }
31
+ results
32
+ end
33
+
34
+ # Show specific resource details
35
+ # @param id [String] Resource UUID
36
+ # @return [Hash]
37
+ # @raise [RequestError] If response code is different than 2XX
38
+ def detail(id:)
39
+ @session.detail(@endpoint, id)
40
+ end
41
+
42
+ # Delete existing resource
43
+ # @param id [String] Resource UUID
44
+ # @return [Boolean] true if resource is successfully deleted else false
45
+ def delete(id:)
46
+ @session.delete(@endpoint, id)
47
+ end
48
+
49
+ # Resume data extraction session. Use this method after you have received a
50
+ # HTTP 428 response.
51
+ # @param session_id [String] Session UUID included in the 428 response
52
+ # @param link [String, nil] Link UUID
53
+ # @raise [RequestError] If response code is different than 2XX
54
+ def resume(session_id:, token:, link: nil)
55
+ data = { session: session_id, token: token, link: link }
56
+ @session.patch(@endpoint, data)
57
+ end
58
+ end
59
+
60
+ # A Link is a set of credentials associated to a end-user access
61
+ class Link < Resource
62
+ class AccessMode
63
+ # Use single to do ad hoc one-time requests
64
+ SINGLE = 'single'
65
+ # Use recurrent to have Belvo refresh your link data on a daily basis
66
+ RECURRENT = 'recurrent'
67
+ end
68
+
69
+ def initialize(session)
70
+ super(session)
71
+ @endpoint = 'links/'
72
+ end
73
+
74
+ # Register a new link
75
+ # @param institution [String] Institution name
76
+ # @param username [String] End-user username
77
+ # @param password [String] End-user password
78
+ # @param password2 [String, nil] End-user secondary password, if any
79
+ # @param options [LinkOptions] Configurable properties
80
+ # @return [Hash] created link details
81
+ # @raise [RequestError] If response code is different than 2XX
82
+ def register(
83
+ institution:,
84
+ username:,
85
+ password:,
86
+ password2: nil,
87
+ options: nil
88
+ )
89
+ options = LinkOptions.from(options)
90
+ body = {
91
+ institution: institution,
92
+ username: username,
93
+ password: password,
94
+ password2: password2,
95
+ token: options.token,
96
+ encryption_key: options.encryption_key,
97
+ access_mode: options.access_mode || AccessMode::SINGLE
98
+ }.merge(options)
99
+ body = clean body: body
100
+ @session.post(@endpoint, body)
101
+ end
102
+
103
+ # Allows to change password, password2 or setting a custom encryption key
104
+ # @param id [String] Link UUID
105
+ # @param password [String] End-user password
106
+ # @param password2 [String, nil] End-user secondary password, if any
107
+ # @param options [LinkOptions] Configurable properties
108
+ # @return [Hash] link details
109
+ # @raise [RequestError] If response code is different than 2XX
110
+ def update(id:, password:, password2: nil, options: nil)
111
+ options = LinkOptions.from(options)
112
+ body = {
113
+ password: password,
114
+ password2: password2,
115
+ token: options.token,
116
+ encryption_key: options.encryption_key
117
+ }.merge(options)
118
+ body = clean body: body
119
+ @session.put(@endpoint, id, body)
120
+ end
121
+ end
122
+
123
+ # An Account is the representation of a bank account inside a financial
124
+ # institution.
125
+ class Account < Resource
126
+ def initialize(session)
127
+ super(session)
128
+ @endpoint = 'accounts/'
129
+ end
130
+
131
+ # Retrieve accounts from an existing link
132
+ # @param link [String] Link UUID
133
+ # @param options [AccountOptions] Configurable properties
134
+ # @return [Hash] created link details
135
+ # @raise [RequestError] If response code is different than 2XX
136
+ def retrieve(link:, options: nil)
137
+ options = AccountOptions.from(options)
138
+ body = {
139
+ link: link,
140
+ token: options.token,
141
+ encryption_key: options.encryption_key,
142
+ save_data: options.save_data || true
143
+ }.merge(options)
144
+ body = clean body: body
145
+ @session.post(@endpoint, body)
146
+ end
147
+ end
148
+
149
+ # A Transaction contains the detailed information of each movement inside an
150
+ # Account.
151
+ class Transaction < Resource
152
+ def initialize(session)
153
+ super(session)
154
+ @endpoint = 'transactions/'
155
+ end
156
+
157
+ # Retrieve transactions from an existing link
158
+ # @param link [String] Link UUID
159
+ # @param date_from [String] Date string (YYYY-MM-DD)
160
+ # @param options [TransactionOptions] Configurable properties
161
+ # @return [Hash] created link details
162
+ # @raise [RequestError] If response code is different than 2XX
163
+ def retrieve(link:, date_from:, options: nil)
164
+ options = TransactionOptions.from(options)
165
+ date_to = options.date_to || Date.today.to_s
166
+ body = {
167
+ link: link,
168
+ date_from: date_from,
169
+ date_to: date_to,
170
+ token: options.token,
171
+ account: options.account,
172
+ encryption_key: options.encryption_key,
173
+ save_data: options.save_data || true
174
+ }.merge(options)
175
+ body = clean body: body
176
+ @session.post(@endpoint, body)
177
+ end
178
+ end
179
+
180
+ # An Owner represents the person who has access to a Link and is the owner
181
+ # of all the Accounts inside the Link
182
+ class Owner < Resource
183
+ def initialize(session)
184
+ super(session)
185
+ @endpoint = 'owners/'
186
+ end
187
+
188
+ # Retrieve owners from an existing link
189
+ # @param link [String] Link UUID
190
+ # @param options [OwnerOptions] Configurable properties
191
+ # @return [Hash] created link details
192
+ # @raise [RequestError] If response code is different than 2XX
193
+ def retrieve(link:, options: nil)
194
+ options = OwnerOptions.from(options)
195
+ body = {
196
+ link: link,
197
+ token: options.token,
198
+ encryption_key: options.encryption_key,
199
+ save_data: options.save_data || true
200
+ }.merge(options)
201
+ body = clean body: body
202
+ @session.post(@endpoint, body)
203
+ end
204
+ end
205
+
206
+ # A Balance represents the financial status of an Account at a given time.
207
+ class Balance < Resource
208
+ def initialize(session)
209
+ super(session)
210
+ @endpoint = 'balances/'
211
+ end
212
+
213
+ # Retrieve balances from a specific account or all accounts from a
214
+ # specific link
215
+ # @param link [String] Link UUID
216
+ # @param date_from [String] Date string (YYYY-MM-DD)
217
+ # @param options [BalanceOptions] Configurable properties
218
+ # @return [Hash] created link details
219
+ # @raise [RequestError] If response code is different than 2XX
220
+ def retrieve(link:, date_from:, options: nil)
221
+ options = BalanceOptions.from(options)
222
+ date_to = options.date_to || Date.today.to_s
223
+ body = {
224
+ link: link,
225
+ date_from: date_from,
226
+ date_to: date_to,
227
+ token: options.token,
228
+ account: options.account,
229
+ encryption_key: options.encryption_key,
230
+ save_data: options.save_data || true
231
+ }.merge(options)
232
+ body = clean body: body
233
+ @session.post(@endpoint, body)
234
+ end
235
+ end
236
+
237
+ # A Statement contains a resume of monthly Transactions inside an Account.
238
+ class Statement < Resource
239
+ def initialize(session)
240
+ super(session)
241
+ @endpoint = 'statements/'
242
+ end
243
+
244
+ # Retrieve statements information from a specific banking link.
245
+ # @param link [String] Link UUID
246
+ # @param year [Integer]
247
+ # @param month [Integer]
248
+ # @param options [StatementOptions] Configurable properties
249
+ # @return [Hash] created link details
250
+ # @raise [RequestError] If response code is different than 2XX
251
+ def retrieve(link:, account:, year:, month:, options: nil)
252
+ options = StatementOptions.from(options)
253
+ body = {
254
+ link: link,
255
+ account: account,
256
+ year: year,
257
+ month: month,
258
+ token: options.token,
259
+ encryption_key: options.encryption_key,
260
+ save_data: options.save_data || true,
261
+ attach_pdf: options.attach_pdf
262
+ }.merge(options)
263
+ body = clean body: body
264
+ @session.post(@endpoint, body)
265
+ end
266
+ end
267
+
268
+ # An Invoice is the representation of an electronic invoice, that can be
269
+ # received or sent, by a business or an individual and has been uploaded
270
+ # to the fiscal institution website
271
+ class Invoice < Resource
272
+ def initialize(session)
273
+ super(session)
274
+ @endpoint = 'invoices/'
275
+ end
276
+
277
+ # @param link [String] Link UUID
278
+ # @param date_from [String] Date string (YYYY-MM-DD)
279
+ # @param date_to [String] Date string (YYYY-MM-DD)
280
+ # @param options [InvoiceOptions] Configurable properties
281
+ # @return [Hash] created link details
282
+ # @raise [RequestError] If response code is different than 2XX
283
+ def retrieve(link:, date_from:, date_to:, type:, options: nil)
284
+ options = InvoiceOptions.from(options)
285
+ body = {
286
+ link: link,
287
+ date_from: date_from,
288
+ date_to: date_to,
289
+ type: type,
290
+ token: options.token,
291
+ encryption_key: options.encryption_key,
292
+ save_data: options.save_data || true,
293
+ attach_xml: options.attach_xml
294
+ }.merge(options)
295
+ body = clean body: body
296
+ @session.post(@endpoint, body)
297
+ end
298
+ end
299
+
300
+ # A Tax return is the representation of the tax return document sent every
301
+ # year by a person or a business to the tax authority in the country.
302
+ class TaxReturn < Resource
303
+ def initialize(session)
304
+ super(session)
305
+ @endpoint = 'tax-returns/'
306
+ end
307
+
308
+ # Retrieve tax returns information from a specific fiscal link.
309
+ # @param link [String] Link UUID
310
+ # @param year_from [Integer]
311
+ # @param year_to [Integer]
312
+ # @param options [TaxReturnOptions] Configurable properties
313
+ # @return [Hash] created link details
314
+ # @raise [RequestError] If response code is different than 2XX
315
+ def retrieve(link:, year_from:, year_to:, options: nil)
316
+ options = TaxReturnOptions.from(options)
317
+ body = {
318
+ link: link,
319
+ year_from: year_from,
320
+ year_to: year_to,
321
+ token: options.token,
322
+ encryption_key: options.encryption_key,
323
+ save_data: options.save_data || true,
324
+ attach_pdf: options.attach_pdf
325
+ }.merge(options)
326
+ body = clean body: body
327
+ @session.post(@endpoint, body)
328
+ end
329
+
330
+ def resume(_session_id, _token, _link: nil)
331
+ raise NotImplementedError 'TaxReturn does not support resuming a session.'
332
+ end
333
+ end
334
+
335
+ # An Institution is an entity that Belvo can access information from. It can
336
+ # be a bank or fiscal type of institutions such as the SAT in Mexico.
337
+ class Institution < Resource
338
+ def initialize(session)
339
+ super(session)
340
+ @endpoint = 'institutions/'
341
+ end
342
+ end
343
+ end