belvo 0.3.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.
@@ -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,344 @@
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
+ # @param params [Hash] Extra parameters sent as query strings.
27
+ # @return [Array]
28
+ # @raise [RequestError] If response code is different than 2XX
29
+ def list(params: nil)
30
+ results = []
31
+ @session.list(@endpoint, params: params) { |item| results.push item }
32
+ results
33
+ end
34
+
35
+ # Show specific resource details
36
+ # @param id [String] Resource UUID
37
+ # @return [Hash]
38
+ # @raise [RequestError] If response code is different than 2XX
39
+ def detail(id:)
40
+ @session.detail(@endpoint, id)
41
+ end
42
+
43
+ # Delete existing resource
44
+ # @param id [String] Resource UUID
45
+ # @return [Boolean] true if resource is successfully deleted else false
46
+ def delete(id:)
47
+ @session.delete(@endpoint, id)
48
+ end
49
+
50
+ # Resume data extraction session. Use this method after you have received a
51
+ # HTTP 428 response.
52
+ # @param session_id [String] Session UUID included in the 428 response
53
+ # @param link [String, nil] Link UUID
54
+ # @raise [RequestError] If response code is different than 2XX
55
+ def resume(session_id:, token:, link: nil)
56
+ data = { session: session_id, token: token, link: link }
57
+ @session.patch(@endpoint, data)
58
+ end
59
+ end
60
+
61
+ # A Link is a set of credentials associated to a end-user access
62
+ class Link < Resource
63
+ class AccessMode
64
+ # Use single to do ad hoc one-time requests
65
+ SINGLE = 'single'
66
+ # Use recurrent to have Belvo refresh your link data on a daily basis
67
+ RECURRENT = 'recurrent'
68
+ end
69
+
70
+ def initialize(session)
71
+ super(session)
72
+ @endpoint = 'links/'
73
+ end
74
+
75
+ # Register a new link
76
+ # @param institution [String] Institution name
77
+ # @param username [String] End-user username
78
+ # @param password [String] End-user password
79
+ # @param password2 [String, nil] End-user secondary password, if any
80
+ # @param options [LinkOptions] Configurable properties
81
+ # @return [Hash] created link details
82
+ # @raise [RequestError] If response code is different than 2XX
83
+ def register(
84
+ institution:,
85
+ username:,
86
+ password:,
87
+ password2: nil,
88
+ options: nil
89
+ )
90
+ options = LinkOptions.from(options)
91
+ body = {
92
+ institution: institution,
93
+ username: username,
94
+ password: password,
95
+ password2: password2,
96
+ token: options.token,
97
+ encryption_key: options.encryption_key,
98
+ access_mode: options.access_mode || AccessMode::SINGLE
99
+ }.merge(options)
100
+ body = clean body: body
101
+ @session.post(@endpoint, body)
102
+ end
103
+
104
+ # Allows to change password, password2 or setting a custom encryption key
105
+ # @param id [String] Link UUID
106
+ # @param password [String] End-user password
107
+ # @param password2 [String, nil] End-user secondary password, if any
108
+ # @param options [LinkOptions] Configurable properties
109
+ # @return [Hash] link details
110
+ # @raise [RequestError] If response code is different than 2XX
111
+ def update(id:, password:, password2: nil, options: nil)
112
+ options = LinkOptions.from(options)
113
+ body = {
114
+ password: password,
115
+ password2: password2,
116
+ token: options.token,
117
+ encryption_key: options.encryption_key
118
+ }.merge(options)
119
+ body = clean body: body
120
+ @session.put(@endpoint, id, body)
121
+ end
122
+ end
123
+
124
+ # An Account is the representation of a bank account inside a financial
125
+ # institution.
126
+ class Account < Resource
127
+ def initialize(session)
128
+ super(session)
129
+ @endpoint = 'accounts/'
130
+ end
131
+
132
+ # Retrieve accounts from an existing link
133
+ # @param link [String] Link UUID
134
+ # @param options [AccountOptions] Configurable properties
135
+ # @return [Hash] created link details
136
+ # @raise [RequestError] If response code is different than 2XX
137
+ def retrieve(link:, options: nil)
138
+ options = AccountOptions.from(options)
139
+ body = {
140
+ link: link,
141
+ token: options.token,
142
+ encryption_key: options.encryption_key,
143
+ save_data: options.save_data || true
144
+ }.merge(options)
145
+ body = clean body: body
146
+ @session.post(@endpoint, body)
147
+ end
148
+ end
149
+
150
+ # A Transaction contains the detailed information of each movement inside an
151
+ # Account.
152
+ class Transaction < Resource
153
+ def initialize(session)
154
+ super(session)
155
+ @endpoint = 'transactions/'
156
+ end
157
+
158
+ # Retrieve transactions from an existing link
159
+ # @param link [String] Link UUID
160
+ # @param date_from [String] Date string (YYYY-MM-DD)
161
+ # @param options [TransactionOptions] Configurable properties
162
+ # @return [Hash] created link details
163
+ # @raise [RequestError] If response code is different than 2XX
164
+ def retrieve(link:, date_from:, options: nil)
165
+ options = TransactionOptions.from(options)
166
+ date_to = options.date_to || Date.today.to_s
167
+ body = {
168
+ link: link,
169
+ date_from: date_from,
170
+ date_to: date_to,
171
+ token: options.token,
172
+ account: options.account,
173
+ encryption_key: options.encryption_key,
174
+ save_data: options.save_data || true
175
+ }.merge(options)
176
+ body = clean body: body
177
+ @session.post(@endpoint, body)
178
+ end
179
+ end
180
+
181
+ # An Owner represents the person who has access to a Link and is the owner
182
+ # of all the Accounts inside the Link
183
+ class Owner < Resource
184
+ def initialize(session)
185
+ super(session)
186
+ @endpoint = 'owners/'
187
+ end
188
+
189
+ # Retrieve owners from an existing link
190
+ # @param link [String] Link UUID
191
+ # @param options [OwnerOptions] Configurable properties
192
+ # @return [Hash] created link details
193
+ # @raise [RequestError] If response code is different than 2XX
194
+ def retrieve(link:, options: nil)
195
+ options = OwnerOptions.from(options)
196
+ body = {
197
+ link: link,
198
+ token: options.token,
199
+ encryption_key: options.encryption_key,
200
+ save_data: options.save_data || true
201
+ }.merge(options)
202
+ body = clean body: body
203
+ @session.post(@endpoint, body)
204
+ end
205
+ end
206
+
207
+ # A Balance represents the financial status of an Account at a given time.
208
+ class Balance < Resource
209
+ def initialize(session)
210
+ super(session)
211
+ @endpoint = 'balances/'
212
+ end
213
+
214
+ # Retrieve balances from a specific account or all accounts from a
215
+ # specific link
216
+ # @param link [String] Link UUID
217
+ # @param date_from [String] Date string (YYYY-MM-DD)
218
+ # @param options [BalanceOptions] Configurable properties
219
+ # @return [Hash] created link details
220
+ # @raise [RequestError] If response code is different than 2XX
221
+ def retrieve(link:, date_from:, options: nil)
222
+ options = BalanceOptions.from(options)
223
+ date_to = options.date_to || Date.today.to_s
224
+ body = {
225
+ link: link,
226
+ date_from: date_from,
227
+ date_to: date_to,
228
+ token: options.token,
229
+ account: options.account,
230
+ encryption_key: options.encryption_key,
231
+ save_data: options.save_data || true
232
+ }.merge(options)
233
+ body = clean body: body
234
+ @session.post(@endpoint, body)
235
+ end
236
+ end
237
+
238
+ # A Statement contains a resume of monthly Transactions inside an Account.
239
+ class Statement < Resource
240
+ def initialize(session)
241
+ super(session)
242
+ @endpoint = 'statements/'
243
+ end
244
+
245
+ # Retrieve statements information from a specific banking link.
246
+ # @param link [String] Link UUID
247
+ # @param year [Integer]
248
+ # @param month [Integer]
249
+ # @param options [StatementOptions] Configurable properties
250
+ # @return [Hash] created link details
251
+ # @raise [RequestError] If response code is different than 2XX
252
+ def retrieve(link:, account:, year:, month:, options: nil)
253
+ options = StatementOptions.from(options)
254
+ body = {
255
+ link: link,
256
+ account: account,
257
+ year: year,
258
+ month: month,
259
+ token: options.token,
260
+ encryption_key: options.encryption_key,
261
+ save_data: options.save_data || true,
262
+ attach_pdf: options.attach_pdf
263
+ }.merge(options)
264
+ body = clean body: body
265
+ @session.post(@endpoint, body)
266
+ end
267
+ end
268
+
269
+ # An Invoice is the representation of an electronic invoice, that can be
270
+ # received or sent, by a business or an individual and has been uploaded
271
+ # to the fiscal institution website
272
+ class Invoice < Resource
273
+ def initialize(session)
274
+ super(session)
275
+ @endpoint = 'invoices/'
276
+ end
277
+
278
+ # @param link [String] Link UUID
279
+ # @param date_from [String] Date string (YYYY-MM-DD)
280
+ # @param date_to [String] Date string (YYYY-MM-DD)
281
+ # @param options [InvoiceOptions] Configurable properties
282
+ # @return [Hash] created link details
283
+ # @raise [RequestError] If response code is different than 2XX
284
+ def retrieve(link:, date_from:, date_to:, type:, options: nil)
285
+ options = InvoiceOptions.from(options)
286
+ body = {
287
+ link: link,
288
+ date_from: date_from,
289
+ date_to: date_to,
290
+ type: type,
291
+ token: options.token,
292
+ encryption_key: options.encryption_key,
293
+ save_data: options.save_data || true,
294
+ attach_xml: options.attach_xml
295
+ }.merge(options)
296
+ body = clean body: body
297
+ @session.post(@endpoint, body)
298
+ end
299
+ end
300
+
301
+ # A Tax return is the representation of the tax return document sent every
302
+ # year by a person or a business to the tax authority in the country.
303
+ class TaxReturn < Resource
304
+ def initialize(session)
305
+ super(session)
306
+ @endpoint = 'tax-returns/'
307
+ end
308
+
309
+ # Retrieve tax returns information from a specific fiscal link.
310
+ # @param link [String] Link UUID
311
+ # @param year_from [Integer]
312
+ # @param year_to [Integer]
313
+ # @param options [TaxReturnOptions] Configurable properties
314
+ # @return [Hash] created link details
315
+ # @raise [RequestError] If response code is different than 2XX
316
+ def retrieve(link:, year_from:, year_to:, options: nil)
317
+ options = TaxReturnOptions.from(options)
318
+ body = {
319
+ link: link,
320
+ year_from: year_from,
321
+ year_to: year_to,
322
+ token: options.token,
323
+ encryption_key: options.encryption_key,
324
+ save_data: options.save_data || true,
325
+ attach_pdf: options.attach_pdf
326
+ }.merge(options)
327
+ body = clean body: body
328
+ @session.post(@endpoint, body)
329
+ end
330
+
331
+ def resume(_session_id, _token, _link: nil)
332
+ raise NotImplementedError 'TaxReturn does not support resuming a session.'
333
+ end
334
+ end
335
+
336
+ # An Institution is an entity that Belvo can access information from. It can
337
+ # be a bank or fiscal type of institutions such as the SAT in Mexico.
338
+ class Institution < Resource
339
+ def initialize(session)
340
+ super(session)
341
+ @endpoint = 'institutions/'
342
+ end
343
+ end
344
+ end