From 41491c113bbc38a5a752d8269edc1694429aa397 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 08:14:42 -0400 Subject: [PATCH 01/72] feature(api): add support for v2 endpoint --- lib/fintoc/client.rb | 21 +++++++++++++-------- lib/fintoc/constants.rb | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 7576ec0..9d71918 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -19,18 +19,22 @@ def initialize(api_key) @default_params = {} end - def get - request('get') + def get(version: :v1) + request('get', version: version) end - def delete - request('delete') + def delete(version: :v1) + request('delete', version: version) end - def request(method) + def get_v2 + get(version: :v2) + end + + def request(method, version: :v1) proc do |resource, **kwargs| parameters = params(method, **kwargs) - response = make_request(method, resource, parameters) + response = make_request(method, resource, parameters, version: version) content = JSON.parse(response.body, symbolize_names: true) if response.status.client_error? || response.status.server_error? @@ -102,13 +106,14 @@ def build_link(data) Link.new(**data, client: self) end - def make_request(method, resource, parameters) + def make_request(method, resource, parameters, version: :v1) # this is to handle url returned in the link headers # I'm sure there is a better and more clever way to solve this if resource.start_with? 'https' client.send(method, resource) else - url = "#{Fintoc::Constants::SCHEME}#{Fintoc::Constants::BASE_URL}#{resource}" + base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL + url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" client.send(method, url, parameters) end end diff --git a/lib/fintoc/constants.rb b/lib/fintoc/constants.rb index 5e27a62..83ea22b 100644 --- a/lib/fintoc/constants.rb +++ b/lib/fintoc/constants.rb @@ -4,5 +4,6 @@ module Constants GENERAL_DOC_URL = 'https://fintoc.com/docs' SCHEME = 'https://' BASE_URL = 'api.fintoc.com/v1/' + BASE_URL_V2 = 'api.fintoc.com/v2/' end end From 96bbb895be90fbab0ce226f733606925cd3acc27 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 08:20:04 -0400 Subject: [PATCH 02/72] feature(entity): Add entity resource --- lib/fintoc/resources/entity.rb | 32 ++++++++++++++ .../fintoc/resources/fintoc/entity_spec.rb | 43 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 lib/fintoc/resources/entity.rb create mode 100644 spec/lib/fintoc/resources/fintoc/entity_spec.rb diff --git a/lib/fintoc/resources/entity.rb b/lib/fintoc/resources/entity.rb new file mode 100644 index 0000000..9ea2b7a --- /dev/null +++ b/lib/fintoc/resources/entity.rb @@ -0,0 +1,32 @@ +require 'fintoc/utils' + +module Fintoc + class Entity + include Utils + + attr_reader :object, :mode, :id, :holder_name, :holder_id, :is_root + + def initialize( + object:, + mode:, + id:, + holder_name:, + holder_id:, + is_root:, + client: nil, + ** + ) + @object = object + @mode = mode + @id = id + @holder_name = holder_name + @holder_id = holder_id + @is_root = is_root + @client = client + end + + def to_s + "🏒 #{@holder_name} (#{@id})" + end + end +end diff --git a/spec/lib/fintoc/resources/fintoc/entity_spec.rb b/spec/lib/fintoc/resources/fintoc/entity_spec.rb new file mode 100644 index 0000000..c5ba394 --- /dev/null +++ b/spec/lib/fintoc/resources/fintoc/entity_spec.rb @@ -0,0 +1,43 @@ +require 'fintoc/resources/entity' + +RSpec.describe Fintoc::Entity do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { Fintoc::Client.new(api_key) } + + let(:data) do + { + object: 'entity', + mode: 'test', + id: 'ent_12345', + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true, + client: client + } + end + + let(:entity) { described_class.new(**data) } + + describe '#new' do + it 'creates an instance of Entity' do + expect(entity).to be_an_instance_of(described_class) + end + + it 'sets all attributes correctly' do + expect(entity).to have_attributes( + object: 'entity', + mode: 'test', + id: 'ent_12345', + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true + ) + end + end + + describe '#to_s' do + it 'returns a formatted string representation' do + expect(entity.to_s).to eq('🏒 Test Company LLC (ent_12345)') + end + end +end From 9f09bf90474dca9fc73dd82545f47448f5104d2c Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 08:20:53 -0400 Subject: [PATCH 03/72] feature(entities): Add entities endpoints --- lib/fintoc/client.rb | 24 +++++++++++++ spec/lib/fintoc/client_spec.rb | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 9d71918..54b2aa2 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -2,6 +2,7 @@ require 'fintoc/utils' require 'fintoc/errors' require 'fintoc/resources/link' +require 'fintoc/resources/entity' require 'fintoc/constants' require 'fintoc/version' require 'json' @@ -73,6 +74,15 @@ def get_account(link_token, account_id) get_link(link_token).find(id: account_id) end + def get_entity(entity_id) + data = _get_entity(entity_id) + build_entity(data) + end + + def get_entities(**params) + _get_entities(**params).map { |data| build_entity(data) } + end + def to_s visible_chars = 4 hidden_part = '*' * (@api_key.size - visible_chars) @@ -106,6 +116,20 @@ def build_link(data) Link.new(**data, client: self) end + def _get_entity(entity_id) + get(version: :v2).call("entities/#{entity_id}") + end + + def _get_entities(**params) + response = get(version: :v2).call('entities', **params) + # Handle both single entity and list responses + response.is_a?(Array) ? response : [response] + end + + def build_entity(data) + Entity.new(**data, client: self) + end + def make_request(method, resource, parameters, version: :v1) # this is to handle url returned in the link headers # I'm sure there is a better and more clever way to solve this diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index bba2a11..0080195 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -45,4 +45,70 @@ end.to output(start_with('This links has 1 account')).to_stdout end end + + describe '#get_entity' do + let(:entity_id) { 'ent_12345' } + + let(:entity_response) do + { + object: 'entity', + mode: 'test', + id: entity_id, + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true + } + end + + before do + allow(client).to receive(:_get_entity).with(entity_id).and_return(entity_response) + end + + it 'returns an Entity instance' do + entity = client.get_entity(entity_id) + + expect(entity) + .to be_an_instance_of(Fintoc::Entity) + .and have_attributes( + id: entity_id, + holder_name: 'Test Company LLC' + ) + end + end + + describe '#get_entities' do + let(:entities_reponse) do + [ + { + object: 'entity', + mode: 'test', + id: 'ent_12345', + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true + }, + { + object: 'entity', + mode: 'test', + id: 'ent_67890', + holder_name: 'Another Company', + holder_id: '98765432-1', + is_root: false + } + ] + end + + before do + allow(client).to receive(:_get_entities).and_return(entities_reponse) + end + + it 'returns an array of Entity instances' do + entities = client.get_entities + + expect(entities).to all(be_a(Fintoc::Entity)) + expect(entities.size).to eq(2) + expect(entities.first.id).to eq('ent_12345') + expect(entities.last.id).to eq('ent_67890') + end + end end From 1133e619027dc659a79bd636d3a92585d83f8216 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 08:28:32 -0400 Subject: [PATCH 04/72] feature(entity): Add refresh method to entity --- lib/fintoc/resources/entity.rb | 7 +++++ .../fintoc/resources/fintoc/entity_spec.rb | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/fintoc/resources/entity.rb b/lib/fintoc/resources/entity.rb index 9ea2b7a..5fd8b32 100644 --- a/lib/fintoc/resources/entity.rb +++ b/lib/fintoc/resources/entity.rb @@ -28,5 +28,12 @@ def initialize( def to_s "🏒 #{@holder_name} (#{@id})" end + + def refresh + entity_data = @client.get_entity(@id) + initialize(**entity_data.instance_variables.each_with_object({}) do |var, hash| + hash[var.to_s.delete('@').to_sym] = entity_data.instance_variable_get(var) + end) + end end end diff --git a/spec/lib/fintoc/resources/fintoc/entity_spec.rb b/spec/lib/fintoc/resources/fintoc/entity_spec.rb index c5ba394..2279bf1 100644 --- a/spec/lib/fintoc/resources/fintoc/entity_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/entity_spec.rb @@ -40,4 +40,33 @@ expect(entity.to_s).to eq('🏒 Test Company LLC (ent_12345)') end end + + describe '#refresh' do + let(:updated_data) do + { + **data, + holder_name: 'Updated Company LLC' + } + end + + let(:updated_entity) { described_class.new(**updated_data, client: client) } + + before do + allow(client).to receive(:get_entity).with('ent_12345').and_return(updated_entity) + end + + it 'refreshes the entity with updated data from the API' do + expect(entity).to have_attributes( + holder_name: 'Test Company LLC' + ) + + entity.refresh + + expect(client).to have_received(:get_entity).with('ent_12345') + + expect(entity).to have_attributes( + holder_name: 'Updated Company LLC' + ) + end + end end From bcf090acb479aaa9f3080f9b95cf832532b72475 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 08:30:51 -0400 Subject: [PATCH 05/72] feature(entities): Add readme docs for entity --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 3da6ca7..d022a57 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Do yourself a favor: go grab some ice cubes by installing this refreshing librar - [Examples](#examples) - [Get accounts](#get-accounts) - [Get movements](#get-movements) + - [Get entities](#get-entities) - [Development](#development) - [Dependencies](#dependencies) - [Setup](#setup) @@ -146,6 +147,31 @@ account.get_movements(since: '2020-01-01', per_page: 100) Calling **get_movements** without arguments gets the last 30 movements of the account +### Get entities + +```ruby +require 'fintoc' + +client = Fintoc::Client.new('api_key') + +# Get all entities +entities = client.get_entities + +# Get a specific entity +entity = client.get_entity('ent_12345') + +puts entity.holder_name # => "My Company LLC" +puts entity.holder_id # => "12345678-9" +puts entity.is_root # => true +``` + +You can also list entities with pagination: + +```ruby +# Get entities with pagination +entities = client.get_entities(limit: 10, starting_after: 'ent_123') +``` + ## Development ### Dependencies From 11163b6460f18d2a0e016567195b069feba8034c Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 11:04:54 -0400 Subject: [PATCH 06/72] feature(client): Add post requests --- lib/fintoc/client.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 54b2aa2..5df2efb 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -28,6 +28,10 @@ def delete(version: :v1) request('delete', version: version) end + def post(version: :v1) + request('post', version: version) + end + def get_v2 get(version: :v2) end From 711d9f4d7683059512e45c9492c397fbb582f77a Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 11:50:43 -0400 Subject: [PATCH 07/72] refactor(movements): Move existing accounts to movements namespace --- lib/fintoc/resources/account.rb | 89 ------------------ lib/fintoc/resources/link.rb | 4 +- lib/fintoc/resources/movements/account.rb | 91 +++++++++++++++++++ spec/lib/fintoc/client_spec.rb | 4 +- spec/lib/fintoc/resources/fintoc/link_spec.rb | 2 +- .../fintoc/{ => movements}/account_spec.rb | 4 +- .../get_the_last_30_account_s_movements.yml | 0 ...get_account_s_movements_with_arguments.yml | 0 .../get_the_last_30_account_s_movements.yml | 0 .../update_account_s_movements.yml | 0 10 files changed, 98 insertions(+), 96 deletions(-) delete mode 100644 lib/fintoc/resources/account.rb create mode 100644 lib/fintoc/resources/movements/account.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/account_spec.rb (95%) rename spec/vcr/{Fintoc_Account => Fintoc_Movements_Account}/_get_movements/get_the_last_30_account_s_movements.yml (100%) rename spec/vcr/{Fintoc_Account => Fintoc_Movements_Account}/_movement_with_since_argument/get_account_s_movements_with_arguments.yml (100%) rename spec/vcr/{Fintoc_Account => Fintoc_Movements_Account}/_movements/get_the_last_30_account_s_movements.yml (100%) rename spec/vcr/{Fintoc_Account => Fintoc_Movements_Account}/_update_balance/update_account_s_movements.yml (100%) diff --git a/lib/fintoc/resources/account.rb b/lib/fintoc/resources/account.rb deleted file mode 100644 index db7b5de..0000000 --- a/lib/fintoc/resources/account.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'tabulate' -require 'fintoc/utils' -require 'fintoc/resources/movement' -require 'fintoc/resources/balance' - -module Fintoc - class Account - include Utils - - attr_reader :id, :name, :holder_name, :currency, :type, :refreshed_at, - :official_name, :number, :holder_id, :balance, :movements - - def initialize( - id:, - name:, - official_name:, - number:, - holder_id:, - holder_name:, - type:, - currency:, - refreshed_at: nil, - balance: nil, - movements: nil, - client: nil, - ** - ) - @id = id - @name = name - @official_name = official_name - @number = number - @holder_id = holder_id - @holder_name = holder_name - @type = type - @currency = currency - @refreshed_at = DateTime.iso8601(refreshed_at) if refreshed_at - @balance = Fintoc::Balance.new(**balance) - @movements = movements || [] - @client = client - end - - def update_balance - @balance = Fintoc::Balance.new(**get_account[:balance]) - end - - def get_movements(**params) - _get_movements(**params).lazy.map { |movement| Fintoc::Movement.new(**movement) } - end - - def update_movements(**params) - @movements += get_movements(**params).to_a - @movements = @movements.uniq.sort_by(&:post_date) - end - - def show_movements(rows = 5) - puts("This account has #{Utils.pluralize(@movements.size, 'movement')}.") - - return unless @movements.any? - - movements = - @movements - .to_a - .slice(0, rows) - .map.with_index do |mov, index| - [index + 1, mov.amount, mov.currency, mov.description, mov.locale_date] - end - headers = ['#', 'Amount', 'Currency', 'Description', 'Date'] - puts - puts tabulate(headers, movements, indent: 4, style: 'fancy') - end - - def to_s - "πŸ’° #{@holder_name}’s #{@name} #{@balance}" - end - - private - - def get_account - @client.get.call("accounts/#{@id}") - end - - def _get_movements(**params) - first = @client.get.call("accounts/#{@id}/movements", **params) - return first if params.empty? - - first + Utils.flatten(@client.fetch_next) - end - end -end diff --git a/lib/fintoc/resources/link.rb b/lib/fintoc/resources/link.rb index a507487..fba142b 100644 --- a/lib/fintoc/resources/link.rb +++ b/lib/fintoc/resources/link.rb @@ -1,7 +1,7 @@ require 'date' require 'tabulate' require 'fintoc/utils' -require 'fintoc/resources/account' +require 'fintoc/resources/movements/account' require 'fintoc/resources/institution' module Fintoc @@ -32,7 +32,7 @@ def initialize( @accounts = if accounts.nil? [] else - accounts.map { |data| Fintoc::Account.new(**data, client: client) } + accounts.map { |data| Fintoc::Movements::Account.new(**data, client: client) } end @token = link_token @client = client diff --git a/lib/fintoc/resources/movements/account.rb b/lib/fintoc/resources/movements/account.rb new file mode 100644 index 0000000..569df52 --- /dev/null +++ b/lib/fintoc/resources/movements/account.rb @@ -0,0 +1,91 @@ +require 'tabulate' +require 'fintoc/utils' +require 'fintoc/resources/movement' +require 'fintoc/resources/balance' + +module Fintoc + module Movements + class Account + include Utils + + attr_reader :id, :name, :holder_name, :currency, :type, :refreshed_at, + :official_name, :number, :holder_id, :balance, :movements + + def initialize( + id:, + name:, + official_name:, + number:, + holder_id:, + holder_name:, + type:, + currency:, + refreshed_at: nil, + balance: nil, + movements: nil, + client: nil, + ** + ) + @id = id + @name = name + @official_name = official_name + @number = number + @holder_id = holder_id + @holder_name = holder_name + @type = type + @currency = currency + @refreshed_at = DateTime.iso8601(refreshed_at) if refreshed_at + @balance = Fintoc::Balance.new(**balance) + @movements = movements || [] + @client = client + end + + def update_balance + @balance = Fintoc::Balance.new(**get_account[:balance]) + end + + def get_movements(**params) + _get_movements(**params).lazy.map { |movement| Fintoc::Movement.new(**movement) } + end + + def update_movements(**params) + @movements += get_movements(**params).to_a + @movements = @movements.uniq.sort_by(&:post_date) + end + + def show_movements(rows = 5) + puts("This account has #{Utils.pluralize(@movements.size, 'movement')}.") + + return unless @movements.any? + + movements = + @movements + .to_a + .slice(0, rows) + .map.with_index do |mov, index| + [index + 1, mov.amount, mov.currency, mov.description, mov.locale_date] + end + headers = ['#', 'Amount', 'Currency', 'Description', 'Date'] + puts + puts tabulate(headers, movements, indent: 4, style: 'fancy') + end + + def to_s + "πŸ’° #{@holder_name}’s #{@name} #{@balance}" + end + + private + + def get_account + @client.get.call("accounts/#{@id}") + end + + def _get_movements(**params) + first = @client.get.call("accounts/#{@id}/movements", **params) + return first if params.empty? + + first + Utils.flatten(@client.fetch_next) + end + end + end +end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 0080195..9460caf 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -1,6 +1,6 @@ require 'fintoc/client' require 'fintoc/resources/link' -require 'fintoc/resources/account' +require 'fintoc/resources/movements/account' require 'fintoc/resources/movement' RSpec.describe Fintoc::Client do @@ -33,7 +33,7 @@ link = client.get_link(link_token) account = link.find(type: 'checking_account') returned_account = client.get_account(link_token, account.id) - expect(returned_account).to be_an_instance_of(Fintoc::Account) + expect(returned_account).to be_an_instance_of(Fintoc::Movements::Account) end end diff --git a/spec/lib/fintoc/resources/fintoc/link_spec.rb b/spec/lib/fintoc/resources/fintoc/link_spec.rb index cd2c678..1637b2a 100644 --- a/spec/lib/fintoc/resources/fintoc/link_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/link_spec.rb @@ -60,7 +60,7 @@ it 'returns and valid checking account if the arg is type: "checking_account"' do checking_account = link.find(type: 'checking_account') data_acc = data[:accounts][0] - expect(checking_account).to be_an_instance_of(Fintoc::Account) + expect(checking_account).to be_an_instance_of(Fintoc::Movements::Account) expect(checking_account.to_s) .to( eq( diff --git a/spec/lib/fintoc/resources/fintoc/account_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb similarity index 95% rename from spec/lib/fintoc/resources/fintoc/account_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/account_spec.rb index afb528b..cc7ef3b 100644 --- a/spec/lib/fintoc/resources/fintoc/account_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/account' +require 'fintoc/resources/movements/account' -RSpec.describe Fintoc::Account do +RSpec.describe Fintoc::Movements::Account do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } let(:client) { Fintoc::Client.new(api_key) } diff --git a/spec/vcr/Fintoc_Account/_get_movements/get_the_last_30_account_s_movements.yml b/spec/vcr/Fintoc_Movements_Account/_get_movements/get_the_last_30_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Account/_get_movements/get_the_last_30_account_s_movements.yml rename to spec/vcr/Fintoc_Movements_Account/_get_movements/get_the_last_30_account_s_movements.yml diff --git a/spec/vcr/Fintoc_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml b/spec/vcr/Fintoc_Movements_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml similarity index 100% rename from spec/vcr/Fintoc_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml rename to spec/vcr/Fintoc_Movements_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml diff --git a/spec/vcr/Fintoc_Account/_movements/get_the_last_30_account_s_movements.yml b/spec/vcr/Fintoc_Movements_Account/_movements/get_the_last_30_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Account/_movements/get_the_last_30_account_s_movements.yml rename to spec/vcr/Fintoc_Movements_Account/_movements/get_the_last_30_account_s_movements.yml diff --git a/spec/vcr/Fintoc_Account/_update_balance/update_account_s_movements.yml b/spec/vcr/Fintoc_Movements_Account/_update_balance/update_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Account/_update_balance/update_account_s_movements.yml rename to spec/vcr/Fintoc_Movements_Account/_update_balance/update_account_s_movements.yml From 23685d5cdb7b0a98313a9328b21150a562e66f16 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 12:05:35 -0400 Subject: [PATCH 08/72] refactor(entity): Move entities to Transfers folder --- lib/fintoc/client.rb | 4 +- lib/fintoc/resources/entity.rb | 39 ------------------ lib/fintoc/resources/transfers/entity.rb | 41 +++++++++++++++++++ spec/lib/fintoc/client_spec.rb | 4 +- .../fintoc/{ => transfers}/entity_spec.rb | 4 +- 5 files changed, 47 insertions(+), 45 deletions(-) delete mode 100644 lib/fintoc/resources/entity.rb create mode 100644 lib/fintoc/resources/transfers/entity.rb rename spec/lib/fintoc/resources/fintoc/{ => transfers}/entity_spec.rb (94%) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 5df2efb..67019ab 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -2,7 +2,7 @@ require 'fintoc/utils' require 'fintoc/errors' require 'fintoc/resources/link' -require 'fintoc/resources/entity' +require 'fintoc/resources/transfers/entity' require 'fintoc/constants' require 'fintoc/version' require 'json' @@ -131,7 +131,7 @@ def _get_entities(**params) end def build_entity(data) - Entity.new(**data, client: self) + Transfers::Entity.new(**data, client: self) end def make_request(method, resource, parameters, version: :v1) diff --git a/lib/fintoc/resources/entity.rb b/lib/fintoc/resources/entity.rb deleted file mode 100644 index 5fd8b32..0000000 --- a/lib/fintoc/resources/entity.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'fintoc/utils' - -module Fintoc - class Entity - include Utils - - attr_reader :object, :mode, :id, :holder_name, :holder_id, :is_root - - def initialize( - object:, - mode:, - id:, - holder_name:, - holder_id:, - is_root:, - client: nil, - ** - ) - @object = object - @mode = mode - @id = id - @holder_name = holder_name - @holder_id = holder_id - @is_root = is_root - @client = client - end - - def to_s - "🏒 #{@holder_name} (#{@id})" - end - - def refresh - entity_data = @client.get_entity(@id) - initialize(**entity_data.instance_variables.each_with_object({}) do |var, hash| - hash[var.to_s.delete('@').to_sym] = entity_data.instance_variable_get(var) - end) - end - end -end diff --git a/lib/fintoc/resources/transfers/entity.rb b/lib/fintoc/resources/transfers/entity.rb new file mode 100644 index 0000000..ebb5820 --- /dev/null +++ b/lib/fintoc/resources/transfers/entity.rb @@ -0,0 +1,41 @@ +require 'fintoc/utils' + +module Fintoc + module Transfers + class Entity + include Utils + + attr_reader :object, :mode, :id, :holder_name, :holder_id, :is_root + + def initialize( + object:, + mode:, + id:, + holder_name:, + holder_id:, + is_root:, + client: nil, + ** + ) + @object = object + @mode = mode + @id = id + @holder_name = holder_name + @holder_id = holder_id + @is_root = is_root + @client = client + end + + def to_s + "🏒 #{@holder_name} (#{@id})" + end + + def refresh + entity_data = @client.get_entity(@id) + initialize(**entity_data.instance_variables.each_with_object({}) do |var, hash| + hash[var.to_s.delete('@').to_sym] = entity_data.instance_variable_get(var) + end) + end + end + end +end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 9460caf..74fe7fb 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -68,7 +68,7 @@ entity = client.get_entity(entity_id) expect(entity) - .to be_an_instance_of(Fintoc::Entity) + .to be_an_instance_of(Fintoc::Transfers::Entity) .and have_attributes( id: entity_id, holder_name: 'Test Company LLC' @@ -105,7 +105,7 @@ it 'returns an array of Entity instances' do entities = client.get_entities - expect(entities).to all(be_a(Fintoc::Entity)) + expect(entities).to all(be_a(Fintoc::Transfers::Entity)) expect(entities.size).to eq(2) expect(entities.first.id).to eq('ent_12345') expect(entities.last.id).to eq('ent_67890') diff --git a/spec/lib/fintoc/resources/fintoc/entity_spec.rb b/spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb similarity index 94% rename from spec/lib/fintoc/resources/fintoc/entity_spec.rb rename to spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb index 2279bf1..e56d804 100644 --- a/spec/lib/fintoc/resources/fintoc/entity_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/entity' +require 'fintoc/resources/transfers/entity' -RSpec.describe Fintoc::Entity do +RSpec.describe Fintoc::Transfers::Entity do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } let(:client) { Fintoc::Client.new(api_key) } From 8604d26b03d2b627bb4956edf914a1df2193c8b9 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:02:15 -0400 Subject: [PATCH 09/72] refactor(clients): Refactor Clients to have separate clients for Movements and Transfers --- README.md | 57 +++++- lib/fintoc/client.rb | 169 ++---------------- lib/fintoc/clients/base_client.rb | 128 +++++++++++++ lib/fintoc/clients/movements_client.rb | 41 +++++ lib/fintoc/clients/transfers_client.rb | 33 ++++ spec/lib/fintoc/client_spec.rb | 117 ++++-------- .../fintoc/clients/movements_client_spec.rb | 61 +++++++ .../fintoc/clients/transfers_client_spec.rb | 91 ++++++++++ .../_account/get_a_link_account.yml | 0 .../_account/get_a_linked_account.yml | 0 .../_get_account/get_a_linked_account.yml | 0 .../prints_accounts_to_console.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 16 files changed, 454 insertions(+), 243 deletions(-) create mode 100644 lib/fintoc/clients/base_client.rb create mode 100644 lib/fintoc/clients/movements_client.rb create mode 100644 lib/fintoc/clients/transfers_client.rb create mode 100644 spec/lib/fintoc/clients/movements_client_spec.rb create mode 100644 spec/lib/fintoc/clients/transfers_client_spec.rb rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_account/get_a_link_account.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_account/get_a_linked_account.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_get_account/get_a_linked_account.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_get_accounts/prints_accounts_to_console.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_get_link/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_get_links/get_all_the_links_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_link/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Client => Fintoc_Clients_MovementsClient}/_links/get_all_the_links_from_a_given_link_token.yml (100%) diff --git a/README.md b/README.md index d022a57..03cc503 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ Do yourself a favor: go grab some ice cubes by installing this refreshing librar - [Table of contents](#table-of-contents) - [How to Install](#how-to-install) - [Quickstart](#quickstart) + - [Client Architecture](#client-architecture) + - [**Movements API Client**](#movements-api-client) + - [**Transfers API Client**](#transfers-api-client) + - [**Backward compatibility**](#backward-compatibility) - [Documentation](#documentation) - [Examples](#examples) - [Get accounts](#get-accounts) @@ -52,11 +56,12 @@ Or install it yourself as: ```ruby require 'fintoc' -client = Fintoc::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') -link = client.get_link('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') +movements_client = + Fintoc::Clients::MovementsClient.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') +link = movements_client.get_link('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') account = link.find(type: 'checking_account') -# Get the las 30 movements +# Get the last 30 movements movements = account.get_movements # Or get all the movements since a specific date @@ -65,6 +70,40 @@ movements = account.get_movements(since: '2020-08-15') And that’s it! +## Client Architecture + +The Fintoc Ruby client is organized into separate clients that mirror the official API structure: + +### **Movements API Client** + +```ruby +movements_client = Fintoc::Clients::MovementsClient.new('api_key') + +links = movements_client.get_links +link = movements_client.get_link('link_token') +movements_client.delete_link('link_id') +account = movements_client.get_account('link_token', 'account_id') +``` + +### **Transfers API Client** + +```ruby +transfers_client = Fintoc::Clients::TransfersClient.new('api_key') + +entities = transfers_client.get_entities +entity = transfers_client.get_entity('entity_id') +``` + +### **Backward compatibility** + +The previous `Fintoc::Client` class is kept for backward compatibility purposes. + +```ruby +client = Fintoc::Client.new('api_key') + +links = client.get_links +``` + ## Documentation This client supports all Fintoc API endpoints. For complete information about the API, head to the [docs](https://docs.fintoc.com/reference). @@ -76,7 +115,7 @@ This client supports all Fintoc API endpoints. For complete information about th ```ruby require 'fintoc' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::MovementsClient.new('api_key') link = client.get_link('link_token') puts link.accounts @@ -92,7 +131,7 @@ If you want to find a specific account in a link, you can use **find**. You can ```ruby require 'fintoc' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::MovementsClient.new('api_key') link = client.get_link('link_token') account = link.find(type: 'checking_account') @@ -108,7 +147,7 @@ You can also search for multiple accounts matching a specific criteria with **fi ```ruby require 'fintoc' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::MovementsClient.new('api_key') link = client.get_link('link_token') accounts = link.find_all(currency: 'CLP') ``` @@ -118,7 +157,7 @@ To update the account balance you can use **update_balance**: ```ruby require 'fintoc' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::MovementsClient.new('api_key') link = client.get_link('link_token') account = link.find(number: '1111111') account.update_balance @@ -130,7 +169,7 @@ account.update_balance require 'fintoc' require 'time' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::MovementsClient.new('api_key') link = client.get_link('link_token') account = link.find(type: 'checking_account') @@ -152,7 +191,7 @@ Calling **get_movements** without arguments gets the last 30 movements of the ac ```ruby require 'fintoc' -client = Fintoc::Client.new('api_key') +client = Fintoc::Clients::TransfersClient.new('api_key') # Get all entities entities = client.get_entities diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 67019ab..c790432 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -1,185 +1,42 @@ -require 'http' -require 'fintoc/utils' -require 'fintoc/errors' -require 'fintoc/resources/link' -require 'fintoc/resources/transfers/entity' -require 'fintoc/constants' -require 'fintoc/version' -require 'json' +require 'fintoc/clients/movements_client' +require 'fintoc/clients/transfers_client' module Fintoc class Client - include Utils + attr_reader :movements, :transfers def initialize(api_key) - @api_key = api_key - @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" - @headers = { Authorization: @api_key, 'User-Agent': @user_agent } - @link_headers = nil - @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' - @default_params = {} - end - - def get(version: :v1) - request('get', version: version) - end - - def delete(version: :v1) - request('delete', version: version) - end - - def post(version: :v1) - request('post', version: version) - end - - def get_v2 - get(version: :v2) - end - - def request(method, version: :v1) - proc do |resource, **kwargs| - parameters = params(method, **kwargs) - response = make_request(method, resource, parameters, version: version) - content = JSON.parse(response.body, symbolize_names: true) - - if response.status.client_error? || response.status.server_error? - raise_custom_error(content[:error]) - end - - @link_headers = response.headers.get('link') - content - end - end - - def fetch_next - next_ = link_headers['next'] - Enumerator.new do |yielder| - while next_ - yielder << get.call(next_) - next_ = link_headers['next'] - end - end + @movements = Clients::MovementsClient.new(api_key) + @transfers = Clients::TransfersClient.new(api_key) end + # Delegate common methods to maintain backward compatibility def get_link(link_token) - data = { **_get_link(link_token), link_token: link_token } - build_link(data) + @movements.get_link(link_token) end def get_links - _get_links.map { |data| build_link(data) } + @movements.get_links end def delete_link(link_id) - delete.call("links/#{link_id}") + @movements.delete_link(link_id) end def get_account(link_token, account_id) - get_link(link_token).find(id: account_id) + @movements.get_account(link_token, account_id) end def get_entity(entity_id) - data = _get_entity(entity_id) - build_entity(data) + @transfers.get_entity(entity_id) end def get_entities(**params) - _get_entities(**params).map { |data| build_entity(data) } + @transfers.get_entities(**params) end def to_s - visible_chars = 4 - hidden_part = '*' * (@api_key.size - visible_chars) - visible_key = @api_key.slice(0, visible_chars) - "Client(πŸ”‘=#{hidden_part + visible_key}" - end - - private - - def client - @client ||= HTTP.headers(@headers) - end - - def parse_headers(dict, link) - matches = link.strip.match(@link_header_pattern) - dict[matches[:rel]] = matches[:url] - dict - end - - def _get_link(link_token) - get.call("links/#{link_token}") - end - - def _get_links - get.call('links') - end - - def build_link(data) - param = Utils.pick(data, 'link_token') - @default_params.update(param) - Link.new(**data, client: self) - end - - def _get_entity(entity_id) - get(version: :v2).call("entities/#{entity_id}") - end - - def _get_entities(**params) - response = get(version: :v2).call('entities', **params) - # Handle both single entity and list responses - response.is_a?(Array) ? response : [response] - end - - def build_entity(data) - Transfers::Entity.new(**data, client: self) - end - - def make_request(method, resource, parameters, version: :v1) - # this is to handle url returned in the link headers - # I'm sure there is a better and more clever way to solve this - if resource.start_with? 'https' - client.send(method, resource) - else - base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL - url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" - client.send(method, url, parameters) - end - end - - def params(method, **kwargs) - if method == 'get' - { params: { **@default_params, **kwargs } } - else - { json: { **@default_params, **kwargs } } - end - end - - def raise_custom_error(error) - raise error_class(error[:code]).new(error[:message], error[:doc_url]) - end - - def error_class(snake_code) - pascal_klass_name = Utils.snake_to_pascal(snake_code) - # this conditional klass_name is to handle InternalServerError custom error class - # without this the error class name would be like InternalServerErrorError (^-^) - klass = pascal_klass_name.end_with?('Error') ? pascal_klass_name : "#{pascal_klass_name}Error" - Module.const_get("Fintoc::Errors::#{klass}") - end - - # rubocop:disable Layout/LineLength - # This attribute getter parses the link headers using some regex 24K magic in the air... - # Ex. - # ; rel="first", ; rel="last" - # this helps to handle pagination see: https://fintoc.com/docs#paginacion - # return a hash like { first:"https://api.fintoc.com/v1/links?page=1" } - # - # @param link_headers [String] - # @return [Hash] - # rubocop:enable Layout/LineLength - def link_headers - return if @link_headers.nil? - - @link_headers[0].split(',').reduce({}) { |dict, link| parse_headers(dict, link) } + "Fintoc::Client(movements: #{@movements}, transfers: #{@transfers})" end end end diff --git a/lib/fintoc/clients/base_client.rb b/lib/fintoc/clients/base_client.rb new file mode 100644 index 0000000..26dc36f --- /dev/null +++ b/lib/fintoc/clients/base_client.rb @@ -0,0 +1,128 @@ +require 'http' +require 'json' +require 'fintoc/utils' +require 'fintoc/errors' +require 'fintoc/constants' +require 'fintoc/version' + +module Fintoc + module Clients + class BaseClient + include Utils + + def initialize(api_key) + @api_key = api_key + @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" + @headers = { Authorization: @api_key, 'User-Agent': @user_agent } + @link_headers = nil + @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' + @default_params = {} + end + + def get(version: :v1) + request('get', version: version) + end + + def delete(version: :v1) + request('delete', version: version) + end + + def post(version: :v1) + request('post', version: version) + end + + def request(method, version: :v1) + proc do |resource, **kwargs| + parameters = params(method, **kwargs) + response = make_request(method, resource, parameters, version: version) + content = JSON.parse(response.body, symbolize_names: true) + + if response.status.client_error? || response.status.server_error? + raise_custom_error(content[:error]) + end + + @link_headers = response.headers.get('link') + content + end + end + + def fetch_next + next_ = link_headers['next'] + Enumerator.new do |yielder| + while next_ + yielder << get.call(next_) + next_ = link_headers['next'] + end + end + end + + def to_s + visible_chars = 4 + hidden_part = '*' * (@api_key.size - visible_chars) + visible_key = @api_key.slice(0, visible_chars) + "#{self.class.name}(πŸ”‘=#{hidden_part + visible_key})" + end + + private + + def client + @client ||= HTTP.headers(@headers) + end + + def parse_headers(dict, link) + matches = link.strip.match(@link_header_pattern) + dict[matches[:rel]] = matches[:url] + dict + end + + def make_request(method, resource, parameters, version: :v1) + # this is to handle url returned in the link headers + # I'm sure there is a better and more clever way to solve this + if resource.start_with? 'https' + client.send(method, resource) + else + base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL + url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" + client.send(method, url, parameters) + end + end + + def params(method, **kwargs) + if method == 'get' + { params: { **@default_params, **kwargs } } + else + { json: { **@default_params, **kwargs } } + end + end + + def raise_custom_error(error) + raise error_class(error[:code]).new(error[:message], error[:doc_url]) + end + + def error_class(snake_code) + pascal_klass_name = Utils.snake_to_pascal(snake_code) + # this conditional klass_name is to handle InternalServerError custom error class + # without this the error class name would be like InternalServerErrorError (^-^) + klass = + pascal_klass_name.end_with?('Error') ? pascal_klass_name : "#{pascal_klass_name}Error" + Module.const_get("Fintoc::Errors::#{klass}") + end + + # rubocop:disable Layout/LineLength + # This attribute getter parses the link headers using some regex 24K magic in the air... + # Ex. + # ; rel="first", ; rel="last" + # this helps to handle pagination see: https://fintoc.com/docs#paginacion + # return a hash like { first:"https://api.fintoc.com/v1/links?page=1" } + # + # @param link_headers [String] + # @return [Hash] + # rubocop:enable Layout/LineLength + def link_headers + return if @link_headers.nil? + + @link_headers[0].split(',').reduce({}) { |dict, link| parse_headers(dict, link) } + end + end + end +end diff --git a/lib/fintoc/clients/movements_client.rb b/lib/fintoc/clients/movements_client.rb new file mode 100644 index 0000000..7159e11 --- /dev/null +++ b/lib/fintoc/clients/movements_client.rb @@ -0,0 +1,41 @@ +require 'fintoc/clients/base_client' +require 'fintoc/resources/link' + +module Fintoc + module Clients + class MovementsClient < BaseClient + def get_link(link_token) + data = { **_get_link(link_token), link_token: link_token } + build_link(data) + end + + def get_links + _get_links.map { |data| build_link(data) } + end + + def delete_link(link_id) + delete.call("links/#{link_id}") + end + + def get_account(link_token, account_id) + get_link(link_token).find(id: account_id) + end + + private + + def _get_link(link_token) + get.call("links/#{link_token}") + end + + def _get_links + get.call('links') + end + + def build_link(data) + param = Utils.pick(data, 'link_token') + @default_params.update(param) + Link.new(**data, client: self) + end + end + end +end diff --git a/lib/fintoc/clients/transfers_client.rb b/lib/fintoc/clients/transfers_client.rb new file mode 100644 index 0000000..942cf0d --- /dev/null +++ b/lib/fintoc/clients/transfers_client.rb @@ -0,0 +1,33 @@ +require 'fintoc/clients/base_client' +require 'fintoc/resources/transfers/entity' + +module Fintoc + module Clients + class TransfersClient < BaseClient + def get_entity(entity_id) + data = _get_entity(entity_id) + build_entity(data) + end + + def get_entities(**params) + _get_entities(**params).map { |data| build_entity(data) } + end + + private + + def _get_entity(entity_id) + get(version: :v2).call("entities/#{entity_id}") + end + + def _get_entities(**params) + response = get(version: :v2).call('entities', **params) + # Handle both single entity and list responses + response.is_a?(Array) ? response : [response] + end + + def build_entity(data) + Transfers::Entity.new(**data, client: self) + end + end + end +end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 74fe7fb..afdf0c6 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -5,110 +5,71 @@ RSpec.describe Fintoc::Client do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } let(:client) { described_class.new(api_key) } describe '.new' do it 'create an instance Client' do expect(client).to be_an_instance_of(described_class) end - end - describe '#get_link' do - it 'get the link from a given link token', :vcr do - link = client.get_link(link_token) - expect(link).to be_an_instance_of(Fintoc::Link) + it 'creates movements and transfers clients' do + expect(client.movements).to be_an_instance_of(Fintoc::Clients::MovementsClient) + expect(client.transfers).to be_an_instance_of(Fintoc::Clients::TransfersClient) end end - describe '#get_links' do - it 'get all the links from a given link token', :vcr do - links = client.get_links - expect(links).to all(be_a(Fintoc::Link)) + describe 'client separation' do + it 'allows direct access to movements client' do + expect(client.movements) + .to respond_to(:get_link) + .and respond_to(:get_links) + .and respond_to(:delete_link) + .and respond_to(:get_account) end - end - describe '#get_account' do - it 'get a linked account', :vcr do - link = client.get_link(link_token) - account = link.find(type: 'checking_account') - returned_account = client.get_account(link_token, account.id) - expect(returned_account).to be_an_instance_of(Fintoc::Movements::Account) + it 'allows direct access to transfers client' do + expect(client.transfers) + .to respond_to(:get_entity) + .and respond_to(:get_entities) end - end - describe '#get_accounts', :vcr do - it 'prints accounts to console' do - link = client.get_link(link_token) - expect do - link.show_accounts - end.to output(start_with('This links has 1 account')).to_stdout + it 'maintains backward compatibility through delegation' do + expect(client) + .to respond_to(:get_link) + .and respond_to(:get_links) + .and respond_to(:delete_link) + .and respond_to(:get_account) + .and respond_to(:get_entity) + .and respond_to(:get_entities) end end - describe '#get_entity' do - let(:entity_id) { 'ent_12345' } - - let(:entity_response) do - { - object: 'entity', - mode: 'test', - id: entity_id, - holder_name: 'Test Company LLC', - holder_id: '12345678-9', - is_root: true - } - end - + describe 'delegation to movements client' do before do - allow(client).to receive(:_get_entity).with(entity_id).and_return(entity_response) + allow(client.movements).to receive(:get_link).with('token').and_return('link') + allow(client.movements).to receive(:get_links).and_return(['links']) + allow(client.movements).to receive(:delete_link).with('link_id').and_return(true) + allow(client.movements) + .to receive(:get_account).with('token', 'account_id').and_return('account') end - it 'returns an Entity instance' do - entity = client.get_entity(entity_id) - - expect(entity) - .to be_an_instance_of(Fintoc::Transfers::Entity) - .and have_attributes( - id: entity_id, - holder_name: 'Test Company LLC' - ) + it 'delegates movements methods to movements client' do + expect(client.get_link('token')).to eq('link') + expect(client.get_links).to eq(['links']) + expect(client.delete_link('link_id')).to be(true) + expect(client.get_account('token', 'account_id')).to eq('account') end end - describe '#get_entities' do - let(:entities_reponse) do - [ - { - object: 'entity', - mode: 'test', - id: 'ent_12345', - holder_name: 'Test Company LLC', - holder_id: '12345678-9', - is_root: true - }, - { - object: 'entity', - mode: 'test', - id: 'ent_67890', - holder_name: 'Another Company', - holder_id: '98765432-1', - is_root: false - } - ] - end - + describe 'delegation to transfers client' do before do - allow(client).to receive(:_get_entities).and_return(entities_reponse) + allow(client.transfers).to receive(:get_entity).with('entity_id').and_return('entity') + allow(client.transfers).to receive(:get_entities).with(limit: 10).and_return(['entities']) end - it 'returns an array of Entity instances' do - entities = client.get_entities - - expect(entities).to all(be_a(Fintoc::Transfers::Entity)) - expect(entities.size).to eq(2) - expect(entities.first.id).to eq('ent_12345') - expect(entities.last.id).to eq('ent_67890') + it 'delegates transfers methods to transfers client' do + expect(client.get_entity('entity_id')).to eq('entity') + expect(client.get_entities(limit: 10)).to eq(['entities']) end end end diff --git a/spec/lib/fintoc/clients/movements_client_spec.rb b/spec/lib/fintoc/clients/movements_client_spec.rb new file mode 100644 index 0000000..7cfa54a --- /dev/null +++ b/spec/lib/fintoc/clients/movements_client_spec.rb @@ -0,0 +1,61 @@ +require 'fintoc/clients/movements_client' + +RSpec.describe Fintoc::Clients::MovementsClient do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { described_class.new(api_key) } + let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } + + describe '.new' do + it 'creates an instance of Clients::MovementsClient' do + expect(client).to be_an_instance_of(described_class) + end + end + + describe '#to_s' do + it 'returns a formatted string representation' do + expect(client.to_s) + .to include('Fintoc::Clients::MovementsClient') + .and include('πŸ”‘=') + end + end + + it 'responds to movements-specific methods' do + expect(client) + .to respond_to(:get_link) + .and respond_to(:get_links) + .and respond_to(:delete_link) + .and respond_to(:get_account) + end + + describe '#get_link' do + it 'get the link from a given link token', :vcr do + link = client.get_link(link_token) + expect(link).to be_an_instance_of(Fintoc::Link) + end + end + + describe '#get_links' do + it 'get all the links from a given link token', :vcr do + links = client.get_links + expect(links).to all(be_a(Fintoc::Link)) + end + end + + describe '#get_account' do + it 'get a linked account', :vcr do + link = client.get_link(link_token) + account = link.find(type: 'checking_account') + returned_account = client.get_account(link_token, account.id) + expect(returned_account).to be_an_instance_of(Fintoc::Movements::Account) + end + end + + describe '#get_accounts', :vcr do + it 'prints accounts to console' do + link = client.get_link(link_token) + expect do + link.show_accounts + end.to output(start_with('This links has 1 account')).to_stdout + end + end +end diff --git a/spec/lib/fintoc/clients/transfers_client_spec.rb b/spec/lib/fintoc/clients/transfers_client_spec.rb new file mode 100644 index 0000000..56326ad --- /dev/null +++ b/spec/lib/fintoc/clients/transfers_client_spec.rb @@ -0,0 +1,91 @@ +require 'fintoc/clients/transfers_client' + +RSpec.describe Fintoc::Clients::TransfersClient do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { described_class.new(api_key) } + + describe '.new' do + it 'creates an instance of TransfersClient' do + expect(client).to be_an_instance_of(described_class) + end + end + + describe '#to_s' do + it 'returns a formatted string representation' do + expect(client.to_s) + .to include('Fintoc::Clients::TransfersClient') + .and include('πŸ”‘=') + end + end + + it 'responds to transfers-specific methods' do + expect(client) + .to respond_to(:get_entity) + .and respond_to(:get_entities) + end + + describe '#get_entity' do + let(:entity_id) { 'ent_12345' } + let(:entity_response) do + { + object: 'entity', + mode: 'test', + id: entity_id, + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true + } + end + + before do + allow(client).to receive(:_get_entity).with(entity_id).and_return(entity_response) + end + + it 'returns an Entity instance' do + entity = client.get_entity(entity_id) + + expect(entity) + .to be_an_instance_of(Fintoc::Transfers::Entity) + .and have_attributes( + id: entity_id, + holder_name: 'Test Company LLC' + ) + end + end + + describe '#get_entities' do + let(:entities_response) do + [ + { + object: 'entity', + mode: 'test', + id: 'ent_12345', + holder_name: 'Test Company LLC', + holder_id: '12345678-9', + is_root: true + }, + { + object: 'entity', + mode: 'test', + id: 'ent_67890', + holder_name: 'Another Company Inc', + holder_id: '98765432-1', + is_root: false + } + ] + end + + before do + allow(client).to receive(:_get_entities).and_return(entities_response) + end + + it 'returns an array of Entity instances' do + entities = client.get_entities + + expect(entities).to all(be_a(Fintoc::Transfers::Entity)) + expect(entities.size).to eq(2) + expect(entities.first.id).to eq('ent_12345') + expect(entities.last.id).to eq('ent_67890') + end + end +end diff --git a/spec/vcr/Fintoc_Client/_account/get_a_link_account.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_link_account.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_account/get_a_link_account.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_link_account.yml diff --git a/spec/vcr/Fintoc_Client/_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_linked_account.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_account/get_a_linked_account.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_linked_account.yml diff --git a/spec/vcr/Fintoc_Client/_get_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_get_account/get_a_linked_account.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_get_account/get_a_linked_account.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_get_account/get_a_linked_account.yml diff --git a/spec/vcr/Fintoc_Client/_get_accounts/prints_accounts_to_console.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_get_accounts/prints_accounts_to_console.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_get_accounts/prints_accounts_to_console.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_get_accounts/prints_accounts_to_console.yml diff --git a/spec/vcr/Fintoc_Client/_get_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_get_link/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_get_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_get_link/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Client/_get_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_get_links/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_get_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_get_links/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Client/_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_link/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_link/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Client/_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Clients_MovementsClient/_links/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Client/_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Clients_MovementsClient/_links/get_all_the_links_from_a_given_link_token.yml From 7ac18efd6121f40e8d647449ef2fdd82a900080f Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:12:45 -0400 Subject: [PATCH 10/72] refactor(balance): Move balance to Movements namespace --- lib/fintoc/resources/balance.rb | 25 ----------------- lib/fintoc/resources/movements/account.rb | 6 ++--- lib/fintoc/resources/movements/balance.rb | 27 +++++++++++++++++++ .../fintoc/{ => movements}/balance_spec.rb | 4 +-- 4 files changed, 32 insertions(+), 30 deletions(-) delete mode 100644 lib/fintoc/resources/balance.rb create mode 100644 lib/fintoc/resources/movements/balance.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/balance_spec.rb (78%) diff --git a/lib/fintoc/resources/balance.rb b/lib/fintoc/resources/balance.rb deleted file mode 100644 index 33f49e9..0000000 --- a/lib/fintoc/resources/balance.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Fintoc - class Balance - attr_reader :available, :current, :limit - - def initialize(available:, current:, limit:) - @available = available - @current = current - @limit = limit - end - - def id - object_id - end - - def to_s - "#{@available} (#{@current})" - end - - def inspect - "" - end - end -end diff --git a/lib/fintoc/resources/movements/account.rb b/lib/fintoc/resources/movements/account.rb index 569df52..6a87614 100644 --- a/lib/fintoc/resources/movements/account.rb +++ b/lib/fintoc/resources/movements/account.rb @@ -1,7 +1,7 @@ require 'tabulate' require 'fintoc/utils' require 'fintoc/resources/movement' -require 'fintoc/resources/balance' +require 'fintoc/resources/movements/balance' module Fintoc module Movements @@ -35,13 +35,13 @@ def initialize( @type = type @currency = currency @refreshed_at = DateTime.iso8601(refreshed_at) if refreshed_at - @balance = Fintoc::Balance.new(**balance) + @balance = Fintoc::Movements::Balance.new(**balance) @movements = movements || [] @client = client end def update_balance - @balance = Fintoc::Balance.new(**get_account[:balance]) + @balance = Fintoc::Movements::Balance.new(**get_account[:balance]) end def get_movements(**params) diff --git a/lib/fintoc/resources/movements/balance.rb b/lib/fintoc/resources/movements/balance.rb new file mode 100644 index 0000000..9e83490 --- /dev/null +++ b/lib/fintoc/resources/movements/balance.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Fintoc + module Movements + class Balance + attr_reader :available, :current, :limit + + def initialize(available:, current:, limit:) + @available = available + @current = current + @limit = limit + end + + def id + object_id + end + + def to_s + "#{@available} (#{@current})" + end + + def inspect + "" + end + end + end +end diff --git a/spec/lib/fintoc/resources/fintoc/balance_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb similarity index 78% rename from spec/lib/fintoc/resources/fintoc/balance_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb index 56a136a..b6b8499 100644 --- a/spec/lib/fintoc/resources/fintoc/balance_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/balance' +require 'fintoc/resources/movements/balance' -RSpec.describe Fintoc::Balance do +RSpec.describe Fintoc::Movements::Balance do let(:data) { { available: 1000, current: 500, limit: 10 } } let(:balance) { described_class.new(**data) } From 0fc1f9a69c91ba09c59434169e283bcad2b73744 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:14:17 -0400 Subject: [PATCH 11/72] refactor(institution): Move Institutions to Movements namespace --- lib/fintoc/resources/institution.rb | 19 ----------------- lib/fintoc/resources/link.rb | 4 ++-- lib/fintoc/resources/movements/institution.rb | 21 +++++++++++++++++++ lib/fintoc/resources/transfer_account.rb | 4 ++-- .../{ => movements}/institution_spec.rb | 4 ++-- 5 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 lib/fintoc/resources/institution.rb create mode 100644 lib/fintoc/resources/movements/institution.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/institution_spec.rb (79%) diff --git a/lib/fintoc/resources/institution.rb b/lib/fintoc/resources/institution.rb deleted file mode 100644 index dd4bb01..0000000 --- a/lib/fintoc/resources/institution.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Fintoc - class Institution - attr_reader :id, :name, :country - - def initialize(id:, name:, country:, **) - @id = id - @name = name - @country = country - end - - def to_s - "🏦 #{@name}" - end - - def inspect - "" - end - end -end diff --git a/lib/fintoc/resources/link.rb b/lib/fintoc/resources/link.rb index fba142b..829ee92 100644 --- a/lib/fintoc/resources/link.rb +++ b/lib/fintoc/resources/link.rb @@ -2,7 +2,7 @@ require 'tabulate' require 'fintoc/utils' require 'fintoc/resources/movements/account' -require 'fintoc/resources/institution' +require 'fintoc/resources/movements/institution' module Fintoc class Link @@ -26,7 +26,7 @@ def initialize( @id = id @username = username @holder_type = holder_type - @institution = Fintoc::Institution.new(**institution) + @institution = Fintoc::Movements::Institution.new(**institution) @created_at = Date.iso8601(created_at) @mode = mode @accounts = if accounts.nil? diff --git a/lib/fintoc/resources/movements/institution.rb b/lib/fintoc/resources/movements/institution.rb new file mode 100644 index 0000000..ad8dad8 --- /dev/null +++ b/lib/fintoc/resources/movements/institution.rb @@ -0,0 +1,21 @@ +module Fintoc + module Movements + class Institution + attr_reader :id, :name, :country + + def initialize(id:, name:, country:, **) + @id = id + @name = name + @country = country + end + + def to_s + "🏦 #{@name}" + end + + def inspect + "" + end + end + end +end diff --git a/lib/fintoc/resources/transfer_account.rb b/lib/fintoc/resources/transfer_account.rb index 804daa8..1e5f313 100644 --- a/lib/fintoc/resources/transfer_account.rb +++ b/lib/fintoc/resources/transfer_account.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/institution' +require 'fintoc/resources/movements/institution' module Fintoc class TransferAccount @@ -8,7 +8,7 @@ def initialize(holder_id:, holder_name:, number:, institution:, **) @holder_id = holder_id @holder_name = holder_name @number = number - @institution = institution && Fintoc::Institution.new(**institution) + @institution = institution && Fintoc::Movements::Institution.new(**institution) end def id diff --git a/spec/lib/fintoc/resources/fintoc/institution_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb similarity index 79% rename from spec/lib/fintoc/resources/fintoc/institution_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb index e9b1a16..69690aa 100644 --- a/spec/lib/fintoc/resources/fintoc/institution_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/institution' +require 'fintoc/resources/movements/institution' -RSpec.describe Fintoc::Institution do +RSpec.describe Fintoc::Movements::Institution do let(:data) do { id: 'cl_banco_de_chile', name: 'Banco de Chile', country: 'cl' } end From 83f33695ac36b3256663042ab09d7fc508823c47 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:16:32 -0400 Subject: [PATCH 12/72] refactor(links): Move links to Movements namespace --- lib/fintoc/clients/movements_client.rb | 4 +- lib/fintoc/resources/link.rb | 83 ------------------ lib/fintoc/resources/movements/link.rb | 85 +++++++++++++++++++ spec/lib/fintoc/client_spec.rb | 2 +- .../fintoc/clients/movements_client_spec.rb | 4 +- .../fintoc/{ => movements}/link_spec.rb | 4 +- 6 files changed, 92 insertions(+), 90 deletions(-) delete mode 100644 lib/fintoc/resources/link.rb create mode 100644 lib/fintoc/resources/movements/link.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/link_spec.rb (96%) diff --git a/lib/fintoc/clients/movements_client.rb b/lib/fintoc/clients/movements_client.rb index 7159e11..d5e85e1 100644 --- a/lib/fintoc/clients/movements_client.rb +++ b/lib/fintoc/clients/movements_client.rb @@ -1,5 +1,5 @@ require 'fintoc/clients/base_client' -require 'fintoc/resources/link' +require 'fintoc/resources/movements/link' module Fintoc module Clients @@ -34,7 +34,7 @@ def _get_links def build_link(data) param = Utils.pick(data, 'link_token') @default_params.update(param) - Link.new(**data, client: self) + Fintoc::Movements::Link.new(**data, client: self) end end end diff --git a/lib/fintoc/resources/link.rb b/lib/fintoc/resources/link.rb deleted file mode 100644 index 829ee92..0000000 --- a/lib/fintoc/resources/link.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'date' -require 'tabulate' -require 'fintoc/utils' -require 'fintoc/resources/movements/account' -require 'fintoc/resources/movements/institution' - -module Fintoc - class Link - attr_reader :id, :username, :holder_type, :institution, :created_at, :mode, - :accounts, :link_token - - include Utils - - def initialize( - id:, - username:, - holder_type:, - institution:, - created_at:, - mode:, - accounts: nil, - link_token: nil, - client: nil, - ** - ) - @id = id - @username = username - @holder_type = holder_type - @institution = Fintoc::Movements::Institution.new(**institution) - @created_at = Date.iso8601(created_at) - @mode = mode - @accounts = if accounts.nil? - [] - else - accounts.map { |data| Fintoc::Movements::Account.new(**data, client: client) } - end - @token = link_token - @client = client - end - - def find_all(**kwargs) - raise 'You must provide *exactly one* account field.' if kwargs.size != 1 - - field, value = kwargs.to_a.first - @accounts.select do |account| - account.send(field.to_sym) == value - end - end - - def find(**) - results = find_all(**) - results.any? ? results.first : nil - end - - def show_accounts(rows = 5) - puts "This links has #{Utils.pluralize(@accounts.size, 'account')}" - return unless @accounts.any? - - accounts = @accounts.to_a.slice(0, rows) - .map.with_index do |acc, index| - [index + 1, acc.name, acc.holder_name, acc.currency] - end - headers = ['#', 'Name', 'Holder', 'Currency'] - puts - puts tabulate(headers, accounts, indent: 4, style: 'fancy') - end - - def update_accounts - @accounts.each do |account| - account.update_balance - account.update_movements - end - end - - def delete - @client.delete_link(@id) - end - - def to_s - "<#{@username}@#{@institution.name}> πŸ”— " - end - end -end diff --git a/lib/fintoc/resources/movements/link.rb b/lib/fintoc/resources/movements/link.rb new file mode 100644 index 0000000..8ade34a --- /dev/null +++ b/lib/fintoc/resources/movements/link.rb @@ -0,0 +1,85 @@ +require 'date' +require 'tabulate' +require 'fintoc/utils' +require 'fintoc/resources/movements/account' +require 'fintoc/resources/movements/institution' + +module Fintoc + module Movements + class Link + attr_reader :id, :username, :holder_type, :institution, :created_at, :mode, + :accounts, :link_token + + include Utils + + def initialize( + id:, + username:, + holder_type:, + institution:, + created_at:, + mode:, + accounts: nil, + link_token: nil, + client: nil, + ** + ) + @id = id + @username = username + @holder_type = holder_type + @institution = Fintoc::Movements::Institution.new(**institution) + @created_at = Date.iso8601(created_at) + @mode = mode + @accounts = if accounts.nil? + [] + else + accounts.map { |data| Fintoc::Movements::Account.new(**data, client: client) } + end + @token = link_token + @client = client + end + + def find_all(**kwargs) + raise 'You must provide *exactly one* account field.' if kwargs.size != 1 + + field, value = kwargs.to_a.first + @accounts.select do |account| + account.send(field.to_sym) == value + end + end + + def find(**) + results = find_all(**) + results.any? ? results.first : nil + end + + def show_accounts(rows = 5) + puts "This links has #{Utils.pluralize(@accounts.size, 'account')}" + return unless @accounts.any? + + accounts = @accounts.to_a.slice(0, rows) + .map.with_index do |acc, index| + [index + 1, acc.name, acc.holder_name, acc.currency] + end + headers = ['#', 'Name', 'Holder', 'Currency'] + puts + puts tabulate(headers, accounts, indent: 4, style: 'fancy') + end + + def update_accounts + @accounts.each do |account| + account.update_balance + account.update_movements + end + end + + def delete + @client.delete_link(@id) + end + + def to_s + "<#{@username}@#{@institution.name}> πŸ”— " + end + end + end +end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index afdf0c6..2f4a75c 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -1,5 +1,5 @@ require 'fintoc/client' -require 'fintoc/resources/link' +require 'fintoc/resources/movements/link' require 'fintoc/resources/movements/account' require 'fintoc/resources/movement' diff --git a/spec/lib/fintoc/clients/movements_client_spec.rb b/spec/lib/fintoc/clients/movements_client_spec.rb index 7cfa54a..9400a2b 100644 --- a/spec/lib/fintoc/clients/movements_client_spec.rb +++ b/spec/lib/fintoc/clients/movements_client_spec.rb @@ -30,14 +30,14 @@ describe '#get_link' do it 'get the link from a given link token', :vcr do link = client.get_link(link_token) - expect(link).to be_an_instance_of(Fintoc::Link) + expect(link).to be_an_instance_of(Fintoc::Movements::Link) end end describe '#get_links' do it 'get all the links from a given link token', :vcr do links = client.get_links - expect(links).to all(be_a(Fintoc::Link)) + expect(links).to all(be_a(Fintoc::Movements::Link)) end end diff --git a/spec/lib/fintoc/resources/fintoc/link_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/link_spec.rb similarity index 96% rename from spec/lib/fintoc/resources/fintoc/link_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/link_spec.rb index 1637b2a..da0640f 100644 --- a/spec/lib/fintoc/resources/fintoc/link_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/link_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/link' +require 'fintoc/resources/movements/link' -RSpec.describe Fintoc::Link do +RSpec.describe Fintoc::Movements::Link do let(:data) do { id: 'nMNejK7BT8oGbvO4', From 4e7fd8cead90483d811b97698157735907340136 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:18:48 -0400 Subject: [PATCH 13/72] refactor(movements): Move movements to Movements namespace --- lib/fintoc/resources/movement.rb | 55 ------------------ lib/fintoc/resources/movements/account.rb | 4 +- lib/fintoc/resources/movements/movement.rb | 57 +++++++++++++++++++ spec/lib/fintoc/client_spec.rb | 2 +- .../fintoc/movements/account_spec.rb | 6 +- .../fintoc/{ => movements}/movement_spec.rb | 4 +- 6 files changed, 65 insertions(+), 63 deletions(-) delete mode 100644 lib/fintoc/resources/movement.rb create mode 100644 lib/fintoc/resources/movements/movement.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/movement_spec.rb (97%) diff --git a/lib/fintoc/resources/movement.rb b/lib/fintoc/resources/movement.rb deleted file mode 100644 index 02ee00e..0000000 --- a/lib/fintoc/resources/movement.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'date' -require 'fintoc/resources/transfer_account' - -module Fintoc - class Movement - attr_reader :id, :amount, :currency, :description, :reference_id, - :post_date, :transaction_date, :type, :recipient_account, - :sender_account, :account, :comment - - def initialize( - id:, - amount:, - currency:, - description:, - post_date:, - transaction_date:, - type:, - reference_id:, - recipient_account:, - sender_account:, - comment:, - ** - ) - @id = id - @amount = amount - @currency = currency - @description = description - @post_date = DateTime.iso8601(post_date) - @transaction_date = DateTime.iso8601(transaction_date) if transaction_date - @type = type - @reference_id = reference_id - @recipient_account = Fintoc::TransferAccount.new(**recipient_account) if recipient_account - @sender_account = Fintoc::TransferAccount.new(**sender_account) if sender_account - @comment = comment - end - - def ==(other) - @id = other.id - end - - alias eql? == - - def hash - @id.hash - end - - def locale_date - @post_date.strftime('%x') - end - - def to_s - "#{@amount} (#{@description} @ #{locale_date})" - end - end -end diff --git a/lib/fintoc/resources/movements/account.rb b/lib/fintoc/resources/movements/account.rb index 6a87614..a506bb5 100644 --- a/lib/fintoc/resources/movements/account.rb +++ b/lib/fintoc/resources/movements/account.rb @@ -1,6 +1,6 @@ require 'tabulate' require 'fintoc/utils' -require 'fintoc/resources/movement' +require 'fintoc/resources/movements/movement' require 'fintoc/resources/movements/balance' module Fintoc @@ -45,7 +45,7 @@ def update_balance end def get_movements(**params) - _get_movements(**params).lazy.map { |movement| Fintoc::Movement.new(**movement) } + _get_movements(**params).lazy.map { |movement| Fintoc::Movements::Movement.new(**movement) } end def update_movements(**params) diff --git a/lib/fintoc/resources/movements/movement.rb b/lib/fintoc/resources/movements/movement.rb new file mode 100644 index 0000000..aaaa191 --- /dev/null +++ b/lib/fintoc/resources/movements/movement.rb @@ -0,0 +1,57 @@ +require 'date' +require 'fintoc/resources/transfer_account' + +module Fintoc + module Movements + class Movement + attr_reader :id, :amount, :currency, :description, :reference_id, + :post_date, :transaction_date, :type, :recipient_account, + :sender_account, :account, :comment + + def initialize( + id:, + amount:, + currency:, + description:, + post_date:, + transaction_date:, + type:, + reference_id:, + recipient_account:, + sender_account:, + comment:, + ** + ) + @id = id + @amount = amount + @currency = currency + @description = description + @post_date = DateTime.iso8601(post_date) + @transaction_date = DateTime.iso8601(transaction_date) if transaction_date + @type = type + @reference_id = reference_id + @recipient_account = Fintoc::TransferAccount.new(**recipient_account) if recipient_account + @sender_account = Fintoc::TransferAccount.new(**sender_account) if sender_account + @comment = comment + end + + def ==(other) + @id = other.id + end + + alias eql? == + + def hash + @id.hash + end + + def locale_date + @post_date.strftime('%x') + end + + def to_s + "#{@amount} (#{@description} @ #{locale_date})" + end + end + end +end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 2f4a75c..ceee859 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -1,7 +1,7 @@ require 'fintoc/client' require 'fintoc/resources/movements/link' require 'fintoc/resources/movements/account' -require 'fintoc/resources/movement' +require 'fintoc/resources/movements/movement' RSpec.describe Fintoc::Client do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } diff --git a/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb index cc7ef3b..0ce2db7 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb @@ -49,7 +49,7 @@ it "get the last 30 account's movements", :vcr do movements = linked_account.get_movements expect(movements.size).to be <= 30 - expect(movements).to all(be_a(Fintoc::Movement)) + expect(movements).to all(be_a(Fintoc::Movements::Movement)) end end @@ -57,14 +57,14 @@ it "get account's movements with arguments", :vcr do movements = linked_account.get_movements(since: '2020-08-15') linked_account.show_movements - expect(movements).to all(be_a(Fintoc::Movement)) + expect(movements).to all(be_a(Fintoc::Movements::Movement)) end end describe '#update_balance' do it "update account's movements", :vcr do movements = linked_account.update_movements - expect(movements).to all(be_a(Fintoc::Movement)) + expect(movements).to all(be_a(Fintoc::Movements::Movement)) end end end diff --git a/spec/lib/fintoc/resources/fintoc/movement_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb similarity index 97% rename from spec/lib/fintoc/resources/fintoc/movement_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb index f2e1245..c45f718 100644 --- a/spec/lib/fintoc/resources/fintoc/movement_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/movement' +require 'fintoc/resources/movements/movement' -RSpec.describe Fintoc::Movement do +RSpec.describe Fintoc::Movements::Movement do let(:data) do { id: 'BO381oEATXonG6bj', From 797ad99b94b61a7ba7d0221dac9e3c576fcb7d20 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 16:24:40 -0400 Subject: [PATCH 14/72] refactor(transfer-accounts): Move TransferAccounts to Movements namespace --- lib/fintoc/resources/movements/movement.rb | 9 ++++--- .../resources/movements/transfer_account.rb | 24 +++++++++++++++++++ lib/fintoc/resources/transfer_account.rb | 22 ----------------- .../{ => movements}/transfer_account_spec.rb | 4 ++-- 4 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 lib/fintoc/resources/movements/transfer_account.rb delete mode 100644 lib/fintoc/resources/transfer_account.rb rename spec/lib/fintoc/resources/fintoc/{ => movements}/transfer_account_spec.rb (78%) diff --git a/lib/fintoc/resources/movements/movement.rb b/lib/fintoc/resources/movements/movement.rb index aaaa191..4521cde 100644 --- a/lib/fintoc/resources/movements/movement.rb +++ b/lib/fintoc/resources/movements/movement.rb @@ -1,5 +1,5 @@ require 'date' -require 'fintoc/resources/transfer_account' +require 'fintoc/resources/movements/transfer_account' module Fintoc module Movements @@ -30,8 +30,11 @@ def initialize( @transaction_date = DateTime.iso8601(transaction_date) if transaction_date @type = type @reference_id = reference_id - @recipient_account = Fintoc::TransferAccount.new(**recipient_account) if recipient_account - @sender_account = Fintoc::TransferAccount.new(**sender_account) if sender_account + @recipient_account = + if recipient_account + Fintoc::Movements::TransferAccount.new(**recipient_account) + end + @sender_account = Fintoc::Movements::TransferAccount.new(**sender_account) if sender_account @comment = comment end diff --git a/lib/fintoc/resources/movements/transfer_account.rb b/lib/fintoc/resources/movements/transfer_account.rb new file mode 100644 index 0000000..ba92d64 --- /dev/null +++ b/lib/fintoc/resources/movements/transfer_account.rb @@ -0,0 +1,24 @@ +require 'fintoc/resources/movements/institution' + +module Fintoc + module Movements + class TransferAccount + attr_reader :holder_id, :holder_name, :number, :institution + + def initialize(holder_id:, holder_name:, number:, institution:, **) + @holder_id = holder_id + @holder_name = holder_name + @number = number + @institution = institution && Fintoc::Movements::Institution.new(**institution) + end + + def id + object_id + end + + def to_s + @holder_id.to_s + end + end + end +end diff --git a/lib/fintoc/resources/transfer_account.rb b/lib/fintoc/resources/transfer_account.rb deleted file mode 100644 index 1e5f313..0000000 --- a/lib/fintoc/resources/transfer_account.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'fintoc/resources/movements/institution' - -module Fintoc - class TransferAccount - attr_reader :holder_id, :holder_name, :number, :institution - - def initialize(holder_id:, holder_name:, number:, institution:, **) - @holder_id = holder_id - @holder_name = holder_name - @number = number - @institution = institution && Fintoc::Movements::Institution.new(**institution) - end - - def id - object_id - end - - def to_s - @holder_id.to_s - end - end -end diff --git a/spec/lib/fintoc/resources/fintoc/transfer_account_spec.rb b/spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb similarity index 78% rename from spec/lib/fintoc/resources/fintoc/transfer_account_spec.rb rename to spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb index efe0bec..ce5b727 100644 --- a/spec/lib/fintoc/resources/fintoc/transfer_account_spec.rb +++ b/spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/resources/transfer_account' +require 'fintoc/resources/movements/transfer_account' -RSpec.describe Fintoc::TransferAccount do +RSpec.describe Fintoc::Movements::TransferAccount do let(:data) do { holder_id: '771806538', From b0e8fc68ccd9f47f5bf33ab76408ff02883ccdb7 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 28 Aug 2025 18:35:29 -0400 Subject: [PATCH 15/72] refactor(fintoc): Restrutcture entire folder structure to have better navigability and more intuitive naming --- lib/fintoc/base_client.rb | 126 +++++++++++++++++ lib/fintoc/client.rb | 8 +- lib/fintoc/clients/base_client.rb | 128 ------------------ lib/fintoc/movements/client/client.rb | 10 ++ .../client/links_methods.rb} | 7 +- .../resources}/account.rb | 4 +- .../resources}/balance.rb | 0 .../resources}/institution.rb | 0 .../movements => movements/resources}/link.rb | 4 +- .../resources}/movement.rb | 2 +- .../resources}/transfer_account.rb | 2 +- lib/fintoc/transfers/client/client.rb | 10 ++ .../client/entities_methods.rb} | 9 +- .../resources}/entity.rb | 0 spec/lib/fintoc/client_spec.rb | 10 +- .../fintoc => }/movements/account_spec.rb | 2 +- .../fintoc => }/movements/balance_spec.rb | 2 +- spec/lib/fintoc/movements/client_spec.rb | 30 ++++ .../fintoc => }/movements/institution_spec.rb | 2 +- .../fintoc => }/movements/link_spec.rb | 2 +- .../fintoc/movements/links_methods_spec.rb | 47 +++++++ .../fintoc => }/movements/movement_spec.rb | 2 +- .../movements/transfer_account_spec.rb | 2 +- spec/lib/fintoc/transfers/client_spec.rb | 28 ++++ .../fintoc/transfers/entities_methods_spec.rb | 37 +++++ .../fintoc => }/transfers/entity_spec.rb | 2 +- spec/spec_helper.rb | 5 + .../clients/entities_client_examples.rb} | 23 +--- .../clients/links_client_examples.rb} | 22 +-- .../_account/get_a_link_account.yml | 0 .../_account/get_a_linked_account.yml | 0 .../_get_account/get_a_linked_account.yml | 0 .../prints_accounts_to_console.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 37 files changed, 326 insertions(+), 200 deletions(-) create mode 100644 lib/fintoc/base_client.rb delete mode 100644 lib/fintoc/clients/base_client.rb create mode 100644 lib/fintoc/movements/client/client.rb rename lib/fintoc/{clients/movements_client.rb => movements/client/links_methods.rb} (85%) rename lib/fintoc/{resources/movements => movements/resources}/account.rb (96%) rename lib/fintoc/{resources/movements => movements/resources}/balance.rb (100%) rename lib/fintoc/{resources/movements => movements/resources}/institution.rb (100%) rename lib/fintoc/{resources/movements => movements/resources}/link.rb (95%) rename lib/fintoc/{resources/movements => movements/resources}/movement.rb (96%) rename lib/fintoc/{resources/movements => movements/resources}/transfer_account.rb (91%) create mode 100644 lib/fintoc/transfers/client/client.rb rename lib/fintoc/{clients/transfers_client.rb => transfers/client/entities_methods.rb} (76%) rename lib/fintoc/{resources/transfers => transfers/resources}/entity.rb (100%) rename spec/lib/fintoc/{resources/fintoc => }/movements/account_spec.rb (97%) rename spec/lib/fintoc/{resources/fintoc => }/movements/balance_spec.rb (89%) create mode 100644 spec/lib/fintoc/movements/client_spec.rb rename spec/lib/fintoc/{resources/fintoc => }/movements/institution_spec.rb (89%) rename spec/lib/fintoc/{resources/fintoc => }/movements/link_spec.rb (98%) create mode 100644 spec/lib/fintoc/movements/links_methods_spec.rb rename spec/lib/fintoc/{resources/fintoc => }/movements/movement_spec.rb (98%) rename spec/lib/fintoc/{resources/fintoc => }/movements/transfer_account_spec.rb (89%) create mode 100644 spec/lib/fintoc/transfers/client_spec.rb create mode 100644 spec/lib/fintoc/transfers/entities_methods_spec.rb rename spec/lib/fintoc/{resources/fintoc => }/transfers/entity_spec.rb (97%) rename spec/{lib/fintoc/clients/transfers_client_spec.rb => support/shared_examples/clients/entities_client_examples.rb} (73%) rename spec/{lib/fintoc/clients/movements_client_spec.rb => support/shared_examples/clients/links_client_examples.rb} (66%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_account/get_a_link_account.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_account/get_a_linked_account.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_get_account/get_a_linked_account.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_get_accounts/prints_accounts_to_console.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_get_link/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_get_links/get_all_the_links_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_link/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Clients_MovementsClient => Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality}/_links/get_all_the_links_from_a_given_link_token.yml (100%) diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb new file mode 100644 index 0000000..78bc85e --- /dev/null +++ b/lib/fintoc/base_client.rb @@ -0,0 +1,126 @@ +require 'http' +require 'json' +require 'fintoc/utils' +require 'fintoc/errors' +require 'fintoc/constants' +require 'fintoc/version' + +module Fintoc + class BaseClient + include Utils + + def initialize(api_key) + @api_key = api_key + @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" + @headers = { Authorization: @api_key, 'User-Agent': @user_agent } + @link_headers = nil + @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' + @default_params = {} + end + + def get(version: :v1) + request('get', version: version) + end + + def delete(version: :v1) + request('delete', version: version) + end + + def post(version: :v1) + request('post', version: version) + end + + def request(method, version: :v1) + proc do |resource, **kwargs| + parameters = params(method, **kwargs) + response = make_request(method, resource, parameters, version: version) + content = JSON.parse(response.body, symbolize_names: true) + + if response.status.client_error? || response.status.server_error? + raise_custom_error(content[:error]) + end + + @link_headers = response.headers.get('link') + content + end + end + + def fetch_next + next_ = link_headers['next'] + Enumerator.new do |yielder| + while next_ + yielder << get.call(next_) + next_ = link_headers['next'] + end + end + end + + def to_s + visible_chars = 4 + hidden_part = '*' * (@api_key.size - visible_chars) + visible_key = @api_key.slice(0, visible_chars) + "#{self.class.name}(πŸ”‘=#{hidden_part + visible_key})" + end + + private + + def client + @client ||= HTTP.headers(@headers) + end + + def parse_headers(dict, link) + matches = link.strip.match(@link_header_pattern) + dict[matches[:rel]] = matches[:url] + dict + end + + def make_request(method, resource, parameters, version: :v1) + # this is to handle url returned in the link headers + # I'm sure there is a better and more clever way to solve this + if resource.start_with? 'https' + client.send(method, resource) + else + base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL + url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" + client.send(method, url, parameters) + end + end + + def params(method, **kwargs) + if method == 'get' + { params: { **@default_params, **kwargs } } + else + { json: { **@default_params, **kwargs } } + end + end + + def raise_custom_error(error) + raise error_class(error[:code]).new(error[:message], error[:doc_url]) + end + + def error_class(snake_code) + pascal_klass_name = Utils.snake_to_pascal(snake_code) + # this conditional klass_name is to handle InternalServerError custom error class + # without this the error class name would be like InternalServerErrorError (^-^) + klass = + pascal_klass_name.end_with?('Error') ? pascal_klass_name : "#{pascal_klass_name}Error" + Module.const_get("Fintoc::Errors::#{klass}") + end + + # rubocop:disable Layout/LineLength + # This attribute getter parses the link headers using some regex 24K magic in the air... + # Ex. + # ; rel="first", ; rel="last" + # this helps to handle pagination see: https://fintoc.com/docs#paginacion + # return a hash like { first:"https://api.fintoc.com/v1/links?page=1" } + # + # @param link_headers [String] + # @return [Hash] + # rubocop:enable Layout/LineLength + def link_headers + return if @link_headers.nil? + + @link_headers[0].split(',').reduce({}) { |dict, link| parse_headers(dict, link) } + end + end +end diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index c790432..7e2a302 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -1,13 +1,13 @@ -require 'fintoc/clients/movements_client' -require 'fintoc/clients/transfers_client' +require 'fintoc/movements/client/client' +require 'fintoc/transfers/client/client' module Fintoc class Client attr_reader :movements, :transfers def initialize(api_key) - @movements = Clients::MovementsClient.new(api_key) - @transfers = Clients::TransfersClient.new(api_key) + @movements = Fintoc::Movements::Client.new(api_key) + @transfers = Fintoc::Transfers::Client.new(api_key) end # Delegate common methods to maintain backward compatibility diff --git a/lib/fintoc/clients/base_client.rb b/lib/fintoc/clients/base_client.rb deleted file mode 100644 index 26dc36f..0000000 --- a/lib/fintoc/clients/base_client.rb +++ /dev/null @@ -1,128 +0,0 @@ -require 'http' -require 'json' -require 'fintoc/utils' -require 'fintoc/errors' -require 'fintoc/constants' -require 'fintoc/version' - -module Fintoc - module Clients - class BaseClient - include Utils - - def initialize(api_key) - @api_key = api_key - @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" - @headers = { Authorization: @api_key, 'User-Agent': @user_agent } - @link_headers = nil - @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' - @default_params = {} - end - - def get(version: :v1) - request('get', version: version) - end - - def delete(version: :v1) - request('delete', version: version) - end - - def post(version: :v1) - request('post', version: version) - end - - def request(method, version: :v1) - proc do |resource, **kwargs| - parameters = params(method, **kwargs) - response = make_request(method, resource, parameters, version: version) - content = JSON.parse(response.body, symbolize_names: true) - - if response.status.client_error? || response.status.server_error? - raise_custom_error(content[:error]) - end - - @link_headers = response.headers.get('link') - content - end - end - - def fetch_next - next_ = link_headers['next'] - Enumerator.new do |yielder| - while next_ - yielder << get.call(next_) - next_ = link_headers['next'] - end - end - end - - def to_s - visible_chars = 4 - hidden_part = '*' * (@api_key.size - visible_chars) - visible_key = @api_key.slice(0, visible_chars) - "#{self.class.name}(πŸ”‘=#{hidden_part + visible_key})" - end - - private - - def client - @client ||= HTTP.headers(@headers) - end - - def parse_headers(dict, link) - matches = link.strip.match(@link_header_pattern) - dict[matches[:rel]] = matches[:url] - dict - end - - def make_request(method, resource, parameters, version: :v1) - # this is to handle url returned in the link headers - # I'm sure there is a better and more clever way to solve this - if resource.start_with? 'https' - client.send(method, resource) - else - base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL - url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" - client.send(method, url, parameters) - end - end - - def params(method, **kwargs) - if method == 'get' - { params: { **@default_params, **kwargs } } - else - { json: { **@default_params, **kwargs } } - end - end - - def raise_custom_error(error) - raise error_class(error[:code]).new(error[:message], error[:doc_url]) - end - - def error_class(snake_code) - pascal_klass_name = Utils.snake_to_pascal(snake_code) - # this conditional klass_name is to handle InternalServerError custom error class - # without this the error class name would be like InternalServerErrorError (^-^) - klass = - pascal_klass_name.end_with?('Error') ? pascal_klass_name : "#{pascal_klass_name}Error" - Module.const_get("Fintoc::Errors::#{klass}") - end - - # rubocop:disable Layout/LineLength - # This attribute getter parses the link headers using some regex 24K magic in the air... - # Ex. - # ; rel="first", ; rel="last" - # this helps to handle pagination see: https://fintoc.com/docs#paginacion - # return a hash like { first:"https://api.fintoc.com/v1/links?page=1" } - # - # @param link_headers [String] - # @return [Hash] - # rubocop:enable Layout/LineLength - def link_headers - return if @link_headers.nil? - - @link_headers[0].split(',').reduce({}) { |dict, link| parse_headers(dict, link) } - end - end - end -end diff --git a/lib/fintoc/movements/client/client.rb b/lib/fintoc/movements/client/client.rb new file mode 100644 index 0000000..fef5bf2 --- /dev/null +++ b/lib/fintoc/movements/client/client.rb @@ -0,0 +1,10 @@ +require 'fintoc/base_client' +require 'fintoc/movements/client/links_methods' + +module Fintoc + module Movements + class Client < BaseClient + include LinksMethods + end + end +end diff --git a/lib/fintoc/clients/movements_client.rb b/lib/fintoc/movements/client/links_methods.rb similarity index 85% rename from lib/fintoc/clients/movements_client.rb rename to lib/fintoc/movements/client/links_methods.rb index d5e85e1..632b1b6 100644 --- a/lib/fintoc/clients/movements_client.rb +++ b/lib/fintoc/movements/client/links_methods.rb @@ -1,9 +1,8 @@ -require 'fintoc/clients/base_client' -require 'fintoc/resources/movements/link' +require 'fintoc/movements/resources/link' module Fintoc - module Clients - class MovementsClient < BaseClient + module Movements + module LinksMethods def get_link(link_token) data = { **_get_link(link_token), link_token: link_token } build_link(data) diff --git a/lib/fintoc/resources/movements/account.rb b/lib/fintoc/movements/resources/account.rb similarity index 96% rename from lib/fintoc/resources/movements/account.rb rename to lib/fintoc/movements/resources/account.rb index a506bb5..c76f1d9 100644 --- a/lib/fintoc/resources/movements/account.rb +++ b/lib/fintoc/movements/resources/account.rb @@ -1,7 +1,7 @@ require 'tabulate' require 'fintoc/utils' -require 'fintoc/resources/movements/movement' -require 'fintoc/resources/movements/balance' +require 'fintoc/movements/resources/movement' +require 'fintoc/movements/resources/balance' module Fintoc module Movements diff --git a/lib/fintoc/resources/movements/balance.rb b/lib/fintoc/movements/resources/balance.rb similarity index 100% rename from lib/fintoc/resources/movements/balance.rb rename to lib/fintoc/movements/resources/balance.rb diff --git a/lib/fintoc/resources/movements/institution.rb b/lib/fintoc/movements/resources/institution.rb similarity index 100% rename from lib/fintoc/resources/movements/institution.rb rename to lib/fintoc/movements/resources/institution.rb diff --git a/lib/fintoc/resources/movements/link.rb b/lib/fintoc/movements/resources/link.rb similarity index 95% rename from lib/fintoc/resources/movements/link.rb rename to lib/fintoc/movements/resources/link.rb index 8ade34a..ed55d21 100644 --- a/lib/fintoc/resources/movements/link.rb +++ b/lib/fintoc/movements/resources/link.rb @@ -1,8 +1,8 @@ require 'date' require 'tabulate' require 'fintoc/utils' -require 'fintoc/resources/movements/account' -require 'fintoc/resources/movements/institution' +require 'fintoc/movements/resources/account' +require 'fintoc/movements/resources/institution' module Fintoc module Movements diff --git a/lib/fintoc/resources/movements/movement.rb b/lib/fintoc/movements/resources/movement.rb similarity index 96% rename from lib/fintoc/resources/movements/movement.rb rename to lib/fintoc/movements/resources/movement.rb index 4521cde..fa444ca 100644 --- a/lib/fintoc/resources/movements/movement.rb +++ b/lib/fintoc/movements/resources/movement.rb @@ -1,5 +1,5 @@ require 'date' -require 'fintoc/resources/movements/transfer_account' +require 'fintoc/movements/resources/transfer_account' module Fintoc module Movements diff --git a/lib/fintoc/resources/movements/transfer_account.rb b/lib/fintoc/movements/resources/transfer_account.rb similarity index 91% rename from lib/fintoc/resources/movements/transfer_account.rb rename to lib/fintoc/movements/resources/transfer_account.rb index ba92d64..077bc2c 100644 --- a/lib/fintoc/resources/movements/transfer_account.rb +++ b/lib/fintoc/movements/resources/transfer_account.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/institution' +require 'fintoc/movements/resources/institution' module Fintoc module Movements diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb new file mode 100644 index 0000000..c08f2a0 --- /dev/null +++ b/lib/fintoc/transfers/client/client.rb @@ -0,0 +1,10 @@ +require 'fintoc/base_client' +require 'fintoc/transfers/client/entities_methods' + +module Fintoc + module Transfers + class Client < BaseClient + include EntitiesMethods + end + end +end diff --git a/lib/fintoc/clients/transfers_client.rb b/lib/fintoc/transfers/client/entities_methods.rb similarity index 76% rename from lib/fintoc/clients/transfers_client.rb rename to lib/fintoc/transfers/client/entities_methods.rb index 942cf0d..605de9a 100644 --- a/lib/fintoc/clients/transfers_client.rb +++ b/lib/fintoc/transfers/client/entities_methods.rb @@ -1,9 +1,8 @@ -require 'fintoc/clients/base_client' -require 'fintoc/resources/transfers/entity' +require 'fintoc/transfers/resources/entity' module Fintoc - module Clients - class TransfersClient < BaseClient + module Transfers + module EntitiesMethods def get_entity(entity_id) data = _get_entity(entity_id) build_entity(data) @@ -26,7 +25,7 @@ def _get_entities(**params) end def build_entity(data) - Transfers::Entity.new(**data, client: self) + Fintoc::Transfers::Entity.new(**data, client: self) end end end diff --git a/lib/fintoc/resources/transfers/entity.rb b/lib/fintoc/transfers/resources/entity.rb similarity index 100% rename from lib/fintoc/resources/transfers/entity.rb rename to lib/fintoc/transfers/resources/entity.rb diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index ceee859..169ca46 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -1,7 +1,7 @@ require 'fintoc/client' -require 'fintoc/resources/movements/link' -require 'fintoc/resources/movements/account' -require 'fintoc/resources/movements/movement' +require 'fintoc/movements/resources/link' +require 'fintoc/movements/resources/account' +require 'fintoc/movements/resources/movement' RSpec.describe Fintoc::Client do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } @@ -13,8 +13,8 @@ end it 'creates movements and transfers clients' do - expect(client.movements).to be_an_instance_of(Fintoc::Clients::MovementsClient) - expect(client.transfers).to be_an_instance_of(Fintoc::Clients::TransfersClient) + expect(client.movements).to be_an_instance_of(Fintoc::Movements::Client) + expect(client.transfers).to be_an_instance_of(Fintoc::Transfers::Client) end end diff --git a/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb b/spec/lib/fintoc/movements/account_spec.rb similarity index 97% rename from spec/lib/fintoc/resources/fintoc/movements/account_spec.rb rename to spec/lib/fintoc/movements/account_spec.rb index 0ce2db7..a900edd 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/account_spec.rb +++ b/spec/lib/fintoc/movements/account_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/account' +require 'fintoc/movements/resources/account' RSpec.describe Fintoc::Movements::Account do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } diff --git a/spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb b/spec/lib/fintoc/movements/balance_spec.rb similarity index 89% rename from spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb rename to spec/lib/fintoc/movements/balance_spec.rb index b6b8499..199d52a 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/balance_spec.rb +++ b/spec/lib/fintoc/movements/balance_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/balance' +require 'fintoc/movements/resources/balance' RSpec.describe Fintoc::Movements::Balance do let(:data) { { available: 1000, current: 500, limit: 10 } } diff --git a/spec/lib/fintoc/movements/client_spec.rb b/spec/lib/fintoc/movements/client_spec.rb new file mode 100644 index 0000000..d2a4026 --- /dev/null +++ b/spec/lib/fintoc/movements/client_spec.rb @@ -0,0 +1,30 @@ +require 'fintoc/movements/client/client' + +RSpec.describe Fintoc::Movements::Client do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { described_class.new(api_key) } + + describe '.new' do + it 'creates an instance of Clients::MovementsClient' do + expect(client).to be_an_instance_of(described_class) + end + end + + describe '#to_s' do + it 'returns a formatted string representation' do + expect(client.to_s) + .to include('Fintoc::Movements::Client') + .and include('πŸ”‘=') + end + end + + it 'responds to movements-specific methods' do + expect(client) + .to respond_to(:get_link) + .and respond_to(:get_links) + .and respond_to(:delete_link) + .and respond_to(:get_account) + end + + it_behaves_like 'a client with links methods' +end diff --git a/spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb b/spec/lib/fintoc/movements/institution_spec.rb similarity index 89% rename from spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb rename to spec/lib/fintoc/movements/institution_spec.rb index 69690aa..93e42f1 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/institution_spec.rb +++ b/spec/lib/fintoc/movements/institution_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/institution' +require 'fintoc/movements/resources/institution' RSpec.describe Fintoc::Movements::Institution do let(:data) do diff --git a/spec/lib/fintoc/resources/fintoc/movements/link_spec.rb b/spec/lib/fintoc/movements/link_spec.rb similarity index 98% rename from spec/lib/fintoc/resources/fintoc/movements/link_spec.rb rename to spec/lib/fintoc/movements/link_spec.rb index da0640f..5d357c0 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/link_spec.rb +++ b/spec/lib/fintoc/movements/link_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/link' +require 'fintoc/movements/resources/link' RSpec.describe Fintoc::Movements::Link do let(:data) do diff --git a/spec/lib/fintoc/movements/links_methods_spec.rb b/spec/lib/fintoc/movements/links_methods_spec.rb new file mode 100644 index 0000000..16b187b --- /dev/null +++ b/spec/lib/fintoc/movements/links_methods_spec.rb @@ -0,0 +1,47 @@ +require 'fintoc/movements/client/links_methods' + +RSpec.describe Fintoc::Movements::LinksMethods do + # Test class that includes the module for testing + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Movements::LinksMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def get(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def delete + proc { |_resource, **_kwargs| { mock: 'deleted' } } + end + + def pick(data, key) + { key => data[key] } if data[key] + end + end + end + + describe 'module inclusion' do + it 'provides link-related methods' do + expect(client) + .to respond_to(:get_link) + .and respond_to(:get_links) + .and respond_to(:delete_link) + .and respond_to(:get_account) + end + end + + describe 'private methods' do + it 'provides private helper methods' do + expect(client.private_methods) + .to include(:_get_link, :_get_links, :build_link) + end + end +end diff --git a/spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb b/spec/lib/fintoc/movements/movement_spec.rb similarity index 98% rename from spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb rename to spec/lib/fintoc/movements/movement_spec.rb index c45f718..6ba258b 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/movement_spec.rb +++ b/spec/lib/fintoc/movements/movement_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/movement' +require 'fintoc/movements/resources/movement' RSpec.describe Fintoc::Movements::Movement do let(:data) do diff --git a/spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb b/spec/lib/fintoc/movements/transfer_account_spec.rb similarity index 89% rename from spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb rename to spec/lib/fintoc/movements/transfer_account_spec.rb index ce5b727..eab3037 100644 --- a/spec/lib/fintoc/resources/fintoc/movements/transfer_account_spec.rb +++ b/spec/lib/fintoc/movements/transfer_account_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/movements/transfer_account' +require 'fintoc/movements/resources/transfer_account' RSpec.describe Fintoc::Movements::TransferAccount do let(:data) do diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb new file mode 100644 index 0000000..7848ae5 --- /dev/null +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -0,0 +1,28 @@ +require 'fintoc/transfers/client/client' + +RSpec.describe Fintoc::Transfers::Client do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { described_class.new(api_key) } + + describe '.new' do + it 'creates an instance of TransfersClient' do + expect(client).to be_an_instance_of(described_class) + end + end + + describe '#to_s' do + it 'returns a formatted string representation' do + expect(client.to_s) + .to include('Fintoc::Transfers::Client') + .and include('πŸ”‘=') + end + end + + it 'responds to transfers-specific methods' do + expect(client) + .to respond_to(:get_entity) + .and respond_to(:get_entities) + end + + it_behaves_like 'a client with entities methods' +end diff --git a/spec/lib/fintoc/transfers/entities_methods_spec.rb b/spec/lib/fintoc/transfers/entities_methods_spec.rb new file mode 100644 index 0000000..2fe143a --- /dev/null +++ b/spec/lib/fintoc/transfers/entities_methods_spec.rb @@ -0,0 +1,37 @@ +require 'fintoc/transfers/client/entities_methods' + +RSpec.describe Fintoc::Transfers::EntitiesMethods do + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Transfers::EntitiesMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def get(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + end + end + + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + + describe 'module inclusion' do + it 'provides entity-related methods' do + expect(client) + .to respond_to(:get_entity) + .and respond_to(:get_entities) + end + end + + describe 'private methods' do + it 'provides private helper methods' do + expect(client.private_methods) + .to include(:_get_entity, :_get_entities, :build_entity) + end + end +end diff --git a/spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb b/spec/lib/fintoc/transfers/entity_spec.rb similarity index 97% rename from spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb rename to spec/lib/fintoc/transfers/entity_spec.rb index e56d804..917ba32 100644 --- a/spec/lib/fintoc/resources/fintoc/transfers/entity_spec.rb +++ b/spec/lib/fintoc/transfers/entity_spec.rb @@ -1,4 +1,4 @@ -require 'fintoc/resources/transfers/entity' +require 'fintoc/transfers/resources/entity' RSpec.describe Fintoc::Transfers::Entity do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index de398e8..2e9f9d7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,11 @@ require 'fintoc' require 'vcr' +# Load shared examples +Dir[File.join(File.dirname(__FILE__), 'support', 'shared_examples', '**', '*.rb')].each do |f| + require f +end + VCR.configure do |c| vcr_mode = /rec/i.match?(ENV.fetch('VCR_MODE', nil)) ? :all : :once diff --git a/spec/lib/fintoc/clients/transfers_client_spec.rb b/spec/support/shared_examples/clients/entities_client_examples.rb similarity index 73% rename from spec/lib/fintoc/clients/transfers_client_spec.rb rename to spec/support/shared_examples/clients/entities_client_examples.rb index 56326ad..3dde3fd 100644 --- a/spec/lib/fintoc/clients/transfers_client_spec.rb +++ b/spec/support/shared_examples/clients/entities_client_examples.rb @@ -1,24 +1,5 @@ -require 'fintoc/clients/transfers_client' - -RSpec.describe Fintoc::Clients::TransfersClient do - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { described_class.new(api_key) } - - describe '.new' do - it 'creates an instance of TransfersClient' do - expect(client).to be_an_instance_of(described_class) - end - end - - describe '#to_s' do - it 'returns a formatted string representation' do - expect(client.to_s) - .to include('Fintoc::Clients::TransfersClient') - .and include('πŸ”‘=') - end - end - - it 'responds to transfers-specific methods' do +RSpec.shared_examples 'a client with entities methods' do + it 'responds to entity-specific methods' do expect(client) .to respond_to(:get_entity) .and respond_to(:get_entities) diff --git a/spec/lib/fintoc/clients/movements_client_spec.rb b/spec/support/shared_examples/clients/links_client_examples.rb similarity index 66% rename from spec/lib/fintoc/clients/movements_client_spec.rb rename to spec/support/shared_examples/clients/links_client_examples.rb index 9400a2b..58a45d3 100644 --- a/spec/lib/fintoc/clients/movements_client_spec.rb +++ b/spec/support/shared_examples/clients/links_client_examples.rb @@ -1,25 +1,7 @@ -require 'fintoc/clients/movements_client' - -RSpec.describe Fintoc::Clients::MovementsClient do - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { described_class.new(api_key) } +RSpec.shared_examples 'a client with links methods' do let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } - describe '.new' do - it 'creates an instance of Clients::MovementsClient' do - expect(client).to be_an_instance_of(described_class) - end - end - - describe '#to_s' do - it 'returns a formatted string representation' do - expect(client.to_s) - .to include('Fintoc::Clients::MovementsClient') - .and include('πŸ”‘=') - end - end - - it 'responds to movements-specific methods' do + it 'responds to link-specific methods' do expect(client) .to respond_to(:get_link) .and respond_to(:get_links) diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_link_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_link_account.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_account/get_a_linked_account.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_get_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_account/get_a_linked_account.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_get_account/get_a_linked_account.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_account/get_a_linked_account.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_get_accounts/prints_accounts_to_console.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_accounts/prints_accounts_to_console.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_get_accounts/prints_accounts_to_console.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_accounts/prints_accounts_to_console.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_get_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_link/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_get_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_link/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_get_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_links/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_get_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_links/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Clients_MovementsClient/_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Clients_MovementsClient/_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml From 0797291d2312d3a8ef47bcff177f14852b85631f Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 11:25:25 -0400 Subject: [PATCH 16/72] refactor(transfers-client): Update spec to mock api response rather than method response --- lib/fintoc/base_client.rb | 2 +- spec/lib/fintoc/transfers/client_spec.rb | 2 +- .../clients/entities_client_examples.rb | 54 ++----------- .../returns_an_array_of_Entity_instances.yml | 75 +++++++++++++++++++ .../returns_an_Entity_instance.yml | 73 ++++++++++++++++++ 5 files changed, 157 insertions(+), 49 deletions(-) create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index 78bc85e..c9773d6 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -12,7 +12,7 @@ class BaseClient def initialize(api_key) @api_key = api_key @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" - @headers = { Authorization: @api_key, 'User-Agent': @user_agent } + @headers = { Authorization: "Bearer #{@api_key}", 'User-Agent': @user_agent } @link_headers = nil @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' @default_params = {} diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index 7848ae5..f6d3049 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -1,7 +1,7 @@ require 'fintoc/transfers/client/client' RSpec.describe Fintoc::Transfers::Client do - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } let(:client) { described_class.new(api_key) } describe '.new' do diff --git a/spec/support/shared_examples/clients/entities_client_examples.rb b/spec/support/shared_examples/clients/entities_client_examples.rb index 3dde3fd..f88d071 100644 --- a/spec/support/shared_examples/clients/entities_client_examples.rb +++ b/spec/support/shared_examples/clients/entities_client_examples.rb @@ -1,4 +1,6 @@ RSpec.shared_examples 'a client with entities methods' do + let(:entity_id) { 'ent_31t0VhhrAXASFQTVYfCfIBnljbT' } + it 'responds to entity-specific methods' do expect(client) .to respond_to(:get_entity) @@ -6,67 +8,25 @@ end describe '#get_entity' do - let(:entity_id) { 'ent_12345' } - let(:entity_response) do - { - object: 'entity', - mode: 'test', - id: entity_id, - holder_name: 'Test Company LLC', - holder_id: '12345678-9', - is_root: true - } - end - - before do - allow(client).to receive(:_get_entity).with(entity_id).and_return(entity_response) - end - - it 'returns an Entity instance' do + it 'returns an Entity instance', :vcr do entity = client.get_entity(entity_id) expect(entity) .to be_an_instance_of(Fintoc::Transfers::Entity) .and have_attributes( id: entity_id, - holder_name: 'Test Company LLC' + holder_name: 'Fintoc' ) end end describe '#get_entities' do - let(:entities_response) do - [ - { - object: 'entity', - mode: 'test', - id: 'ent_12345', - holder_name: 'Test Company LLC', - holder_id: '12345678-9', - is_root: true - }, - { - object: 'entity', - mode: 'test', - id: 'ent_67890', - holder_name: 'Another Company Inc', - holder_id: '98765432-1', - is_root: false - } - ] - end - - before do - allow(client).to receive(:_get_entities).and_return(entities_response) - end - - it 'returns an array of Entity instances' do + it 'returns an array of Entity instances', :vcr do entities = client.get_entities expect(entities).to all(be_a(Fintoc::Transfers::Entity)) - expect(entities.size).to eq(2) - expect(entities.first.id).to eq('ent_12345') - expect(entities.last.id).to eq('ent_67890') + expect(entities.size).to eq(1) + expect(entities.first.id).to eq('ent_31t0VhhrAXASFQTVYfCfIBnljbT') end end end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml new file mode 100644 index 0000000..1c10930 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml @@ -0,0 +1,75 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/entities + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 29 Aug 2025 15:21:01 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '129' + Connection: + - close + Cf-Ray: + - 976d09250985a4ff-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"4e0fddc47966ed6497da73e42163ad1e" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 72b515f0-cf44-4ff5-b97c-8998ed4bdf6d + X-Runtime: + - '0.099485' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=tktryiSMTGe%2BnX0QteWiKwGyNjTIAgr7K7akMekvkj4%2F%2BYuO7g5Xc54CyOTI7x7UAapA2mVexTcYGY%2FMso36qPIpNNavkBb5eKurglaGkMY2Kp3e8qm5ozscyU97y8nZ"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=60112&min_rtt=59235&rtt_var=22840&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1791&delivery_rate=71916&cwnd=253&unsent_bytes=0&cid=b15990fef133e00b&ts=411&x=0" + body: + encoding: UTF-8 + string: '[{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","mode":"test","holder_name":"Fintoc","holder_id":"ND","is_root":true,"object":"entity"}]' + recorded_at: Fri, 29 Aug 2025 15:21:01 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml new file mode 100644 index 0000000..3c38327 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/entities/ent_31t0VhhrAXASFQTVYfCfIBnljbT + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 29 Aug 2025 15:21:40 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '127' + Connection: + - close + Cf-Ray: + - 976d0a19fe74d94e-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"f896e8d1f096825161c240052d9acb4a" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - e74046b0-bcae-4651-a845-9705b2375a43 + X-Runtime: + - '0.242517' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=mfXjDHsKGkEx3BFFZkGTiu9SFzAphuic90v7abBlNrgOnkfgHoMyJKqhxMUMqeVCjTjhpRNn7TwFk7nr1f%2FUvxz1No26uGqS1uyfIimV4ockxJRU%2Bu0xDuLHBKd40ctF"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=58995&min_rtt=58862&rtt_var=22169&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1823&delivery_rate=72372&cwnd=107&unsent_bytes=0&cid=2cf15dc447adbd92&ts=558&x=0" + body: + encoding: UTF-8 + string: '{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","mode":"test","holder_name":"Fintoc","holder_id":"ND","is_root":true,"object":"entity"}' + recorded_at: Fri, 29 Aug 2025 15:21:40 GMT +recorded_with: VCR 6.3.1 From d7181f6d1d4d8b0d8eca78e579abbaca7e3cb0a1 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 12:08:08 -0400 Subject: [PATCH 17/72] fix(movements-http-mocks): Move mocks to correct folder (and delete deprecated ones) --- .../_get_account/get_a_linked_account.yml | 0 .../prints_accounts_to_console.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 .../_account/get_a_link_account.yml | 151 ------------------ .../_account/get_a_linked_account.yml | 151 ------------------ .../get_the_link_from_a_given_link_token.yml | 77 --------- ..._all_the_links_from_a_given_link_token.yml | 80 ---------- 8 files changed, 459 deletions(-) rename spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/{links_functionality => }/_get_account/get_a_linked_account.yml (100%) rename spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/{links_functionality => }/_get_accounts/prints_accounts_to_console.yml (100%) rename spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/{links_functionality => }/_get_link/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/{links_functionality => }/_get_links/get_all_the_links_from_a_given_link_token.yml (100%) delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_account/get_a_linked_account.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_accounts/prints_accounts_to_console.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_accounts/prints_accounts_to_console.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_link/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_link/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_links/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_get_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_links/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml deleted file mode 100644 index c64ccff..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_link_account.yml +++ /dev/null @@ -1,151 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 11 Sep 2020 18:21:20 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=d2558e1ce5725a4144ffa971d816a8f7a1599848479; expires=Sun, 11-Oct-20 - 18:21:19 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"94045ec584e62887fbeed1515a5e8029" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - d24b3efd-7ac9-4152-9d3c-a96148e4fdf7 - X-Runtime: - - '0.418800' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 051fff93580000f7c2ef9a5200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d1368655d26f7c2-EZE - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":62255521,"current":62255521,"limit":62255521},"holder_id":"416148503","holder_name":"MarΓ­a - del Carmen GirΓ³n Raya","currency":"CLP"}]}' - recorded_at: Fri, 11 Sep 2020 18:21:20 GMT -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W?link_token=6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 11 Sep 2020 18:21:21 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=d3f0cd4044924512fd5bddb926ab1fc651599848480; expires=Sun, 11-Oct-20 - 18:21:20 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"94045ec584e62887fbeed1515a5e8029" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - c1a35086-bef1-491b-abda-285d2caa1821 - X-Runtime: - - '0.397008' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 051fff986a0000e522273b9200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d13686d7c4be522-ARI - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente", "refreshed_at": null, "official_name":"Cuenta Corriente","balance":{"available":62255521,"current":62255521,"limit":62255521},"holder_id":"416148503","holder_name":"MarΓ­a - del Carmen GirΓ³n Raya","currency":"CLP"}]}' - recorded_at: Fri, 11 Sep 2020 18:21:22 GMT -recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml deleted file mode 100644 index b8658b5..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_account/get_a_linked_account.yml +++ /dev/null @@ -1,151 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 18 Sep 2020 19:30:22 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=d422e7ea5f64ed2f17d7849b72bc08f861600457421; expires=Sun, 18-Oct-20 - 19:30:21 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"897ae694e5399ed740681eeb2602478d" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - bbfc257f-e666-4de8-8943-13b5311319e2 - X-Runtime: - - '0.382821' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 05444b49530000e52eca0a5200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d4d7b221a91e52e-ARI - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. - Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}]}' - recorded_at: Fri, 18 Sep 2020 19:30:22 GMT -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W?link_token=6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 18 Sep 2020 19:30:23 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=dcbe25b41d5cf53120aeb80d08e32f03e1600457422; expires=Sun, 18-Oct-20 - 19:30:22 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"897ae694e5399ed740681eeb2602478d" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - 72ddb4c5-36b3-4dfd-b85d-33fd23e8661c - X-Runtime: - - '0.330236' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 05444b4ee00000d7d13612e200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d4d7b2b0a7cd7d1-EZE - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","refreshed_at":null,"official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. - Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}]}' - recorded_at: Fri, 18 Sep 2020 19:30:23 GMT -recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml deleted file mode 100644 index 9f1af0d..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_link/get_the_link_from_a_given_link_token.yml +++ /dev/null @@ -1,77 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 11 Sep 2020 18:21:18 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=deae8e9e8f76a047dcd4868adfe72bf081599848477; expires=Sun, 11-Oct-20 - 18:21:17 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"94045ec584e62887fbeed1515a5e8029" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - 26836f44-6c7c-4d02-b795-83f1a4bee81a - X-Runtime: - - '0.535821' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 051fff89490000e52e201a9200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d1368554ccde52e-ARI - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":62255521,"current":62255521,"limit":62255521},"holder_id":"416148503","holder_name":"MarΓ­a - del Carmen GirΓ³n Raya","currency":"CLP"}]}' - recorded_at: Fri, 11 Sep 2020 18:21:18 GMT -recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml deleted file mode 100644 index 3ff8947..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/links_functionality/_links/get_all_the_links_from_a_given_link_token.yml +++ /dev/null @@ -1,80 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 11 Sep 2020 18:21:19 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=d067424ee2e91e31de078cbd3f1cc5c011599848478; expires=Sun, 11-Oct-20 - 18:21:18 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Link: - - ; rel="first", ; - rel="last" - X-Total-Count: - - '1' - Etag: - - W/"80e799153079ddb19ef402cbfea3c804" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - 1a1a5c10-d715-4ad8-bb64-76496333feb6 - X-Runtime: - - '0.058623' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 051fff8f49000074dfb32ae200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d13685eda8c74df-EZE - body: - encoding: UTF-8 - string: '[{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":null}]' - recorded_at: Fri, 11 Sep 2020 18:21:19 GMT -recorded_with: VCR 6.0.0 From 9ce475107d20b79a26b1e58745216a71ba02bea2 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 12:11:43 -0400 Subject: [PATCH 18/72] fix(movements): Fix movements comparison --- lib/fintoc/movements/resources/movement.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fintoc/movements/resources/movement.rb b/lib/fintoc/movements/resources/movement.rb index fa444ca..45bd93f 100644 --- a/lib/fintoc/movements/resources/movement.rb +++ b/lib/fintoc/movements/resources/movement.rb @@ -39,7 +39,7 @@ def initialize( end def ==(other) - @id = other.id + @id == other.id end alias eql? == From fed7f3aa14c778313af1cc273885bbd59ede163e Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 13:05:38 -0400 Subject: [PATCH 19/72] feature(base-client): Add patch request method --- lib/fintoc/base_client.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index c9773d6..352fd82 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -30,6 +30,10 @@ def post(version: :v1) request('post', version: version) end + def patch(version: :v1) + request('patch', version: version) + end + def request(method, version: :v1) proc do |resource, **kwargs| parameters = params(method, **kwargs) From 8572aaf79c739414560bda077d6dac3d4bbf3368 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 15:00:52 -0400 Subject: [PATCH 20/72] feature(accounts): Create transfers account resource --- lib/fintoc/transfers/resources/account.rb | 70 +++++++++++++ spec/lib/fintoc/transfers/account_spec.rb | 114 ++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 lib/fintoc/transfers/resources/account.rb create mode 100644 spec/lib/fintoc/transfers/account_spec.rb diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb new file mode 100644 index 0000000..4c6dc8d --- /dev/null +++ b/lib/fintoc/transfers/resources/account.rb @@ -0,0 +1,70 @@ +require 'fintoc/utils' + +module Fintoc + module Transfers + class Account + include Utils + + attr_reader :id, :object, :mode, :description, :available_balance, :currency, + :is_root, :root_account_number_id, :root_account_number, :status, :entity + + def initialize( + id:, + object:, + mode:, + description:, + available_balance:, + currency:, + is_root:, + root_account_number_id:, + root_account_number:, + status:, + entity:, + client: nil, + ** + ) + @id = id + @object = object + @mode = mode + @description = description + @available_balance = available_balance + @currency = currency + @is_root = is_root + @root_account_number_id = root_account_number_id + @root_account_number = root_account_number + @status = status + @entity = entity + @client = client + end + + def to_s + "πŸ’° #{@description} (#{@id}) - #{format_currency(@available_balance, @currency)}" + end + + def active? + @status == 'active' + end + + def blocked? + @status == 'blocked' + end + + def closed? + @status == 'closed' + end + + private + + def format_currency(amount, currency) + case currency + when 'MXN' + "MXN $#{amount / 100.0}" + when 'CLP' + "CLP $#{amount}" + else + "#{currency} #{amount}" + end + end + end + end +end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb new file mode 100644 index 0000000..a537525 --- /dev/null +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -0,0 +1,114 @@ +require 'fintoc/transfers/resources/account' + +RSpec.describe Fintoc::Transfers::Account do + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } + let(:client) { Fintoc::Transfers::Client.new(api_key) } + + let(:entity_data) do + { + id: 'ent_12345', + holder_name: 'ACME Inc.', + holder_id: 'ND' + } + end + + let(:data) do + { + object: 'account', + mode: 'test', + id: 'acc_123', + description: 'My root account', + available_balance: 23459183, + currency: 'MXN', + is_root: true, + root_account_number_id: 'acno_Kasf91034gj1AD', + root_account_number: '738969123456789120', + status: 'active', + entity: entity_data, + client: client + } + end + + let(:account) { described_class.new(**data) } + + describe '#new' do + it 'creates an instance of Account' do + expect(account).to be_an_instance_of(described_class) + end + + it 'sets all attributes correctly' do # rubocop:disable RSpec/ExampleLength + expect(account).to have_attributes( + object: 'account', + mode: 'test', + id: 'acc_123', + description: 'My root account', + available_balance: 23459183, + currency: 'MXN', + is_root: true, + root_account_number_id: 'acno_Kasf91034gj1AD', + root_account_number: '738969123456789120', + status: 'active', + entity: entity_data + ) + end + end + + describe '#to_s' do + it 'returns a formatted string representation with MXN currency' do + expect(account.to_s).to eq('πŸ’° My root account (acc_123) - MXN $234591.83') + end + + context 'with CLP currency' do + let(:clp_data) { data.merge(currency: 'CLP', available_balance: 1000000) } + let(:clp_account) { described_class.new(**clp_data) } + + it 'returns a formatted string representation with CLP currency' do + expect(clp_account.to_s).to eq('πŸ’° My root account (acc_123) - CLP $1000000') + end + end + + context 'with other currency' do + let(:other_data) { data.merge(currency: 'USD', available_balance: 50000) } + let(:other_account) { described_class.new(**other_data) } + + it 'returns a formatted string representation with generic currency format' do + expect(other_account.to_s).to eq('πŸ’° My root account (acc_123) - USD 50000') + end + end + end + + describe 'status methods' do + describe '#active?' do + it 'returns true when status is active' do + expect(account.active?).to be true + end + + it 'returns false when status is not active' do + blocked_account = described_class.new(**data, status: 'blocked') + expect(blocked_account.active?).to be false + end + end + + describe '#blocked?' do + it 'returns false when status is active' do + expect(account.blocked?).to be false + end + + it 'returns true when status is blocked' do + blocked_account = described_class.new(**data, status: 'blocked') + expect(blocked_account.blocked?).to be true + end + end + + describe '#closed?' do + it 'returns false when status is active' do + expect(account.closed?).to be false + end + + it 'returns true when status is closed' do + closed_account = described_class.new(**data, status: 'closed') + expect(closed_account.closed?).to be true + end + end + end +end From 8d4ad50ac68010f2bac4a5c12eab9820675313da Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 16:06:46 -0400 Subject: [PATCH 21/72] feature(accounts): Add accounts methods to Transfers client --- .../transfers/client/accounts_methods.rb | 48 ++++++++++++ lib/fintoc/transfers/client/client.rb | 2 + .../fintoc/transfers/accounts_methods_spec.rb | 49 ++++++++++++ spec/lib/fintoc/transfers/client_spec.rb | 8 +- .../clients/accounts_client_examples.rb | 63 +++++++++++++++ .../returns_an_Account_instance.yml | 77 +++++++++++++++++++ .../returns_an_Account_instance.yml | 74 ++++++++++++++++++ .../returns_an_array_of_Account_instances.yml | 76 ++++++++++++++++++ .../returns_an_updated_Account_instance.yml | 76 ++++++++++++++++++ 9 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 lib/fintoc/transfers/client/accounts_methods.rb create mode 100644 spec/lib/fintoc/transfers/accounts_methods_spec.rb create mode 100644 spec/support/shared_examples/clients/accounts_client_examples.rb create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml diff --git a/lib/fintoc/transfers/client/accounts_methods.rb b/lib/fintoc/transfers/client/accounts_methods.rb new file mode 100644 index 0000000..ee9e683 --- /dev/null +++ b/lib/fintoc/transfers/client/accounts_methods.rb @@ -0,0 +1,48 @@ +require 'fintoc/transfers/resources/account' + +module Fintoc + module Transfers + module AccountsMethods + def create_account(entity_id:, description:, **params) + data = _create_account(entity_id:, description:, **params) + build_account(data) + end + + def get_account(account_id) + data = _get_account(account_id) + build_account(data) + end + + def list_accounts(**params) + _list_accounts(**params).map { |data| build_account(data) } + end + + def update_account(account_id, **params) + data = _update_account(account_id, **params) + build_account(data) + end + + private + + def _create_account(entity_id:, description:, **params) + post(version: :v2).call('accounts', entity_id:, description:, **params) + end + + def _get_account(account_id) + get(version: :v2).call("accounts/#{account_id}") + end + + def _list_accounts(**params) + get(version: :v2).call('accounts', **params) + end + + def _update_account(account_id, **params) + patch(version: :v2).call("accounts/#{account_id}", **params) + end + + def build_account(data) + Fintoc::Transfers::Account.new(**data, client: self) + end + end + end +end diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index c08f2a0..f52063b 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -1,10 +1,12 @@ require 'fintoc/base_client' require 'fintoc/transfers/client/entities_methods' +require 'fintoc/transfers/client/accounts_methods' module Fintoc module Transfers class Client < BaseClient include EntitiesMethods + include AccountsMethods end end end diff --git a/spec/lib/fintoc/transfers/accounts_methods_spec.rb b/spec/lib/fintoc/transfers/accounts_methods_spec.rb new file mode 100644 index 0000000..b244b66 --- /dev/null +++ b/spec/lib/fintoc/transfers/accounts_methods_spec.rb @@ -0,0 +1,49 @@ +require 'fintoc/transfers/client/accounts_methods' + +RSpec.describe Fintoc::Transfers::AccountsMethods do + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Transfers::AccountsMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def get(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def post(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def patch(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + end + end + + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + + describe 'module inclusion' do + it 'provides account-related methods' do + expect(client) + .to respond_to(:create_account) + .and respond_to(:get_account) + .and respond_to(:list_accounts) + .and respond_to(:update_account) + end + end + + describe 'private methods' do + it 'provides private helper methods' do + expect(client.private_methods) + .to include( + :_create_account, :_get_account, :_list_accounts, :_update_account, :build_account + ) + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index f6d3049..f913be4 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -18,11 +18,7 @@ end end - it 'responds to transfers-specific methods' do - expect(client) - .to respond_to(:get_entity) - .and respond_to(:get_entities) - end - it_behaves_like 'a client with entities methods' + + it_behaves_like 'a client with accounts methods' end diff --git a/spec/support/shared_examples/clients/accounts_client_examples.rb b/spec/support/shared_examples/clients/accounts_client_examples.rb new file mode 100644 index 0000000..9a5cfe9 --- /dev/null +++ b/spec/support/shared_examples/clients/accounts_client_examples.rb @@ -0,0 +1,63 @@ +RSpec.shared_examples 'a client with accounts methods' do + let(:account_id) { 'acc_31yYL7h9LVPg121AgFtCyJPDsgM' } + let(:entity_id) { 'ent_31t0VhhrAXASFQTVYfCfIBnljbT' } + + it 'responds to account-specific methods' do + expect(client) + .to respond_to(:create_account) + .and respond_to(:get_account) + .and respond_to(:list_accounts) + .and respond_to(:update_account) + end + + describe '#create_account' do + it 'returns an Account instance', :vcr do + account = client.create_account(entity_id:, description: 'Test account') + + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + description: 'Test account', + currency: 'MXN', + status: 'active' + ) + end + end + + describe '#get_account' do + it 'returns an Account instance', :vcr do + account = client.get_account(account_id) + + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + id: account_id, + description: 'Test account' + ) + end + end + + describe '#list_accounts' do + it 'returns an array of Account instances', :vcr do + accounts = client.list_accounts + + expect(accounts).to all(be_a(Fintoc::Transfers::Account)) + expect(accounts.size).to be >= 1 + expect(accounts.first.id).to eq(account_id) + end + end + + describe '#update_account' do + it 'returns an updated Account instance', :vcr do + updated_description = 'Updated account description' + account = client.update_account(account_id, description: updated_description) + + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + id: account_id, + description: updated_description + ) + end + end +end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml new file mode 100644 index 0000000..534fec1 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/accounts + body: + encoding: UTF-8 + string: '{"entity_id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","description":"Test + account"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 29 Aug 2025 20:02:00 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '383' + Connection: + - close + Cf-Ray: + - 976ea4bf29a642f0-MIA + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"dfa167aa8ce1e0f1d5764f021583ccfb" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 1fed91d6-8877-4d68-884c-54de16443d43 + X-Runtime: + - '0.282044' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=m1B2GZTfOo6Y24n8mMkDyjW%2Fpf5dVb1QH8vBVV0%2BIS9i7qAS%2FHrpWNcjpWKMX91r%2FsV67xtSI1gc0UWqgoawtWxhHZ%2FVgSP8V6331v6OATD3KkLJIA3gW0WKoxl%2B5B%2Fb"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=132154&min_rtt=131328&rtt_var=49838&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1935&delivery_rate=32437&cwnd=38&unsent_bytes=0&cid=b5f864790f975efa&ts=696&x=0" + body: + encoding: UTF-8 + string: '{"id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","object":"account","mode":"test","root_account_number":"735969000000203297","is_root":false,"root_account_number_id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","available_balance":0,"currency":"MXN","entity":{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","holder_name":"Fintoc","holder_id":"ND","is_root":true},"description":"Test + account","status":"active"}' + recorded_at: Fri, 29 Aug 2025 20:02:00 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml new file mode 100644 index 0000000..1b8d889 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/accounts/acc_31yYL7h9LVPg121AgFtCyJPDsgM + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 29 Aug 2025 20:02:42 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '383' + Connection: + - close + Cf-Ray: + - 976ea5c5ee95f1f9-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"dfa167aa8ce1e0f1d5764f021583ccfb" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - f5c4a054-f4ce-4a20-bd01-897f574b75d5 + X-Runtime: + - '0.167283' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=vMY0I4XXrgNfMRSDKhJGiurTSJqsamrvy7mb%2BX25LYFjI%2BbmHc9IzI2BWc%2F5C97bQlCgfeoVaCWac75V6Dcmjf94Uc%2BZPwaLhsgwiVFH7XA8VGWcH4i7Fe%2BLu39x3JWb"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=70398&min_rtt=59455&rtt_var=44182&sent=7&recv=8&lost=0&retrans=1&sent_bytes=4920&recv_bytes=1823&delivery_rate=29058&cwnd=163&unsent_bytes=0&cid=a1434ae19ea63fff&ts=512&x=0" + body: + encoding: UTF-8 + string: '{"id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","object":"account","mode":"test","root_account_number":"735969000000203297","is_root":false,"root_account_number_id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","available_balance":0,"currency":"MXN","entity":{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","holder_name":"Fintoc","holder_id":"ND","is_root":true},"description":"Test + account","status":"active"}' + recorded_at: Fri, 29 Aug 2025 20:02:42 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml new file mode 100644 index 0000000..ba1f10e --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/accounts + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 29 Aug 2025 20:02:43 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '758' + Connection: + - close + Cf-Ray: + - 976ea5c9caab8bc1-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"db90e857bbfff3b588f63b53be770a09" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - f2247a46-e47d-4bd6-9839-73ae79c603a4 + X-Runtime: + - '0.122842' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=SAJpKW0SU6ZiCsnlxevZ64wjOntOithUhDYqR7JYNjIh4SXSRYAJvrno1yfY%2BId%2FpCsXD%2BTevxrf%2BmcnvuThHBw9YraScB0Nps0l29xG7E4R1tOtgR8%2BL01iCH%2F8D3jM"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=31428&min_rtt=30960&rtt_var=11944&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3880&recv_bytes=1791&delivery_rate=137596&cwnd=236&unsent_bytes=0&cid=5023caf54b582d86&ts=379&x=0" + body: + encoding: UTF-8 + string: '[{"id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","object":"account","mode":"test","root_account_number":"735969000000203297","is_root":false,"root_account_number_id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","available_balance":0,"currency":"MXN","entity":{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","holder_name":"Fintoc","holder_id":"ND","is_root":true},"description":"Test + account","status":"active"},{"id":"acc_31t0VhaF6uFoj0O9oreXw3jDtzL","object":"account","mode":"test","root_account_number":"735969000000203226","is_root":true,"root_account_number_id":"acno_31t0VhIYoVNy4mYjyo8cWZTYcK9","available_balance":0,"currency":"MXN","entity":{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","holder_name":"Fintoc","holder_id":"ND","is_root":true},"description":null,"status":"active"}]' + recorded_at: Fri, 29 Aug 2025 20:02:43 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml new file mode 100644 index 0000000..4286b05 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: patch + uri: https://api.fintoc.com/v2/accounts/acc_31yYL7h9LVPg121AgFtCyJPDsgM + body: + encoding: UTF-8 + string: '{"description":"Updated account description"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 29 Aug 2025 20:02:43 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '398' + Connection: + - close + Cf-Ray: + - 976ea5cd28dda58f-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"2021b3a9eceabbed2b5e2539dae3ca57" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 12f3b99f-2368-46fb-a72f-67e20705813c + X-Runtime: + - '0.150449' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=ZIwfCgCpGUQdsU6gArPxnP5wee6vB8eJiHk61oVLdCzUTbbZkF59fnPvcuQ4U%2FM1SvgZyVkDGxGviYwCERBP8Fr2h%2BDm%2BJMQIbsogweD%2FYBDrteSOe1etqMFiblLGAOv"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=32296&min_rtt=31883&rtt_var=12251&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1937&delivery_rate=133613&cwnd=253&unsent_bytes=0&cid=ba5dd9e9de4421c8&ts=417&x=0" + body: + encoding: UTF-8 + string: '{"id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","object":"account","mode":"test","root_account_number":"735969000000203297","is_root":false,"root_account_number_id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","available_balance":0,"currency":"MXN","entity":{"id":"ent_31t0VhhrAXASFQTVYfCfIBnljbT","holder_name":"Fintoc","holder_id":"ND","is_root":true},"description":"Updated + account description","status":"active"}' + recorded_at: Fri, 29 Aug 2025 20:02:43 GMT +recorded_with: VCR 6.3.1 From 05ea1b92b6e0441f18cef9b2a7ee9cf9a0ab5583 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 16:07:51 -0400 Subject: [PATCH 22/72] refactor(client): Add comment to Fintoc::Client to make sure that the AI doesn't use it since it is expected to be deprecated --- lib/fintoc/client.rb | 3 +++ spec/lib/fintoc/transfers/entity_spec.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 7e2a302..b37987f 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -3,6 +3,9 @@ module Fintoc class Client + # Deprecated in favor of Fintoc::Movements::Client and Fintoc::Transfers::Client + # It should not be used anymore + attr_reader :movements, :transfers def initialize(api_key) diff --git a/spec/lib/fintoc/transfers/entity_spec.rb b/spec/lib/fintoc/transfers/entity_spec.rb index 917ba32..321459b 100644 --- a/spec/lib/fintoc/transfers/entity_spec.rb +++ b/spec/lib/fintoc/transfers/entity_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Fintoc::Transfers::Entity do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { Fintoc::Client.new(api_key) } + let(:client) { Fintoc::Transfers::Client.new(api_key) } let(:data) do { From 4a48e0743f551c14f2390745c647e9c32f0d2f32 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 16:08:07 -0400 Subject: [PATCH 23/72] refactor(entitities): Remove unnecessary entities array conversion --- lib/fintoc/transfers/client/entities_methods.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/fintoc/transfers/client/entities_methods.rb b/lib/fintoc/transfers/client/entities_methods.rb index 605de9a..5821508 100644 --- a/lib/fintoc/transfers/client/entities_methods.rb +++ b/lib/fintoc/transfers/client/entities_methods.rb @@ -19,9 +19,7 @@ def _get_entity(entity_id) end def _get_entities(**params) - response = get(version: :v2).call('entities', **params) - # Handle both single entity and list responses - response.is_a?(Array) ? response : [response] + get(version: :v2).call('entities', **params) end def build_entity(data) From 9ece3dd7f2ed3bccb517b561dc6111d847c01459 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 16:28:04 -0400 Subject: [PATCH 24/72] feature(accounts): Add refresh method to accounts --- lib/fintoc/transfers/resources/account.rb | 7 +++++++ spec/lib/fintoc/transfers/account_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 4c6dc8d..2ba04a2 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -41,6 +41,13 @@ def to_s "πŸ’° #{@description} (#{@id}) - #{format_currency(@available_balance, @currency)}" end + def refresh + account_data = @client.get_account(@id) + initialize(**account_data.instance_variables.each_with_object({}) do |var, hash| + hash[var.to_s.delete('@').to_sym] = account_data.instance_variable_get(var) + end) + end + def active? @status == 'active' end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index a537525..ea89452 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -111,4 +111,24 @@ end end end + + describe '#refresh' do + let(:updated_data) { data.merge(description: 'Updated account description') } + + let(:updated_account) { described_class.new(**updated_data, client: client) } + + before do + allow(client).to receive(:get_account).with('acc_123').and_return(updated_account) + end + + it 'refreshes the account with updated data from the API' do + expect(account.description).to eq('My root account') + + account.refresh + + expect(client).to have_received(:get_account).with('acc_123') + + expect(account.description).to eq('Updated account description') + end + end end From 4d3ab3e35efc8c38732d5d588fb9d783ee24e3a4 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 16:28:52 -0400 Subject: [PATCH 25/72] feature(accounts): Add update method for accounts --- lib/fintoc/transfers/resources/account.rb | 10 +++++++ spec/lib/fintoc/transfers/account_spec.rb | 32 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 2ba04a2..fca62e8 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -48,6 +48,16 @@ def refresh end) end + def update(description: nil) + params = {} + params[:description] = description if description + + updated_account = @client.update_account(@id, **params) + initialize(**updated_account.instance_variables.each_with_object({}) do |var, hash| + hash[var.to_s.delete('@').to_sym] = updated_account.instance_variable_get(var) + end) + end + def active? @status == 'active' end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index ea89452..20d2790 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -131,4 +131,36 @@ expect(account.description).to eq('Updated account description') end end + + describe '#update' do + let(:updated_data) { data.merge(description: 'New account description') } + + let(:updated_account) { described_class.new(**updated_data, client: client) } + + before do + allow(client).to receive(:update_account) do |_id, params| + updated_data_for_call = { **data, **params } + described_class.new(**updated_data_for_call, client: client) + end + end + + it 'updates the account description and refreshes the instance' do + expect(account.description).to eq('My root account') + + account.update(description: 'New account description') + + expect(client) + .to have_received(:update_account) + .with('acc_123', description: 'New account description') + + expect(account.description).to eq('New account description') + end + + it 'only sends provided parameters' do + account.update(description: 'Test description') + expect(client) + .to have_received(:update_account) + .with('acc_123', description: 'Test description') + end + end end From 0a2446b9425a2e48880704fa7f52c200e9fe29a5 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 18:56:47 -0400 Subject: [PATCH 26/72] refactor(client): Add comment indicating that Client is only kept for backward compatibility --- lib/fintoc/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index b37987f..0e35f6d 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -4,7 +4,7 @@ module Fintoc class Client # Deprecated in favor of Fintoc::Movements::Client and Fintoc::Transfers::Client - # It should not be used anymore + # It should not be used anymore, but it will be kept for now for backward compatibility attr_reader :movements, :transfers From d56fcf2fe5df70d57943d49258f7b279e8713112 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 19:25:53 -0400 Subject: [PATCH 27/72] refactor(accounts): Configure money-rails gem instead of custom formatter --- Gemfile.lock | 97 +++++++++++++++++++++-- fintoc.gemspec | 1 + lib/config/initializers/money.rb | 5 ++ lib/fintoc.rb | 2 + lib/fintoc/transfers/resources/account.rb | 16 +--- spec/lib/fintoc/transfers/account_spec.rb | 18 ++--- 6 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 lib/config/initializers/money.rb diff --git a/Gemfile.lock b/Gemfile.lock index ee60bbe..a946bcc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,11 +3,28 @@ PATH specs: fintoc (0.2.0) http + money-rails tabulate GEM remote: https://rubygems.org/ specs: + actionpack (8.0.2.1) + actionview (= 8.0.2.1) + activesupport (= 8.0.2.1) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actionview (8.0.2.1) + activesupport (= 8.0.2.1) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) activesupport (8.0.2.1) base64 benchmark (>= 0.3) @@ -27,22 +44,25 @@ GEM base64 (0.3.0) benchmark (0.4.1) bigdecimal (3.2.2) + builder (3.3.0) concurrent-ruby (1.3.5) connection_pool (2.5.3) crack (1.0.0) bigdecimal rexml + crass (1.0.6) + date (3.4.1) diff-lcs (1.6.2) domain_name (0.6.20240107) drb (2.2.3) + erb (5.0.2) + erubi (1.13.1) ffi (1.17.2) ffi (1.17.2-aarch64-linux-gnu) ffi (1.17.2-aarch64-linux-musl) ffi (1.17.2-arm-linux-gnu) ffi (1.17.2-arm-linux-musl) ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86-linux-gnu) - ffi (1.17.2-x86-linux-musl) ffi (1.17.2-x86_64-darwin) ffi (1.17.2-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-musl) @@ -60,6 +80,11 @@ GEM http-form_data (2.3.0) i18n (1.14.7) concurrent-ruby (~> 1.0) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.13.2) language_server-protocol (3.17.0.5) lint_roller (1.1.0) @@ -67,18 +92,79 @@ GEM ffi-compiler (~> 1.0) rake (~> 13.0) logger (1.7.0) + loofah (2.24.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) minitest (5.25.5) + monetize (1.13.0) + money (~> 6.12) + money (6.19.0) + i18n (>= 0.6.4, <= 2) + money-rails (1.15.0) + activesupport (>= 3.0) + monetize (~> 1.9) + money (~> 6.13) + railties (>= 3.0) + nokogiri (1.18.9-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.9-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.9-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-linux-musl) + racc (~> 1.4) parallel (1.27.0) parser (3.3.9.0) ast (~> 2.4.1) racc + pp (0.6.2) + prettyprint + prettyprint (0.2.0) prism (1.4.0) + psych (5.2.6) + date + stringio public_suffix (6.0.2) racc (1.8.1) rack (3.2.0) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.2.1) + actionpack (= 8.0.2.1) + activesupport (= 8.0.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.3.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) regexp_parser (2.11.2) + reline (0.6.2) + io-console (~> 0.5) rexml (3.4.2) rspec (3.13.1) rspec-core (~> 3.13.0) @@ -125,19 +211,23 @@ GEM rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) securerandom (0.4.1) + stringio (3.1.7) tabulate (0.1.2) + thor (1.4.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.1.5) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) uri (1.0.3) + useragent (0.16.11) vcr (6.3.1) base64 webmock (3.25.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + zeitwerk (2.7.3) PLATFORMS aarch64-linux-gnu @@ -145,9 +235,6 @@ PLATFORMS arm-linux-gnu arm-linux-musl arm64-darwin - ruby - x86-linux-gnu - x86-linux-musl x86_64-darwin x86_64-linux x86_64-linux-gnu diff --git a/fintoc.gemspec b/fintoc.gemspec index 28396b0..a0c6afc 100644 --- a/fintoc.gemspec +++ b/fintoc.gemspec @@ -27,5 +27,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] spec.add_dependency 'http' + spec.add_dependency 'money-rails' spec.add_dependency 'tabulate' end diff --git a/lib/config/initializers/money.rb b/lib/config/initializers/money.rb new file mode 100644 index 0000000..f541c58 --- /dev/null +++ b/lib/config/initializers/money.rb @@ -0,0 +1,5 @@ +require 'money-rails' + +MoneyRails.configure do |config| + config.locale_backend = :currency +end diff --git a/lib/fintoc.rb b/lib/fintoc.rb index 022b5c8..1224934 100644 --- a/lib/fintoc.rb +++ b/lib/fintoc.rb @@ -2,5 +2,7 @@ require 'fintoc/errors' require 'fintoc/client' +require 'config/initializers/money' + module Fintoc end diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index fca62e8..4b04bec 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -1,3 +1,4 @@ +require 'money' require 'fintoc/utils' module Fintoc @@ -38,7 +39,7 @@ def initialize( end def to_s - "πŸ’° #{@description} (#{@id}) - #{format_currency(@available_balance, @currency)}" + "πŸ’° #{@description} (#{@id}) - #{Money.from_cents(@available_balance, @currency).format}" end def refresh @@ -69,19 +70,6 @@ def blocked? def closed? @status == 'closed' end - - private - - def format_currency(amount, currency) - case currency - when 'MXN' - "MXN $#{amount / 100.0}" - when 'CLP' - "CLP $#{amount}" - else - "#{currency} #{amount}" - end - end end end end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index 20d2790..9e134cc 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -54,8 +54,10 @@ end describe '#to_s' do - it 'returns a formatted string representation with MXN currency' do - expect(account.to_s).to eq('πŸ’° My root account (acc_123) - MXN $234591.83') + context 'with MXN currency' do + it 'returns a formatted string representation with MXN currency' do + expect(account.to_s).to eq('πŸ’° My root account (acc_123) - $234,591.83') + end end context 'with CLP currency' do @@ -63,16 +65,8 @@ let(:clp_account) { described_class.new(**clp_data) } it 'returns a formatted string representation with CLP currency' do - expect(clp_account.to_s).to eq('πŸ’° My root account (acc_123) - CLP $1000000') - end - end - - context 'with other currency' do - let(:other_data) { data.merge(currency: 'USD', available_balance: 50000) } - let(:other_account) { described_class.new(**other_data) } - - it 'returns a formatted string representation with generic currency format' do - expect(other_account.to_s).to eq('πŸ’° My root account (acc_123) - USD 50000') + expect(Money.from_cents(1000000, 'CLP').currency.thousands_separator).to eq('.') + expect(clp_account.to_s).to eq('πŸ’° My root account (acc_123) - $1.000.000') end end end From ebb922b687c4d59fb5a8e3cfee1945dcaa366cf2 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 29 Aug 2025 19:33:38 -0400 Subject: [PATCH 28/72] refactor(resources): Prefer explicit refreshing of resource values --- lib/fintoc/transfers/resources/account.rb | 31 ++++++++++++++++++----- lib/fintoc/transfers/resources/entity.rb | 22 +++++++++++++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 4b04bec..e145f35 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -43,10 +43,8 @@ def to_s end def refresh - account_data = @client.get_account(@id) - initialize(**account_data.instance_variables.each_with_object({}) do |var, hash| - hash[var.to_s.delete('@').to_sym] = account_data.instance_variable_get(var) - end) + fresh_account = @client.get_account(@id) + refresh_from_account(fresh_account) end def update(description: nil) @@ -54,9 +52,7 @@ def update(description: nil) params[:description] = description if description updated_account = @client.update_account(@id, **params) - initialize(**updated_account.instance_variables.each_with_object({}) do |var, hash| - hash[var.to_s.delete('@').to_sym] = updated_account.instance_variable_get(var) - end) + refresh_from_account(updated_account) end def active? @@ -70,6 +66,27 @@ def blocked? def closed? @status == 'closed' end + + private + + def refresh_from_account(account) + raise 'Account must be the same instance' unless account.id == @id + + initialize( + id: account.id, + object: account.object, + mode: account.mode, + description: account.description, + available_balance: account.available_balance, + currency: account.currency, + is_root: account.is_root, + root_account_number_id: account.root_account_number_id, + root_account_number: account.root_account_number, + status: account.status, + entity: account.entity, + client: @client + ) + end end end end diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/transfers/resources/entity.rb index ebb5820..8dfbc05 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/transfers/resources/entity.rb @@ -31,10 +31,24 @@ def to_s end def refresh - entity_data = @client.get_entity(@id) - initialize(**entity_data.instance_variables.each_with_object({}) do |var, hash| - hash[var.to_s.delete('@').to_sym] = entity_data.instance_variable_get(var) - end) + fresh_entity = @client.get_entity(@id) + refresh_from_entity(fresh_entity) + end + + private + + def refresh_from_entity(entity) + raise 'Entity must be the same instance' unless entity.id == @id + + initialize( + id: entity.id, + object: entity.object, + mode: entity.mode, + holder_name: entity.holder_name, + holder_id: entity.holder_id, + is_root: entity.is_root, + client: @client + ) end end end From 0531ad14d047630b0d7a933d018813ad0436b33b Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 11:41:33 -0400 Subject: [PATCH 29/72] feature(account-numbers): Add account numbers transfer resource --- .../transfers/resources/account_number.rb | 78 +++++++++++++ .../fintoc/transfers/account_number_spec.rb | 106 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 lib/fintoc/transfers/resources/account_number.rb create mode 100644 spec/lib/fintoc/transfers/account_number_spec.rb diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb new file mode 100644 index 0000000..61ebae5 --- /dev/null +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -0,0 +1,78 @@ +require 'fintoc/utils' + +module Fintoc + module Transfers + class AccountNumber + include Utils + + attr_reader :id, :object, :description, :number, :created_at, :updated_at, + :mode, :status, :is_root, :account_id, :metadata + + def initialize( + id:, + object:, + description:, + number:, + created_at:, + updated_at:, + mode:, + status:, + is_root:, + account_id:, + metadata:, + client: nil, + ** + ) + @id = id + @object = object + @description = description + @number = number + @created_at = created_at + @updated_at = updated_at + @mode = mode + @status = status + @is_root = is_root + @account_id = account_id + @metadata = metadata + @client = client + end + + def to_s + "πŸ”’ #{@number} (#{@id}) - #{@description}" + end + + def enabled? + @status == 'enabled' + end + + def disabled? + @status == 'disabled' + end + + def root? + @is_root + end + + private + + def refresh_from_account_number(account_number) + raise 'AccountNumber must be the same instance' unless account_number.id == @id + + initialize( + id: account_number.id, + object: account_number.object, + description: account_number.description, + number: account_number.number, + created_at: account_number.created_at, + updated_at: account_number.updated_at, + mode: account_number.mode, + status: account_number.status, + is_root: account_number.is_root, + account_id: account_number.account_id, + metadata: account_number.metadata, + client: @client + ) + end + end + end +end diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb new file mode 100644 index 0000000..c11b5f7 --- /dev/null +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -0,0 +1,106 @@ +require 'fintoc/transfers/resources/account_number' + +RSpec.describe Fintoc::Transfers::AccountNumber do + subject(:account_number) { described_class.new(**data) } + + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } + let(:client) { Fintoc::Transfers::Client.new(api_key) } + + let(:data) do + { + id: 'acno_Kasf91034gj1AD', + object: 'account_number', + description: 'My payins', + number: '738969123456789120', + created_at: '2024-03-01T20:09:42.949787176Z', + updated_at: '2024-03-01T20:09:42.949787176Z', + mode: 'test', + status: 'enabled', + is_root: false, + account_id: 'acc_Lq7dP901xZgA2B', + metadata: { + order_id: '12343212' + }, + client: client + } + end + + describe '#initialize' do + it 'assigns all attributes correctly' do # rubocop:disable RSpec/ExampleLength + expect(account_number).to have_attributes( + id: 'acno_Kasf91034gj1AD', + object: 'account_number', + description: 'My payins', + number: '738969123456789120', + created_at: '2024-03-01T20:09:42.949787176Z', + updated_at: '2024-03-01T20:09:42.949787176Z', + mode: 'test', + status: 'enabled', + is_root: false, + account_id: 'acc_Lq7dP901xZgA2B', + metadata: { order_id: '12343212' } + ) + end + end + + describe '#to_s' do + it 'returns a string representation' do + expected = 'πŸ”’ 738969123456789120 (acno_Kasf91034gj1AD) - My payins' + expect(account_number.to_s).to eq(expected) + end + end + + describe '#enabled?' do + context 'when status is enabled' do + it 'returns true' do + expect(account_number.enabled?).to be true + end + end + + context 'when status is not enabled' do + let(:data) do + super().merge(status: 'disabled') + end + + it 'returns false' do + expect(account_number.enabled?).to be false + end + end + end + + describe '#disabled?' do + context 'when status is disabled' do + let(:data) do + super().merge(status: 'disabled') + end + + it 'returns true' do + expect(account_number.disabled?).to be true + end + end + + context 'when status is not disabled' do + it 'returns false' do + expect(account_number.disabled?).to be false + end + end + end + + describe '#root?' do + context 'when is_root is true' do + let(:data) do + super().merge(is_root: true) + end + + it 'returns true' do + expect(account_number.root?).to be true + end + end + + context 'when is_root is false' do + it 'returns false' do + expect(account_number.root?).to be false + end + end + end +end From 10a075a694c9f0a9d62e545c77996d06432ddb24 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 13:30:17 -0400 Subject: [PATCH 30/72] feature(account-numbers): Add account numbers methods --- .../client/account_numbers_methods.rb | 53 +++++++++++++ lib/fintoc/transfers/client/client.rb | 2 + .../transfers/account_numbers_methods_spec.rb | 78 +++++++++++++++++++ spec/lib/fintoc/transfers/client_spec.rb | 2 + .../account_numbers_client_examples.rb | 67 ++++++++++++++++ .../returns_an_AccountNumber_instance.yml | 77 ++++++++++++++++++ .../returns_an_AccountNumber_instance.yml | 74 ++++++++++++++++++ ...ns_an_array_of_AccountNumber_instances.yml | 76 ++++++++++++++++++ ...urns_an_updated_AccountNumber_instance.yml | 76 ++++++++++++++++++ 9 files changed, 505 insertions(+) create mode 100644 lib/fintoc/transfers/client/account_numbers_methods.rb create mode 100644 spec/lib/fintoc/transfers/account_numbers_methods_spec.rb create mode 100644 spec/support/shared_examples/clients/account_numbers_client_examples.rb create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml diff --git a/lib/fintoc/transfers/client/account_numbers_methods.rb b/lib/fintoc/transfers/client/account_numbers_methods.rb new file mode 100644 index 0000000..956b30b --- /dev/null +++ b/lib/fintoc/transfers/client/account_numbers_methods.rb @@ -0,0 +1,53 @@ +require 'fintoc/transfers/resources/account_number' + +module Fintoc + module Transfers + module AccountNumbersMethods + def create_account_number(account_id:, description: nil, metadata: nil, **params) + data = _create_account_number(account_id:, description:, metadata:, **params) + build_account_number(data) + end + + def get_account_number(account_number_id) + data = _get_account_number(account_number_id) + build_account_number(data) + end + + def list_account_numbers(**params) + _list_account_numbers(**params).map { |data| build_account_number(data) } + end + + def update_account_number(account_number_id, **params) + data = _update_account_number(account_number_id, **params) + build_account_number(data) + end + + private + + def _create_account_number(account_id:, description: nil, metadata: nil, **params) + request_params = { account_id: } + request_params[:description] = description if description + request_params[:metadata] = metadata if metadata + request_params.merge!(params) + + post(version: :v2).call('account_numbers', **request_params) + end + + def _get_account_number(account_number_id) + get(version: :v2).call("account_numbers/#{account_number_id}") + end + + def _list_account_numbers(**params) + get(version: :v2).call('account_numbers', **params) + end + + def _update_account_number(account_number_id, **params) + patch(version: :v2).call("account_numbers/#{account_number_id}", **params) + end + + def build_account_number(data) + Fintoc::Transfers::AccountNumber.new(**data, client: self) + end + end + end +end diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index f52063b..8ef79ee 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -1,12 +1,14 @@ require 'fintoc/base_client' require 'fintoc/transfers/client/entities_methods' require 'fintoc/transfers/client/accounts_methods' +require 'fintoc/transfers/client/account_numbers_methods' module Fintoc module Transfers class Client < BaseClient include EntitiesMethods include AccountsMethods + include AccountNumbersMethods end end end diff --git a/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb b/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb new file mode 100644 index 0000000..c64fead --- /dev/null +++ b/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb @@ -0,0 +1,78 @@ +require 'fintoc/transfers/client/account_numbers_methods' + +RSpec.describe Fintoc::Transfers::AccountNumbersMethods do + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Transfers::AccountNumbersMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def get(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def post(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def patch(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + end + end + + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + + describe '#create_account_number' do + before do + allow(client).to receive(:build_account_number) + end + + it 'calls build_account_number with the response' do + client.create_account_number(account_id: 'acc_123') + expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + end + end + + describe '#get_account_number' do + before do + allow(client).to receive(:build_account_number) + end + + it 'calls build_account_number with the response' do + client.get_account_number('acno_123') + expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + end + end + + describe '#list_account_numbers' do + before do + allow(client) + .to receive(:_list_account_numbers) + .and_return([{ mock: 'response1' }, { mock: 'response2' }]) + allow(client).to receive(:build_account_number) + end + + it 'calls build_account_number for each response' do + client.list_account_numbers + expect(client).to have_received(:build_account_number).with({ mock: 'response1' }) + expect(client).to have_received(:build_account_number).with({ mock: 'response2' }) + end + end + + describe '#update_account_number' do + before do + allow(client).to receive(:build_account_number) + end + + it 'calls build_account_number with the response' do + client.update_account_number('acno_123', description: 'Updated') + expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index f913be4..792f052 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -21,4 +21,6 @@ it_behaves_like 'a client with entities methods' it_behaves_like 'a client with accounts methods' + + it_behaves_like 'a client with account numbers methods' end diff --git a/spec/support/shared_examples/clients/account_numbers_client_examples.rb b/spec/support/shared_examples/clients/account_numbers_client_examples.rb new file mode 100644 index 0000000..c92b4d4 --- /dev/null +++ b/spec/support/shared_examples/clients/account_numbers_client_examples.rb @@ -0,0 +1,67 @@ +RSpec.shared_examples 'a client with account numbers methods' do + let(:account_number_id) { 'acno_326dzRGqxLee3j9TkaBBBMfs2i0' } + let(:account_id) { 'acc_31yYL7h9LVPg121AgFtCyJPDsgM' } + + it 'responds to account number-specific methods' do + expect(client) + .to respond_to(:create_account_number) + .and respond_to(:get_account_number) + .and respond_to(:list_account_numbers) + .and respond_to(:update_account_number) + end + + describe '#create_account_number' do + it 'returns an AccountNumber instance', :vcr do + account_number = client.create_account_number( + account_id:, description: 'Test account number', metadata: { test_id: '12345' } + ) + + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + account_id:, + description: 'Test account number', + object: 'account_number' + ) + end + end + + describe '#get_account_number' do + it 'returns an AccountNumber instance', :vcr do + account_number = client.get_account_number(account_number_id) + + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + id: account_number_id, + object: 'account_number' + ) + end + end + + describe '#list_account_numbers' do + it 'returns an array of AccountNumber instances', :vcr do + account_numbers = client.list_account_numbers + + expect(account_numbers).to all(be_a(Fintoc::Transfers::AccountNumber)) + expect(account_numbers.size).to be >= 1 + expect(account_numbers.first.id).to eq(account_number_id) + end + end + + describe '#update_account_number' do + it 'returns an updated AccountNumber instance', :vcr do + updated_description = 'Updated account number description' + account_number = client.update_account_number( + account_number_id, description: updated_description + ) + + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + id: account_number_id, + description: updated_description + ) + end + end +end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml new file mode 100644 index 0000000..9a08e04 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/account_numbers + body: + encoding: UTF-8 + string: '{"account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","description":"Test + account number","metadata":{"test_id":"12345"}}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 01 Sep 2025 16:46:57 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '332' + Connection: + - close + Cf-Ray: + - 97863f28a88118e5-MIA + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"442b2cd87d7e1a87203a6faed3009bdd" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 74814dce-220d-4d89-ab7f-9790440bd84a + X-Runtime: + - '0.133817' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=ffhqW6LhQ%2Fypc8%2Bz6iw5sbozBYa8WCjEdl8rvTmLhum%2B%2FT2N5fcCmK2OiVQKMoGm4RGFN2s0fZcjr40UsCGu9OdrW8fA6p2h67rrSqYOlJtnSbzXwxTYUS2TqD%2Fn%2F%2F%2B8"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=130539&min_rtt=130055&rtt_var=49117&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1982&delivery_rate=32755&cwnd=59&unsent_bytes=0&cid=db9dd6d09b32fd20&ts=475&x=0" + body: + encoding: UTF-8 + string: '{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:46:57Z","mode":"test","description":"Test + account number","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"}' + recorded_at: Mon, 01 Sep 2025 16:46:57 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml new file mode 100644 index 0000000..51b183e --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/account_numbers/acno_326dzRGqxLee3j9TkaBBBMfs2i0 + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 01 Sep 2025 16:52:36 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '332' + Connection: + - close + Cf-Ray: + - 9786476f1fd41e7b-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"442b2cd87d7e1a87203a6faed3009bdd" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 53940869-7188-40bd-8aa9-8453edf6c082 + X-Runtime: + - '0.062726' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=79CIOfUYM4KWBPevnJKUJNnuZFH%2BbqmQO8Xf5LUGlE%2FXFbngDgRNaifrW1ZB3uPTEOv90vGlEbJJ7x6PkDv4mau8JGOBI9kBWUxb1c2uHCyWcHrnrxv6xTJ0buEW68l4"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=31870&min_rtt=29868&rtt_var=12630&sent=6&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1831&delivery_rate=142627&cwnd=244&unsent_bytes=0&cid=714e21d2176eeb80&ts=329&x=0" + body: + encoding: UTF-8 + string: '{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:46:57Z","mode":"test","description":"Test + account number","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"}' + recorded_at: Mon, 01 Sep 2025 16:52:36 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml new file mode 100644 index 0000000..844ebe4 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/account_numbers + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 01 Sep 2025 16:46:58 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '930' + Connection: + - close + Cf-Ray: + - 97863f2f0c391ac8-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"58e4fdf440240df4932ac91479501c77" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - a83b322e-ac48-4797-a170-ff6bfd49f9e1 + X-Runtime: + - '0.051392' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=dRTTdxBVoYQuL895%2FcWmvxxIdm4KCPnpFHLGMJaF0KvPcssrPKaWnOAH97Odm4SZt0gGKrBmolwmLFck4dHoBTYXFdsVTTiCDECq8cVg%2BGYBpKTRUbzwCxXhrZl7wCMz"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=60788&min_rtt=60655&rtt_var=22841&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1798&delivery_rate=70233&cwnd=33&unsent_bytes=0&cid=1248c3ff5fad2964&ts=383&x=0" + body: + encoding: UTF-8 + string: '[{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:46:57Z","mode":"test","description":"Test + account number","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"},{"id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203297","created_at":"2025-08-29T20:02:00Z","updated_at":"2025-08-29T20:02:00Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"},{"id":"acno_31t0VhIYoVNy4mYjyo8cWZTYcK9","account_id":"acc_31t0VhaF6uFoj0O9oreXw3jDtzL","number":"735969000000203226","created_at":"2025-08-27T20:54:46Z","updated_at":"2025-08-27T20:54:46Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"}]' + recorded_at: Mon, 01 Sep 2025 16:46:58 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml new file mode 100644 index 0000000..f72b7d7 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: patch + uri: https://api.fintoc.com/v2/account_numbers/acno_326dzRGqxLee3j9TkaBBBMfs2i0 + body: + encoding: UTF-8 + string: '{"description":"Updated account number description"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 01 Sep 2025 17:04:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '347' + Connection: + - close + Cf-Ray: + - 9786589b39b2634f-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"82628675d4c8cf1521db24ac25986c9e" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - aa8a0a15-5f5f-4505-b232-869f9b0fd8ca + X-Runtime: + - '0.059235' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=zmI4LMK8CCyLk0eNFxF9GFHTqxyri6qzOSTiJ4tg9Xqhz21k9LEjK1ZAfAU4NausD9Kv2SnsSkIlvRmX4V10SnA%2Bi9R0rRdMtCWtvuk1oVIqqKRie9%2Bnbr7YbGemxjiD"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=51825&min_rtt=30574&rtt_var=26644&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3880&recv_bytes=1952&delivery_rate=139334&cwnd=252&unsent_bytes=0&cid=1dbd524bd165a7ea&ts=323&x=0" + body: + encoding: UTF-8 + string: '{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:54:40Z","mode":"test","description":"Updated + account number description","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"}' + recorded_at: Mon, 01 Sep 2025 17:04:19 GMT +recorded_with: VCR 6.3.1 From 9735e0bb68043e9aebfd788ed6063af5a100485e Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 13:31:45 -0400 Subject: [PATCH 31/72] feature(account-number): Add refresh method to account numbers --- .../transfers/resources/account_number.rb | 5 ++++ .../fintoc/transfers/account_number_spec.rb | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 61ebae5..23b1879 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -41,6 +41,11 @@ def to_s "πŸ”’ #{@number} (#{@id}) - #{@description}" end + def refresh + fresh_account_number = @client.get_account_number(@id) + refresh_from_account_number(fresh_account_number) + end + def enabled? @status == 'enabled' end diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index c11b5f7..773a745 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -103,4 +103,28 @@ end end end + + describe '#refresh' do + let(:refreshed_data) do + data.merge(description: 'Updated description') + end + let(:refreshed_account_number) { described_class.new(**refreshed_data) } + + before do + allow(client) + .to receive(:get_account_number) + .with('acno_Kasf91034gj1AD') + .and_return(refreshed_account_number) + end + + it 'refreshes the account number with the latest data' do + account_number.refresh + expect(account_number.description).to eq('Updated description') + end + + it 'calls get_account_number with the correct id' do + account_number.refresh + expect(client).to have_received(:get_account_number).with('acno_Kasf91034gj1AD') + end + end end From f890227a55b36a843da3d6aaf415eb3052b9345e Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 13:36:23 -0400 Subject: [PATCH 32/72] feature(account-numbers): Add update method to account numbers --- .../transfers/resources/account_number.rb | 10 ++++++ .../fintoc/transfers/account_number_spec.rb | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 23b1879..99280e8 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -46,6 +46,16 @@ def refresh refresh_from_account_number(fresh_account_number) end + def update(description: nil, status: nil, metadata: nil) + params = {} + params[:description] = description if description + params[:status] = status if status + params[:metadata] = metadata if metadata + + updated_account_number = @client.update_account_number(@id, **params) + refresh_from_account_number(updated_account_number) + end + def enabled? @status == 'enabled' end diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index 773a745..7da62fb 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -127,4 +127,39 @@ expect(client).to have_received(:get_account_number).with('acno_Kasf91034gj1AD') end end + + describe '#update' do + let(:new_metadata) { { user_id: '54321' } } + let(:new_description) { 'New description' } + let(:new_status) { 'disabled' } + let(:updated_data) do + data.merge(description: new_description, status: new_status, metadata: new_metadata) + end + let(:updated_account_number) { described_class.new(**updated_data) } + + before do + allow(client).to receive(:update_account_number).and_return(updated_account_number) + end + + it 'updates all provided parameters' do + account_number + .update(description: new_description, status: new_status, metadata: new_metadata) + + expect(account_number.description).to eq(new_description) + expect(account_number.status).to eq(new_status) + expect(account_number.metadata).to eq(new_metadata) + end + + it 'calls update_account_number with all parameters' do + account_number + .update(description: new_description, status: new_status, metadata: new_metadata) + + expect(client).to have_received(:update_account_number).with( + account_number.id, + description: new_description, + status: new_status, + metadata: new_metadata + ) + end + end end From bd2e4087837f8e8a64d8862dffd05ccccb9765cb Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 15:21:02 -0400 Subject: [PATCH 33/72] refactor(resources): Do not refresh using initialize as it returns a different object --- lib/fintoc/transfers/resources/account.rb | 26 +++++++++---------- .../transfers/resources/account_number.rb | 26 +++++++++---------- lib/fintoc/transfers/resources/entity.rb | 16 +++++------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index e145f35..b32d652 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -72,20 +72,18 @@ def closed? def refresh_from_account(account) raise 'Account must be the same instance' unless account.id == @id - initialize( - id: account.id, - object: account.object, - mode: account.mode, - description: account.description, - available_balance: account.available_balance, - currency: account.currency, - is_root: account.is_root, - root_account_number_id: account.root_account_number_id, - root_account_number: account.root_account_number, - status: account.status, - entity: account.entity, - client: @client - ) + @object = account.object + @mode = account.mode + @description = account.description + @available_balance = account.available_balance + @currency = account.currency + @is_root = account.is_root + @root_account_number_id = account.root_account_number_id + @root_account_number = account.root_account_number + @status = account.status + @entity = account.entity + + self end end end diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 99280e8..4eb9d37 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -73,20 +73,18 @@ def root? def refresh_from_account_number(account_number) raise 'AccountNumber must be the same instance' unless account_number.id == @id - initialize( - id: account_number.id, - object: account_number.object, - description: account_number.description, - number: account_number.number, - created_at: account_number.created_at, - updated_at: account_number.updated_at, - mode: account_number.mode, - status: account_number.status, - is_root: account_number.is_root, - account_id: account_number.account_id, - metadata: account_number.metadata, - client: @client - ) + @object = account_number.object + @description = account_number.description + @number = account_number.number + @created_at = account_number.created_at + @updated_at = account_number.updated_at + @mode = account_number.mode + @status = account_number.status + @is_root = account_number.is_root + @account_id = account_number.account_id + @metadata = account_number.metadata + + self end end end diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/transfers/resources/entity.rb index 8dfbc05..2828bb8 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/transfers/resources/entity.rb @@ -40,15 +40,13 @@ def refresh def refresh_from_entity(entity) raise 'Entity must be the same instance' unless entity.id == @id - initialize( - id: entity.id, - object: entity.object, - mode: entity.mode, - holder_name: entity.holder_name, - holder_id: entity.holder_id, - is_root: entity.is_root, - client: @client - ) + @object = entity.object + @mode = entity.mode + @holder_name = entity.holder_name + @holder_id = entity.holder_id + @is_root = entity.is_root + + self end end end From a658fe289fa732002d8c54e3d0eb8d071ecc6b3d Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Mon, 1 Sep 2025 16:36:41 -0400 Subject: [PATCH 34/72] feature(transfers): Add transfers resources --- lib/fintoc/transfers/resources/transfer.rb | 96 +++++++++ spec/lib/fintoc/transfers/transfer_spec.rb | 215 +++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 lib/fintoc/transfers/resources/transfer.rb create mode 100644 spec/lib/fintoc/transfers/transfer_spec.rb diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb new file mode 100644 index 0000000..18a4a03 --- /dev/null +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -0,0 +1,96 @@ +require 'money' +require 'fintoc/utils' + +module Fintoc + module Transfers + class Transfer + include Utils + + attr_reader :id, :object, :amount, :currency, :direction, :status, :mode, + :post_date, :transaction_date, :comment, :reference_id, :receipt_url, + :tracking_key, :return_reason, :counterparty, :account_number, + :metadata, :created_at + + def initialize( # rubocop:disable Metrics/MethodLength + id:, + object:, + amount:, + currency:, + status:, + mode:, + counterparty:, + direction: nil, + post_date: nil, + transaction_date: nil, + comment: nil, + reference_id: nil, + receipt_url: nil, + tracking_key: nil, + return_reason: nil, + account_number: nil, + metadata: {}, + created_at: nil, + client: nil, + ** + ) + @id = id + @object = object + @amount = amount + @currency = currency + @direction = direction + @status = status + @mode = mode + @post_date = post_date + @transaction_date = transaction_date + @comment = comment + @reference_id = reference_id + @receipt_url = receipt_url + @tracking_key = tracking_key + @return_reason = return_reason + @counterparty = counterparty + @account_number = account_number + @metadata = metadata || {} + @created_at = created_at + @client = client + end + + def to_s + amount_str = Money.from_cents(@amount, @currency).format + direction_icon = inbound? ? '⬇️' : '⬆️' + "#{direction_icon} #{amount_str} (#{@id}) - #{@status}" + end + + def pending? + @status == 'pending' + end + + def succeeded? + @status == 'succeeded' + end + + def failed? + @status == 'failed' + end + + def returned? + @status == 'returned' + end + + def return_pending? + @status == 'return_pending' + end + + def rejected? + @status == 'rejected' + end + + def inbound? + @direction == 'inbound' + end + + def outbound? + @direction == 'outbound' + end + end + end +end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb new file mode 100644 index 0000000..296addc --- /dev/null +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -0,0 +1,215 @@ +require 'fintoc/transfers/resources/transfer' + +RSpec.describe Fintoc::Transfers::Transfer do + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } + let(:client) { Fintoc::Transfers::Client.new(api_key) } + + let(:counterparty_data) do + { + holder_id: 'LFHU290523OG0', + holder_name: 'Jon Snow', + account_number: '012969123456789120', + account_type: 'clabe', + institution: { + id: '40012', + name: 'BBVA MEXICO', + country: 'mx' + } + } + end + + let(:account_number_data) do + { + id: 'acno_Kasf91034gj1AD', + object: 'account_number', + account_id: 'acc_Jas92lf9adg94ka', + description: 'Mis payins', + number: '738969123456789120', + created_at: '2024-03-01T20:09:42.949787176Z', + mode: 'test', + metadata: { + id_cliente: '12343212' + } + } + end + + let(:data) do + { + id: 'tr_jKaHD105H', + object: 'transfer', + amount: 59013, + currency: 'MXN', + direction: 'outbound', + status: 'pending', + mode: 'test', + post_date: nil, + transaction_date: nil, + comment: 'Pago de credito 10451', + reference_id: '150195', + receipt_url: nil, + tracking_key: nil, + return_reason: nil, + counterparty: counterparty_data, + account_number: account_number_data, + metadata: {}, + created_at: '2020-04-17T00:00:00.000Z', + client: client + } + end + + let(:transfer) { described_class.new(**data) } + + describe '#new' do + it 'creates an instance of Transfer' do + expect(transfer).to be_an_instance_of(described_class) + end + + it 'sets all attributes correctly' do # rubocop:disable RSpec/ExampleLength + expect(transfer).to have_attributes( + id: 'tr_jKaHD105H', + object: 'transfer', + amount: 59013, + currency: 'MXN', + direction: 'outbound', + status: 'pending', + mode: 'test', + post_date: nil, + transaction_date: nil, + comment: 'Pago de credito 10451', + reference_id: '150195', + receipt_url: nil, + tracking_key: nil, + return_reason: nil, + counterparty: counterparty_data, + account_number: account_number_data, + metadata: {}, + created_at: '2020-04-17T00:00:00.000Z' + ) + end + end + + describe '#to_s' do + it 'returns a string representation' do + expect(transfer.to_s) + .to include('⬆️') + .and include('tr_jKaHD105H') + .and include('pending') + end + + context 'when transfer is inbound' do + before { data[:direction] = 'inbound' } + + it 'uses the inbound arrow' do + expect(transfer.to_s).to include('⬇️') + end + end + end + + describe 'status predicates' do + it 'responds to status predicate methods' do + expect(transfer) + .to respond_to(:pending?) + .and respond_to(:succeeded?) + .and respond_to(:failed?) + .and respond_to(:returned?) + .and respond_to(:return_pending?) + .and respond_to(:rejected?) + end + + it 'returns correct status for pending transfer' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).to be_pending + expect(transfer).not_to be_succeeded + expect(transfer).not_to be_failed + expect(transfer).not_to be_returned + expect(transfer).not_to be_return_pending + expect(transfer).not_to be_rejected + end + + context 'when status is succeeded' do + before { data[:status] = 'succeeded' } + + it 'returns correct status' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).not_to be_pending + expect(transfer).to be_succeeded + expect(transfer).not_to be_failed + expect(transfer).not_to be_returned + expect(transfer).not_to be_return_pending + expect(transfer).not_to be_rejected + end + end + + context 'when status is failed' do + before { data[:status] = 'failed' } + + it 'returns correct status' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).not_to be_pending + expect(transfer).not_to be_succeeded + expect(transfer).to be_failed + expect(transfer).not_to be_returned + expect(transfer).not_to be_return_pending + expect(transfer).not_to be_rejected + end + end + + context 'when status is returned' do + before { data[:status] = 'returned' } + + it 'returns correct status' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).not_to be_pending + expect(transfer).not_to be_succeeded + expect(transfer).not_to be_failed + expect(transfer).to be_returned + expect(transfer).not_to be_return_pending + expect(transfer).not_to be_rejected + end + end + + context 'when status is return_pending' do + before { data[:status] = 'return_pending' } + + it 'returns correct status' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).not_to be_pending + expect(transfer).not_to be_succeeded + expect(transfer).not_to be_failed + expect(transfer).not_to be_returned + expect(transfer).to be_return_pending + expect(transfer).not_to be_rejected + end + end + + context 'when status is rejected' do + before { data[:status] = 'rejected' } + + it 'returns correct status' do # rubocop:disable RSpec/MultipleExpectations + expect(transfer).not_to be_pending + expect(transfer).not_to be_succeeded + expect(transfer).not_to be_failed + expect(transfer).not_to be_returned + expect(transfer).not_to be_return_pending + expect(transfer).to be_rejected + end + end + end + + describe 'direction predicates' do + it 'responds to direction predicate methods' do + expect(transfer) + .to respond_to(:inbound?) + .and respond_to(:outbound?) + end + + it 'returns correct direction for outbound transfer' do + expect(transfer).to be_outbound + expect(transfer).not_to be_inbound + end + + context 'when direction is inbound' do + before { data[:direction] = 'inbound' } + + it 'returns correct direction' do + expect(transfer).to be_inbound + expect(transfer).not_to be_outbound + end + end + end +end From 8c4162c357782028b735fdacb3050b4aa3b3471c Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Tue, 2 Sep 2025 18:31:29 -0400 Subject: [PATCH 35/72] feature(jws): Add JWS private key to request args for use of transfers create and patch methods --- lib/fintoc/base_client.rb | 40 +++++--- lib/fintoc/client.rb | 4 +- lib/fintoc/jws.rb | 83 ++++++++++++++++ spec/lib/fintoc/jws_spec.rb | 120 +++++++++++++++++++++++ spec/lib/fintoc/transfers/client_spec.rb | 3 +- 5 files changed, 234 insertions(+), 16 deletions(-) create mode 100644 lib/fintoc/jws.rb create mode 100644 spec/lib/fintoc/jws_spec.rb diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index 352fd82..9216961 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -4,18 +4,20 @@ require 'fintoc/errors' require 'fintoc/constants' require 'fintoc/version' +require 'fintoc/jws' module Fintoc class BaseClient include Utils - def initialize(api_key) + def initialize(api_key, jws_private_key: nil) @api_key = api_key @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" @headers = { Authorization: "Bearer #{@api_key}", 'User-Agent': @user_agent } @link_headers = nil @link_header_pattern = '<(?.*)>;\s*rel="(?.*)"' @default_params = {} + @jws = jws_private_key ? Fintoc::JWS.new(jws_private_key) : nil end def get(version: :v1) @@ -26,18 +28,18 @@ def delete(version: :v1) request('delete', version: version) end - def post(version: :v1) - request('post', version: version) + def post(version: :v1, use_jws: false) + request('post', version: version, use_jws: use_jws) end - def patch(version: :v1) - request('patch', version: version) + def patch(version: :v1, use_jws: false) + request('patch', version: version, use_jws: use_jws) end - def request(method, version: :v1) + def request(method, version: :v1, use_jws: false) proc do |resource, **kwargs| parameters = params(method, **kwargs) - response = make_request(method, resource, parameters, version: version) + response = make_request(method, resource, parameters, version: version, use_jws: use_jws) content = JSON.parse(response.body, symbolize_names: true) if response.status.client_error? || response.status.server_error? @@ -78,16 +80,28 @@ def parse_headers(dict, link) dict end - def make_request(method, resource, parameters, version: :v1) + def build_url(resource, version: :v1) + base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL + "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" + end + + def make_request(method, resource, parameters, version: :v1, use_jws: false) # this is to handle url returned in the link headers # I'm sure there is a better and more clever way to solve this if resource.start_with? 'https' - client.send(method, resource) - else - base_url = version == :v2 ? Fintoc::Constants::BASE_URL_V2 : Fintoc::Constants::BASE_URL - url = "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" - client.send(method, url, parameters) + return client.send(method, resource) + end + + url = build_url(resource, version:) + + if use_jws && @jws && %w[post patch put].include?(method.downcase) + request_body = parameters[:json]&.to_json || '' + jws_signature = @jws.generate_signature(request_body) + + return client.headers('Fintoc-JWS-Signature' => jws_signature).send(method, url, parameters) end + + client.send(method, url, parameters) end def params(method, **kwargs) diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 0e35f6d..166d31c 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -8,9 +8,9 @@ class Client attr_reader :movements, :transfers - def initialize(api_key) + def initialize(api_key, jws_private_key: nil) @movements = Fintoc::Movements::Client.new(api_key) - @transfers = Fintoc::Transfers::Client.new(api_key) + @transfers = Fintoc::Transfers::Client.new(api_key, jws_private_key: jws_private_key) end # Delegate common methods to maintain backward compatibility diff --git a/lib/fintoc/jws.rb b/lib/fintoc/jws.rb new file mode 100644 index 0000000..9110ccd --- /dev/null +++ b/lib/fintoc/jws.rb @@ -0,0 +1,83 @@ +require 'openssl' +require 'json' +require 'base64' +require 'securerandom' + +module Fintoc + class JWS + def initialize(private_key) + unless private_key.is_a?(OpenSSL::PKey::RSA) + raise ArgumentError, 'private_key must be an OpenSSL::PKey::RSA instance' + end + + @private_key = private_key + end + + def generate_signature(raw_body) + body_string = raw_body.is_a?(Hash) ? raw_body.to_json : raw_body.to_s + + headers = { + alg: 'RS256', + nonce: SecureRandom.hex(16), + ts: Time.now.to_i, + crit: %w[ts nonce] + } + + protected_base64 = base64url_encode(headers.to_json) + payload_base64 = base64url_encode(body_string) + signing_input = "#{protected_base64}.#{payload_base64}" + + signature = @private_key.sign(OpenSSL::Digest.new('SHA256'), signing_input) + signature_base64 = base64url_encode(signature) + + "#{protected_base64}.#{signature_base64}" + end + + def parse_signature(signature) + protected_b64, signature_b64 = signature.split('.') + + { + protected_headers: decode_protected_headers(protected_b64), + signature_bytes: decode_signature(signature_b64), + protected_b64: protected_b64, + signature_b64: signature_b64 + } + end + + def verify_signature(signature, payload) + parsed = parse_signature(signature) + + # Reconstruct the signing input + payload_json = payload.is_a?(Hash) ? payload.to_json : payload.to_s + payload_b64 = base64url_encode(payload_json) + signing_input = "#{parsed[:protected_b64]}.#{payload_b64}" + + # Verify with public key + public_key = @private_key.public_key + public_key.verify(OpenSSL::Digest.new('SHA256'), parsed[:signature_bytes], signing_input) + end + + private + + def decode_protected_headers(protected_b64) + padded = add_padding(protected_b64) + + protected_json = Base64.urlsafe_decode64(padded) + JSON.parse(protected_json, symbolize_names: true) + end + + def decode_signature(signature_b64) + padded = add_padding(signature_b64) + + Base64.urlsafe_decode64(padded) + end + + def add_padding(b64) + (b64.length % 4).zero? ? b64 : (b64 + ('=' * (4 - (b64.length % 4)))) + end + + def base64url_encode(data) + Base64.urlsafe_encode64(data).tr('=', '') + end + end +end diff --git a/spec/lib/fintoc/jws_spec.rb b/spec/lib/fintoc/jws_spec.rb new file mode 100644 index 0000000..bcebfec --- /dev/null +++ b/spec/lib/fintoc/jws_spec.rb @@ -0,0 +1,120 @@ +require 'fintoc/jws' +require 'openssl' +require 'json' +require 'base64' + +RSpec.describe Fintoc::JWS do + let(:private_key) do + OpenSSL::PKey::RSA.new(2048) + end + + let(:jws) { described_class.new(private_key) } + + describe '#initialize' do + it 'accepts an OpenSSL::PKey::RSA object' do + expect { described_class.new(private_key) }.not_to raise_error + end + + it 'raises an error for invalid input' do + expect { described_class.new('invalid_string') } + .to raise_error(ArgumentError, 'private_key must be an OpenSSL::PKey::RSA instance') + end + + it 'raises an error for numeric input' do + expect { described_class.new(123) } + .to raise_error(ArgumentError, 'private_key must be an OpenSSL::PKey::RSA instance') + end + end + + describe '#generate_signature' do + let(:payload) { { amount: 1000, currency: 'MXN' } } + + it 'generates a valid JWS signature' do + signature = jws.generate_signature(payload) + + expect(signature).to be_a(String) + expect(signature.split('.').length).to eq(2) + end + + it 'includes required headers' do + signature = jws.generate_signature(payload) + parsed = jws.parse_signature(signature) + + expect(parsed[:protected_headers]).to include( + alg: 'RS256', + nonce: be_a(String), + ts: be_a(Integer), + crit: %w[ts nonce] + ) + end + + it 'accepts string payloads' do + string_payload = '{"amount":1000,"currency":"MXN"}' + signature = jws.generate_signature(string_payload) + + expect(signature).to be_a(String) + expect(signature.split('.').length).to eq(2) + end + + it 'generates different signatures for the same payload' do + signature1 = jws.generate_signature(payload) + signature2 = jws.generate_signature(payload) + + expect(signature1).not_to eq(signature2) + end + + it 'generates verifiable signatures' do + signature = jws.generate_signature(payload) + + expect(jws.verify_signature(signature, payload)).to be true + end + end + + describe '#parse_signature' do + let(:payload) { { amount: 1000, currency: 'MXN' } } + let(:signature) { jws.generate_signature(payload) } + + it 'parses signature components correctly' do + parsed = jws.parse_signature(signature) + + expect(parsed).to include( + protected_headers: be_a(Hash), + signature_bytes: be_a(String), + protected_b64: be_a(String), + signature_b64: be_a(String) + ) + end + + it 'includes correct header values' do + parsed = jws.parse_signature(signature) + + expect(parsed[:protected_headers]).to include( + alg: 'RS256', + crit: %w[ts nonce] + ) + end + end + + describe '#verify_signature' do + let(:payload) { { amount: 1000, currency: 'MXN' } } + + it 'verifies valid signatures' do + signature = jws.generate_signature(payload) + expect(jws.verify_signature(signature, payload)).to be true + end + + it 'rejects signatures with wrong payload' do + signature = jws.generate_signature(payload) + wrong_payload = { amount: 2000, currency: 'CLP' } + + expect(jws.verify_signature(signature, wrong_payload)).to be false + end + + it 'works with string payloads' do + string_payload = '{"amount":1000,"currency":"MXN"}' + signature = jws.generate_signature(string_payload) + + expect(jws.verify_signature(signature, string_payload)).to be true + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index 792f052..83ac4b8 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -2,7 +2,8 @@ RSpec.describe Fintoc::Transfers::Client do let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } - let(:client) { described_class.new(api_key) } + let(:jws_private_key) { nil } + let(:client) { described_class.new(api_key, jws_private_key: jws_private_key) } describe '.new' do it 'creates an instance of TransfersClient' do From 27592799a47fc8064bd467f0128209bfd313a782 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Tue, 2 Sep 2025 18:32:54 -0400 Subject: [PATCH 36/72] feature(transfers): Add transfers methods --- lib/fintoc/transfers/client/client.rb | 2 + .../transfers/client/transfers_methods.rb | 49 +++++++ spec/lib/fintoc/transfers/client_spec.rb | 2 + spec/lib/fintoc/transfers/transfer_spec.rb | 14 +- .../clients/transfers_client_examples.rb | 126 ++++++++++++++++++ .../returns_a_Transfer_instance.yml | 81 +++++++++++ .../returns_a_Transfer_instance.yml | 75 +++++++++++ .../accepts_filtering_parameters.yml | 77 +++++++++++ ...returns_an_array_of_Transfer_instances.yml | 80 +++++++++++ ...er_instance_with_return_pending_status.yml | 80 +++++++++++ 10 files changed, 579 insertions(+), 7 deletions(-) create mode 100644 lib/fintoc/transfers/client/transfers_methods.rb create mode 100644 spec/support/shared_examples/clients/transfers_client_examples.rb create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index 8ef79ee..6c945ba 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -2,6 +2,7 @@ require 'fintoc/transfers/client/entities_methods' require 'fintoc/transfers/client/accounts_methods' require 'fintoc/transfers/client/account_numbers_methods' +require 'fintoc/transfers/client/transfers_methods' module Fintoc module Transfers @@ -9,6 +10,7 @@ class Client < BaseClient include EntitiesMethods include AccountsMethods include AccountNumbersMethods + include TransfersMethods end end end diff --git a/lib/fintoc/transfers/client/transfers_methods.rb b/lib/fintoc/transfers/client/transfers_methods.rb new file mode 100644 index 0000000..7ab1e07 --- /dev/null +++ b/lib/fintoc/transfers/client/transfers_methods.rb @@ -0,0 +1,49 @@ +require 'fintoc/transfers/resources/transfer' + +module Fintoc + module Transfers + module TransfersMethods + def create_transfer(amount:, currency:, account_id:, counterparty:, **params) + data = _create_transfer(amount:, currency:, account_id:, counterparty:, **params) + build_transfer(data) + end + + def get_transfer(transfer_id) + data = _get_transfer(transfer_id) + build_transfer(data) + end + + def list_transfers(**params) + _list_transfers(**params).map { |data| build_transfer(data) } + end + + def return_transfer(transfer_id) + data = _return_transfer(transfer_id) + build_transfer(data) + end + + private + + def _create_transfer(amount:, currency:, account_id:, counterparty:, **params) + post(version: :v2, use_jws: true) + .call('transfers', amount:, currency:, account_id:, counterparty:, **params) + end + + def _get_transfer(transfer_id) + get(version: :v2).call("transfers/#{transfer_id}") + end + + def _list_transfers(**params) + get(version: :v2).call('transfers', **params) + end + + def _return_transfer(transfer_id) + post(version: :v2, use_jws: true).call('transfers/return', transfer_id:) + end + + def build_transfer(data) + Fintoc::Transfers::Transfer.new(**data, client: self) + end + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index 83ac4b8..cc6c026 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -24,4 +24,6 @@ it_behaves_like 'a client with accounts methods' it_behaves_like 'a client with account numbers methods' + + it_behaves_like 'a client with transfers methods' end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb index 296addc..ccffb6c 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -8,7 +8,7 @@ { holder_id: 'LFHU290523OG0', holder_name: 'Jon Snow', - account_number: '012969123456789120', + account_number: '735969000000203297', account_type: 'clabe', institution: { id: '40012', @@ -20,11 +20,11 @@ let(:account_number_data) do { - id: 'acno_Kasf91034gj1AD', + id: 'acno_326dzRGqxLee3j9TkaBBBMfs2i0', object: 'account_number', - account_id: 'acc_Jas92lf9adg94ka', + account_id: 'acc_31yYL7h9LVPg121AgFtCyJPDsgM', description: 'Mis payins', - number: '738969123456789120', + number: '735969000000203365', created_at: '2024-03-01T20:09:42.949787176Z', mode: 'test', metadata: { @@ -35,7 +35,7 @@ let(:data) do { - id: 'tr_jKaHD105H', + id: 'tr_329NGN1M4If6VvcMRALv4gjAQJx', object: 'transfer', amount: 59013, currency: 'MXN', @@ -66,7 +66,7 @@ it 'sets all attributes correctly' do # rubocop:disable RSpec/ExampleLength expect(transfer).to have_attributes( - id: 'tr_jKaHD105H', + id: 'tr_329NGN1M4If6VvcMRALv4gjAQJx', object: 'transfer', amount: 59013, currency: 'MXN', @@ -92,7 +92,7 @@ it 'returns a string representation' do expect(transfer.to_s) .to include('⬆️') - .and include('tr_jKaHD105H') + .and include('tr_329NGN1M4If6VvcMRALv4gjAQJx') .and include('pending') end diff --git a/spec/support/shared_examples/clients/transfers_client_examples.rb b/spec/support/shared_examples/clients/transfers_client_examples.rb new file mode 100644 index 0000000..fbf6a5d --- /dev/null +++ b/spec/support/shared_examples/clients/transfers_client_examples.rb @@ -0,0 +1,126 @@ +require 'openssl' + +RSpec.shared_examples 'a client with transfers methods' do + let(:jws_private_key) do + key_string = "-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNLwwQr/uFToDH +8x1GHlW5gRsngNp8J+sg8vZc5jX+dISZh42CNM2eMSyPOMLSZL08xIA9ISoxjCJb +rpJ7MY9OKOZdrFheclz8e3z/hVcXpmgT0JARYIyKUb2sgoh1JsH3aSQILTc4KuDM +dm0+WIWl9tOqKXm23j9RcYL+WOUCcLYNj3xga39CnXdqT3dy2fMIOJ+vZxfSxPhG +EBTyV6v9jrMbRukhxllTqDc64WkVdt0MOvzFzcSNkGcvHRdrK1w+x5IhfsYGtv+9 +mz+fvmI88qGsb0x8peGVDgWfQjykxrB/8umpQKANn9bqjyay+ogRRwv05dmaB/gm +IycvGXE7AgMBAAECggEAI4gTKcyf3MzkZjvGhP75z175KdUZgMiU4ibQ3POMxBy/ +XaroqXSlatCPK9ojerWxQ5Wvs2ZL3TqsNH49pZHGhD127x/KSci6K4ri8YjQtSq+ ++Tdzy16R194h337XTJpCmqqdb8EMv/BE74NOla5UrpHYw63dAvrnsh3bFlqkhdBZ +E5OBfdLyxGy5FYdewV803a8XGfnDfT7RrsdWhPib8E3i+wix+/dv10AX/+Y6VPpG +2EPXRV63UtmO2EVXyIGT5kSAnzZBJPIB3EYTlm1A86PxQGVD4X8LAUXIj6VRVC8h +B1KXb5YZ9W1vYmKyWUZPyMQHMpUTNGEuU/EtN0aOCQKBgQD+zMd1+3BhoSwBusXb +EK2SBJwn9TfqdUghsFHNz0xjvpAFKpO55qA7XyilZwZeJsOzPQZ33ERCRk18crCd +Q6oWI15xKjPl+Dfxf4UYjokx/iQCCHu8lJ6TXcEwXniIs6CVsUq9QV+s6JBlb3C4 +qD/wwp7VrmbcMLfIUs3nb3tqHQKBgQDOJnGylmqC/l4BCZj9BhLiVg7nioY24lG1 +9DY0/nnnbuMtDQ+8VUKtt93Or8giejOVqj3BZ8/TflkwCQAt9cIvFG50aTVZBo2E +4uPJLGSBLrQqpuUNPR2O239o4RqGgDICkh9TRH9D9GsekLRCgefLRubUyuZZ4pI9 +j1ty2kMpNwKBgGtYJmgEKBJZbkrEPvrNifJMUuVan9X81wiqWaxVOx+CdvZWO6pE +CRk6O8uDHeGofyYR/ZmdiHxLVfWp89ItYYi2GeGfIAIwkpEBYjc4RYB0SwM4Q7js +++mlw+/2vN0KoAqwiIY29nHIAJ1bV6fT6iwqMfRf5yG4vJR+nhR0mQ/ZAoGAfgLJ +5RxEpyXNWF0Bg0i/KlLocWgfelUFFW/d4q7a3TjO7K7bO4fyZjXKA5k3gLup5IZX +kW1fgCvvYIlf7rgWpqiai9XzoiN7RgtaqZHVLZHa12eFA36kHrrVOsq+aBDcgO3I +8CEimetBv0E8rpqxkXQZjWEpRTBVrAOBJsd73ikCgYAwf4fnpTcEa4g6ejmbjkXw +yacTlKFuxyLZHZ5R7D0+Fj19gwm9EzvrRIEcX84ebiJ8P1bL3kycQmLV19zE5B3M +pcsQmZ28/Oa3xCPiy8CDyDuiDbbNfnR1Ot3IbgfFL7xPYySljJbMyl7vhKJIacWs +draAAQ5iJEb5BR8AmL6tAQ== +-----END PRIVATE KEY-----" + OpenSSL::PKey::RSA.new(key_string) + end + let(:transfer_id) { 'tr_329NGN1M4If6VvcMRALv4gjAQJx' } + let(:account_id) { 'acc_31yYL7h9LVPg121AgFtCyJPDsgM' } + + let(:counterparty) do + { + holder_id: 'LFHU290523OG0', + holder_name: 'Jon Snow', + account_number: '735969000000203297', + account_type: 'clabe', + institution_id: '40012' + } + end + + let(:transfer_data) do + { + amount: 50000, + currency: 'MXN', + account_id:, + counterparty:, + comment: 'Test payment', + reference_id: '123456' + } + end + + it 'responds to transfer-specific methods' do + expect(client) + .to respond_to(:create_transfer) + .and respond_to(:get_transfer) + .and respond_to(:list_transfers) + .and respond_to(:return_transfer) + end + + describe '#create_transfer' do + it 'returns a Transfer instance', :vcr do + transfer = client.create_transfer(**transfer_data) + + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + amount: 50000, + currency: 'MXN', + comment: 'Test payment', + reference_id: '123456', + status: 'pending' + ) + end + end + + describe '#get_transfer' do + it 'returns a Transfer instance', :vcr do + transfer = client.get_transfer(transfer_id) + + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + id: transfer_id, + object: 'transfer' + ) + end + end + + describe '#list_transfers' do + it 'returns an array of Transfer instances', :vcr do + transfers = client.list_transfers + + expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers.size).to be >= 1 + end + + it 'accepts filtering parameters', :vcr do + transfers = client.list_transfers(status: 'succeeded', direction: 'outbound') + + expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers).to all(have_attributes(status: 'succeeded', direction: 'outbound')) + end + end + + describe '#return_transfer' do + let(:transfer_id) { 'tr_329R3l5JksDkoevCGTOBsugCsnb' } + + it 'returns a Transfer instance with return_pending status', :vcr do + transfer = client.return_transfer(transfer_id) + + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + id: transfer_id, + status: 'return_pending' + ) + end + end +end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml new file mode 100644 index 0000000..0701bbd --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml @@ -0,0 +1,81 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/transfers + body: + encoding: UTF-8 + string: '{"amount":50000,"currency":"MXN","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","counterparty":{"holder_id":"LFHU290523OG0","holder_name":"Jon + Snow","account_number":"735969000000203297","account_type":"clabe","institution_id":"40012"},"comment":"Test + payment","reference_id":"123456"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Fintoc-Jws-Signature: + - eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiMWU0NzU1ZGNlYzFhZjc2NmNhNjQ2ZjI4NzJlOTc1MmYiLCJ0cyI6MTc1NjgyODczMSwiY3JpdCI6WyJ0cyIsIm5vbmNlIl19.jXtZ3_PClHDiL_wMDGXQqtcUu3Oomn-jRACxvmNmJkCK_ETBHNRA1TMW4SFQqSuEW12XX1R1gA2UlOoKDJOLIZHRX6d4muvbcR32gio9mGEDst9e1cHZkUG0QT7eF_Cb4mseAvjeCTbWvIV3f6-FQ9zF0biffejPVsZvg3KArgbyTt3O1_sst1bng4zuTyVaSB4XEHK8gy-HNyJOHKX9GXGGsE7Mma--LeOneDJQCcoLe3-oXmBtkHwLEFRsgNK6jF8-jYJkDPKR5oFA6EuT-Yr1oXiAhsPr5FDZy3H6i99cz14dPk0iUN-UdozW8jj7wLUtWSYLBKrWoPkm5iXn_w + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 201 + message: Created + headers: + Date: + - Tue, 02 Sep 2025 15:58:51 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '863' + Connection: + - close + Cf-Ray: + - 978e36138f38b861-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"3bcd22908f94497674e96ead614e8a01" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 96e11256-7b3e-42e6-8514-882728242e85 + X-Runtime: + - '0.293159' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=Iv7AS7YO%2FSvC6uTUZgyWC2%2BaUfgdXl7xakqe1BSg2PGNCHtalgXnlviJpWj1wQJK3dsFWG9U6L0AuPx2E0tMdXKTtuVH8gXuCvBOmscmnic9ljwLzFlpxqN0l1IZeAL4"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=30125&min_rtt=30083&rtt_var=11366&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=2641&delivery_rate=140016&cwnd=252&unsent_bytes=0&cid=b848458890c32a83&ts=558&x=0" + body: + encoding: UTF-8 + string: '{"object":"transfer","id":"tr_329NGN1M4If6VvcMRALv4gjAQJx","amount":50000,"currency":"MXN","direction":"outbound","status":"pending","transaction_date":"2025-09-02T15:58:51Z","post_date":null,"comment":"Test + payment","reference_id":"123456","tracking_key":"202509029073500000000000000006","receipt_url":null,"mode":"test","counterparty":{"holder_id":"LFHU290523OG0","holder_name":"Jon + Snow","account_number":"735969000000203297","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"account_number":{"id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203297","created_at":"2025-08-29T20:02:00Z","updated_at":"2025-08-29T20:02:00Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"},"metadata":{},"return_reason":null}' + recorded_at: Tue, 02 Sep 2025 15:58:51 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml new file mode 100644 index 0000000..e5409f2 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml @@ -0,0 +1,75 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/transfers/tr_329NGN1M4If6VvcMRALv4gjAQJx + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 02 Sep 2025 15:59:54 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1070' + Connection: + - close + Cf-Ray: + - 978e379f2c927842-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"37bbc649f214cfce2f8d9aba6caecf21" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 726056fc-bf78-483a-9983-f2453fba746b + X-Runtime: + - '0.059927' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=FAx0QL7Eef4ZkT1QR%2BkB0cgqRpZSc8mZ1kyh6SsnZcqbg6fJZ8bi7euv7xDBrfgrqPI1HfMqlYq9EgszMsvIeR37MiKYP%2FXsLOzy6%2FyMrq3a9QkVFFsPv2s7H5extDUw"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=32899&min_rtt=32105&rtt_var=12607&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1823&delivery_rate=132689&cwnd=253&unsent_bytes=0&cid=a29d81c8d5b56467&ts=316&x=0" + body: + encoding: UTF-8 + string: '{"object":"transfer","id":"tr_329NGN1M4If6VvcMRALv4gjAQJx","amount":50000,"currency":"MXN","direction":"outbound","status":"succeeded","transaction_date":"2025-09-02T15:58:51Z","post_date":"2025-09-02T00:00:00Z","comment":"Test + payment","reference_id":"123456","tracking_key":"202509029073500000000000000006","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=qOK7ARfoEwlVpkLWUmFvihhtr5%2B4Gf1LA%2FnWvzdRnC8vLzAGeXauPtgz1bSaTV%2BmUP9oThn6%2B%2Bujt23nuyI3u8OR00eq5WezdAT1eG%2BhBvo%3D","mode":"test","counterparty":{"holder_id":"LFHU290523OG0","holder_name":"Jon + Snow","account_number":"735969000000203297","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"account_number":{"id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203297","created_at":"2025-08-29T20:02:00Z","updated_at":"2025-08-29T20:02:00Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"},"metadata":{},"return_reason":null}' + recorded_at: Tue, 02 Sep 2025 15:59:54 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml new file mode 100644 index 0000000..c6ec808 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/transfers?direction=outbound&status=succeeded + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 02 Sep 2025 16:01:43 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1072' + Connection: + - close + Cf-Ray: + - 978e3a451c00a4fc-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"8cd7042fdca7259ed4ef7a4518bdf6ee" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - d53e9642-da84-46ef-875c-6144fd090fd0 + X-Runtime: + - '0.064205' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=%2B1u%2FnTIHzyJohdVDT2cSiuJNrBQxE3acdGpl7gOBsai8IUWqJ%2B2ZgXyEbbEy37QiN9OMvKCM6%2BkDEF7sdBZNcaWXlGzUcgefErVT0PqggHoMOb7D1jlrzGh5iYP5wl3F"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=59799&min_rtt=59513&rtt_var=22521&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1828&delivery_rate=71580&cwnd=45&unsent_bytes=0&cid=e3345cf16d5e8803&ts=392&x=0" + body: + encoding: UTF-8 + string: '[{"object":"transfer","id":"tr_329NGN1M4If6VvcMRALv4gjAQJx","amount":50000,"currency":"MXN","direction":"outbound","status":"succeeded","transaction_date":"2025-09-02T15:58:51Z","post_date":"2025-09-02T00:00:00Z","comment":"Test + payment","reference_id":"123456","tracking_key":"202509029073500000000000000006","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=qOK7ARfoEwlVpkLWUmFvihhtr5%2B4Gf1LA%2FnWvzdRnC8vLzAGeXauPtgz1bSaTV%2BmUP9oThn6%2B%2Bujt23nuyI3u8OR00eq5WezdAT1eG%2BhBvo%3D","mode":"test","counterparty":{"holder_id":"LFHU290523OG0","holder_name":"Jon + Snow","account_number":"735969000000203297","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"account_number":{"id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203297","created_at":"2025-08-29T20:02:00Z","updated_at":"2025-08-29T20:02:00Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"},"metadata":{},"return_reason":null}]' + recorded_at: Tue, 02 Sep 2025 16:01:43 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml new file mode 100644 index 0000000..99718b6 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/transfers + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 02 Sep 2025 15:59:14 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '2218' + Connection: + - close + Cf-Ray: + - 978e36a3cd4e0ba0-EZE + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"c44d2930120083fb1fcfe202e1d2c7c5" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 9f2d1b9e-ab81-412c-b2c8-46a21d943e44 + X-Runtime: + - '0.123093' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=dGWez9dCHhHPY3gIOowmzhSbR5PjvTHLFLD2kGv8rpM2Aopvl00MqFrSvjwLfv0b8SN7sBFSRqHfTLsqRSAr0RyMm2v3L1WRrkg3IJHXnMcfpFPH9msZJiFM8Ch1a3pC"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=31982&min_rtt=31972&rtt_var=12010&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1792&delivery_rate=132896&cwnd=253&unsent_bytes=0&cid=cc7a1dbdbfbf7d50&ts=384&x=0" + body: + encoding: UTF-8 + string: '[{"object":"transfer","id":"tr_329NGN1M4If6VvcMRALv4gjAQJx","amount":50000,"currency":"MXN","direction":"outbound","status":"succeeded","transaction_date":"2025-09-02T15:58:51Z","post_date":"2025-09-02T00:00:00Z","comment":"Test + payment","reference_id":"123456","tracking_key":"202509029073500000000000000006","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=qOK7ARfoEwlVpkLWUmFvihhtr5%2B4Gf1LA%2FnWvzdRnC8vLzAGeXauPtgz1bSaTV%2BmUP9oThn6%2B%2Bujt23nuyI3u8OR00eq5WezdAT1eG%2BhBvo%3D","mode":"test","counterparty":{"holder_id":"LFHU290523OG0","holder_name":"Jon + Snow","account_number":"735969000000203297","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"account_number":{"id":"acno_31yYL5uOb39vzvRIszPhoXYMjt4","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203297","created_at":"2025-08-29T20:02:00Z","updated_at":"2025-08-29T20:02:00Z","mode":"test","description":null,"metadata":{},"status":"enabled","is_root":true,"object":"account_number"},"metadata":{},"return_reason":null},{"object":"transfer","id":"tr_329N35lH813ZhFPBvHV8mXiKKMn","amount":10000000,"currency":"MXN","direction":"inbound","status":"succeeded","transaction_date":"2025-09-02T15:57:04Z","post_date":"2025-09-02T00:00:00Z","comment":null,"reference_id":"3","tracking_key":"202509029073500000000000000005","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=qOK7ARfoEwlVpkLWUmFvihhtr5%2B4Gf1LA%2FnWvzdRnC%2FtIlWZFTyXKH99y1SPS%2FioW%2FpFGGl1VboyxCzRY946tah7TfWJHw%2FM75NVz2XXmBZLqjqrQknbHmKnMMubiNsD","mode":"test","counterparty":{"holder_id":"VFHD640116IY8","holder_name":"Nakesha + Breitenberg","account_number":"680969192837645191","account_type":"clabe","institution":{"id":"90680","name":"CRISTOBAL + COLON","country":"mx"}},"account_number":{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:54:40Z","mode":"test","description":"Updated + account number description","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"},"metadata":{},"return_reason":null}]' + recorded_at: Tue, 02 Sep 2025 15:59:14 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml new file mode 100644 index 0000000..cd92f83 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/transfers/return + body: + encoding: UTF-8 + string: '{"transfer_id":"tr_329R3l5JksDkoevCGTOBsugCsnb"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Fintoc-Jws-Signature: + - eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiMGQ3MGY1MzFkNjk0MmM4MjQzMDdiZjZlZTIzZDMxNjYiLCJ0cyI6MTc1Njg1MTYyMywiY3JpdCI6WyJ0cyIsIm5vbmNlIl19.knGOuq89bDkhnw5JwAZ57rI6Sm405T3aXA8KazSTl_DsavvEeN9MY34athHc_JZt3rsf6daOR0ZK-RnaAwdtGP33XzxGqW8iDYaUo7UysiVN0gIJUl_cJazJWxG1joG8t58RWMxyfiE-VQHYER9c_XWB-t0E17ou8WmCMUgWndlGLQ7vgr03mjWXdDT5AYoZ1jXrXqTsgzy7cUAaA0j84H2aH9KFDnKZ17bKdg4NDnSJ-lalzicQeNgjzVzA0BZ-h5t07stsJiWU_EJwJDiqHOaUSSRiqDr-H2cQpppUJMQyGwBCkbtWR0G8PG39f7e97_yFmOcHkeUCHz8nlthdKg + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 02 Sep 2025 22:20:24 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1128' + Connection: + - close + Cf-Ray: + - 979064f7bbd7f21f-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"3954a8d24b91a22476a95d941ad9e2fa" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - c7cb38f4-7136-4d76-8837-0aa2b5aa0381 + X-Runtime: + - '0.363494' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=tVgzLymGEFhRDHaueKi3ZCGKvNSYWPxiAgv6Nbcgp0c152jGVD9oaeNNIyp575JdfHU1llo0RZ8eCm853iwMTMe9jb%2FHC3BGP73GbngYphHVz0KonnREFqeqyXX0qmJ8"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=59502&min_rtt=58986&rtt_var=22488&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=2410&delivery_rate=72220&cwnd=178&unsent_bytes=0&cid=5523d13563682b99&ts=692&x=0" + body: + encoding: UTF-8 + string: '{"object":"transfer","id":"tr_329R3l5JksDkoevCGTOBsugCsnb","amount":1000000,"currency":"MXN","direction":"inbound","status":"return_pending","transaction_date":"2025-09-02T16:30:04Z","post_date":"2025-09-02T00:00:00Z","comment":null,"reference_id":"6","tracking_key":"202509029073500000000000000009","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=qOK7ARfoEwlVpkLWUmFvihhtr5%2B4Gf1LA%2FnWvzdRnC8iI16fI6nvQ9dAhBV8NYBa7pR6J8gKo6rGoff3qWJ2xOPIDPJsG2njYPkZrzpG1waTgRRYwe2jS7Y4eikheYeK","mode":"test","counterparty":{"holder_id":"RBZZ190718TEA","holder_name":"Dede + Kuhlman","account_number":"110969987654321988","account_type":"clabe","institution":{"id":"40110","name":"JP + MORGAN","country":"mx"}},"account_number":{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:54:40Z","mode":"test","description":"Updated + account number description","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"},"metadata":{},"return_reason":null}' + recorded_at: Tue, 02 Sep 2025 22:20:24 GMT +recorded_with: VCR 6.3.1 From be966279bbf58d3abab50b0f9c114e3f19b1357c Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Tue, 2 Sep 2025 18:33:30 -0400 Subject: [PATCH 37/72] feature(transfers): Add refresh method to transfers --- lib/fintoc/transfers/resources/transfer.rb | 31 ++++++++++++++++++++++ spec/lib/fintoc/transfers/transfer_spec.rb | 17 ++++++++++++ 2 files changed, 48 insertions(+) diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb index 18a4a03..688b833 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -60,6 +60,11 @@ def to_s "#{direction_icon} #{amount_str} (#{@id}) - #{@status}" end + def refresh + fresh_transfer = @client.get_transfer(@id) + refresh_from_transfer(fresh_transfer) + end + def pending? @status == 'pending' end @@ -91,6 +96,32 @@ def inbound? def outbound? @direction == 'outbound' end + + private + + def refresh_from_transfer(transfer) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + raise 'Transfer must be the same instance' unless transfer.id == @id + + @object = transfer.object + @amount = transfer.amount + @currency = transfer.currency + @direction = transfer.direction + @status = transfer.status + @mode = transfer.mode + @post_date = transfer.post_date + @transaction_date = transfer.transaction_date + @comment = transfer.comment + @reference_id = transfer.reference_id + @receipt_url = transfer.receipt_url + @tracking_key = transfer.tracking_key + @return_reason = transfer.return_reason + @counterparty = transfer.counterparty + @account_number = transfer.account_number + @metadata = transfer.metadata + @created_at = transfer.created_at + + self + end end end end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb index ccffb6c..fc3cefe 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -212,4 +212,21 @@ end end end + + describe '#refresh' do + let(:refreshed_data) { data.merge(status: 'succeeded') } + let(:refreshed_transfer) { described_class.new(**refreshed_data) } + + before do + allow(client).to receive(:get_transfer).with(data[:id]).and_return(refreshed_transfer) + end + + it 'refreshes the transfer data' do + expect { transfer.refresh }.to change { transfer.status }.from('pending').to('succeeded') + end + + it 'returns self' do + expect(transfer.refresh).to eq(transfer) + end + end end From 804379321581f38a7987f434c5f2aa9193ecef59 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Tue, 2 Sep 2025 18:33:55 -0400 Subject: [PATCH 38/72] feature(transfer): Add return_transfer method to transfer --- lib/fintoc/transfers/resources/transfer.rb | 5 +++++ spec/lib/fintoc/transfers/transfer_spec.rb | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb index 688b833..5274192 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -65,6 +65,11 @@ def refresh refresh_from_transfer(fresh_transfer) end + def return_transfer + returned_transfer = @client.return_transfer(@id) + refresh_from_transfer(returned_transfer) + end + def pending? @status == 'pending' end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb index fc3cefe..8abe098 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -229,4 +229,22 @@ expect(transfer.refresh).to eq(transfer) end end + + describe '#return_transfer' do + let(:returned_data) { data.merge(status: 'return_pending') } + let(:returned_transfer) { described_class.new(**returned_data) } + + before do + allow(client).to receive(:return_transfer).with(data[:id]).and_return(returned_transfer) + end + + it 'returns the transfer and updates status' do + expect { transfer.return_transfer } + .to change { transfer.status }.from('pending').to('return_pending') + end + + it 'returns self' do + expect(transfer.return_transfer).to eq(transfer) + end + end end From 3cc388f61a2726a1110bdb305f1825ed79cdb0b1 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 10:54:02 -0400 Subject: [PATCH 39/72] refactor(client/jws): Move jws condition to separate method --- lib/fintoc/base_client.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index 9216961..ebe01f5 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -85,6 +85,10 @@ def build_url(resource, version: :v1) "#{Fintoc::Constants::SCHEME}#{base_url}#{resource}" end + def should_use_jws?(method, use_jws) + use_jws && @jws && %w[post patch put].include?(method.downcase) + end + def make_request(method, resource, parameters, version: :v1, use_jws: false) # this is to handle url returned in the link headers # I'm sure there is a better and more clever way to solve this @@ -94,7 +98,7 @@ def make_request(method, resource, parameters, version: :v1, use_jws: false) url = build_url(resource, version:) - if use_jws && @jws && %w[post patch put].include?(method.downcase) + if should_use_jws?(method, use_jws) request_body = parameters[:json]&.to_json || '' jws_signature = @jws.generate_signature(request_body) From 908a6521ca1bbc42be41736b4babe3c9884b0c89 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 15:53:06 -0400 Subject: [PATCH 40/72] feature(simulate): Implement simulate methods --- lib/fintoc/transfers/client/client.rb | 2 + .../transfers/client/simulation_methods.rb | 23 ++++++ spec/lib/fintoc/transfers/client_spec.rb | 2 + .../transfers/simulation_methods_spec.rb | 37 +++++++++ .../clients/simulation_client_examples.rb | 23 ++++++ ...a_transfer_and_returns_Transfer_object.yml | 78 +++++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 lib/fintoc/transfers/client/simulation_methods.rb create mode 100644 spec/lib/fintoc/transfers/simulation_methods_spec.rb create mode 100644 spec/support/shared_examples/clients/simulation_client_examples.rb create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index 6c945ba..03ac06c 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -3,6 +3,7 @@ require 'fintoc/transfers/client/accounts_methods' require 'fintoc/transfers/client/account_numbers_methods' require 'fintoc/transfers/client/transfers_methods' +require 'fintoc/transfers/client/simulation_methods' module Fintoc module Transfers @@ -11,6 +12,7 @@ class Client < BaseClient include AccountsMethods include AccountNumbersMethods include TransfersMethods + include SimulationMethods end end end diff --git a/lib/fintoc/transfers/client/simulation_methods.rb b/lib/fintoc/transfers/client/simulation_methods.rb new file mode 100644 index 0000000..58d7b90 --- /dev/null +++ b/lib/fintoc/transfers/client/simulation_methods.rb @@ -0,0 +1,23 @@ +require 'fintoc/transfers/resources/transfer' + +module Fintoc + module Transfers + module SimulationMethods + def simulate_receive_transfer(account_number_id:, amount:, currency:) + data = _simulate_receive_transfer(account_number_id:, amount:, currency:) + build_transfer(data) + end + + private + + def _simulate_receive_transfer(account_number_id:, amount:, currency:) + post(version: :v2) + .call('simulate/receive_transfer', account_number_id:, amount:, currency:) + end + + def build_transfer(data) + Fintoc::Transfers::Transfer.new(**data, client: self) + end + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index cc6c026..b0bbac4 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -26,4 +26,6 @@ it_behaves_like 'a client with account numbers methods' it_behaves_like 'a client with transfers methods' + + it_behaves_like 'a client with simulation methods' end diff --git a/spec/lib/fintoc/transfers/simulation_methods_spec.rb b/spec/lib/fintoc/transfers/simulation_methods_spec.rb new file mode 100644 index 0000000..91094d6 --- /dev/null +++ b/spec/lib/fintoc/transfers/simulation_methods_spec.rb @@ -0,0 +1,37 @@ +require 'fintoc/transfers/client/simulation_methods' + +RSpec.describe Fintoc::Transfers::SimulationMethods do + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Transfers::SimulationMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def post(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + end + end + + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + + describe '#simulate_receive_transfer' do + before do + allow(client).to receive(:build_transfer) + end + + it 'calls build_transfer with the response' do + client.simulate_receive_transfer( + account_number_id: 'acno_123', + amount: 10000, + currency: 'MXN' + ) + expect(client).to have_received(:build_transfer).with({ mock: 'response' }) + end + end +end diff --git a/spec/support/shared_examples/clients/simulation_client_examples.rb b/spec/support/shared_examples/clients/simulation_client_examples.rb new file mode 100644 index 0000000..29aacad --- /dev/null +++ b/spec/support/shared_examples/clients/simulation_client_examples.rb @@ -0,0 +1,23 @@ +RSpec.shared_examples 'a client with simulation methods' do + describe '#simulate_receive_transfer' do + let(:simulate_transfer_data) do + { + account_number_id: 'acno_326dzRGqxLee3j9TkaBBBMfs2i0', + amount: 10000, + currency: 'MXN' + } + end + + it 'simulates receiving a transfer and returns Transfer object', :vcr do + transfer = client.simulate_receive_transfer(**simulate_transfer_data) + + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + amount: simulate_transfer_data[:amount], + currency: simulate_transfer_data[:currency], + account_number: include(id: simulate_transfer_data[:account_number_id]) + ) + end + end +end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml new file mode 100644 index 0000000..2610ccc --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml @@ -0,0 +1,78 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/simulate/receive_transfer + body: + encoding: UTF-8 + string: '{"account_number_id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","amount":10000,"currency":"MXN"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 201 + message: Created + headers: + Date: + - Wed, 03 Sep 2025 19:42:17 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '924' + Connection: + - close + Cf-Ray: + - 9797baba0e1cf1dd-GRU + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"9b8507bbddf62fbbc39dc0f60d4b934b" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 86ee4f18-ab70-4f72-8c7e-13fbfdb54c82 + X-Runtime: + - '0.805720' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=zweUJRHwMtzbEa6DKV8VCWV%2F%2FBNzngx9kLr%2FuhozNtY28G8rqA3vSZ8sgAoLJHSfU1mWsNinNDoRu%2Fe7eLWa3n2Ayzg%2FfZRqtlyNiah3gRfe8jXhcn6PXoevXNtc%2F0p5"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=59037&min_rtt=58831&rtt_var=22475&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=1964&delivery_rate=70429&cwnd=253&unsent_bytes=0&cid=a8dd37502f9ccbd5&ts=1128&x=0" + body: + encoding: UTF-8 + string: '{"object":"transfer","id":"tr_32CdYk4p58orHAqG8E3f5yrEjeo","amount":10000,"currency":"MXN","direction":"inbound","status":"pending","transaction_date":"2025-09-03T19:42:17Z","post_date":"2025-09-03T00:00:00Z","comment":null,"reference_id":"3","tracking_key":"202509039073500000000000000012","receipt_url":null,"mode":"test","counterparty":{"holder_id":"PPPR510220DB1","holder_name":"Adrianne + Murray","account_number":"631969456789123456","account_type":"clabe","institution":{"id":"90631","name":"CI + BOLSA","country":"mx"}},"account_number":{"id":"acno_326dzRGqxLee3j9TkaBBBMfs2i0","account_id":"acc_31yYL7h9LVPg121AgFtCyJPDsgM","number":"735969000000203365","created_at":"2025-09-01T16:46:57Z","updated_at":"2025-09-01T16:54:40Z","mode":"test","description":"Updated + account number description","metadata":{"test_id":"12345"},"status":"enabled","is_root":false,"object":"account_number"},"metadata":{},"return_reason":null}' + recorded_at: Wed, 03 Sep 2025 19:42:17 GMT +recorded_with: VCR 6.3.1 From 6870fddbfa1c1a997f5a15d240a8b51c2fcaef3d Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 15:56:49 -0400 Subject: [PATCH 41/72] feature(account/simulate): Use simulate_receive_transfer as isntance method for Account --- lib/fintoc/transfers/resources/account.rb | 8 ++++++++ spec/lib/fintoc/transfers/account_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index b32d652..6946098 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -67,6 +67,14 @@ def closed? @status == 'closed' end + def simulate_receive_transfer(amount:) + @client.simulate_receive_transfer( + account_number_id: @root_account_number_id, + amount:, + currency: @currency + ) + end + private def refresh_from_account(account) diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index 9e134cc..d0424ab 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -157,4 +157,24 @@ .with('acc_123', description: 'Test description') end end + + describe '#simulate_receive_transfer' do + let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } + + before do + allow(client) + .to receive(:simulate_receive_transfer) + .with( + account_number_id: account.root_account_number_id, + amount: 10000, + currency: account.currency + ) + .and_return(expected_transfer) + end + + it 'simulates receiving a transfer using account defaults' do + result = account.simulate_receive_transfer(amount: 10000) + expect(result).to eq(expected_transfer) + end + end end From 37a92f7c48651fb0e2ebdc06e6a3b8a6f85ea0b3 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 15:58:07 -0400 Subject: [PATCH 42/72] feature(account-number/simulate): Use simulate_receive_transfer as isntance method for AccountNumber --- lib/fintoc/transfers/resources/account_number.rb | 8 ++++++++ spec/lib/fintoc/transfers/account_number_spec.rb | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 4eb9d37..0e11d26 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -68,6 +68,14 @@ def root? @is_root end + def simulate_receive_transfer(amount:, currency: 'MXN') + @client.simulate_receive_transfer( + account_number_id: @id, + amount:, + currency: + ) + end + private def refresh_from_account_number(account_number) diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index 7da62fb..ffd6db8 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -162,4 +162,20 @@ ) end end + + describe '#simulate_receive_transfer' do + let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } + + before do + allow(client) + .to receive(:simulate_receive_transfer) + .with(account_number_id: account_number.id, amount: 10000, currency: 'MXN') + .and_return(expected_transfer) + end + + it 'simulates receiving a transfer using account number id' do + result = account_number.simulate_receive_transfer(amount: 10000) + expect(result).to eq(expected_transfer) + end + end end From 5342981a1a01d6b913cd000f46d4f2d7e94ed1ca Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 16:07:38 -0400 Subject: [PATCH 43/72] feature(simulate): Only allow simulating inbound transfers on test mode --- lib/fintoc/transfers/resources/account.rb | 8 +++ .../transfers/resources/account_number.rb | 8 +++ .../fintoc/transfers/account_number_spec.rb | 41 +++++++++++--- spec/lib/fintoc/transfers/account_spec.rb | 53 ++++++++++++++----- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 6946098..135beb8 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -67,7 +67,15 @@ def closed? @status == 'closed' end + def test_mode? + @mode == 'test' + end + def simulate_receive_transfer(amount:) + unless test_mode? + raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode' + end + @client.simulate_receive_transfer( account_number_id: @root_account_number_id, amount:, diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 0e11d26..4663b57 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -68,7 +68,15 @@ def root? @is_root end + def test_mode? + @mode == 'test' + end + def simulate_receive_transfer(amount:, currency: 'MXN') + unless test_mode? + raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode' + end + @client.simulate_receive_transfer( account_number_id: @id, amount:, diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index ffd6db8..cb8b960 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -163,19 +163,44 @@ end end + describe '#test_mode?' do + it 'returns true when mode is test' do + expect(account_number.test_mode?).to be true + end + + it 'returns false when mode is not test' do + live_account_number = described_class.new(**data, mode: 'live') + expect(live_account_number.test_mode?).to be false + end + end + describe '#simulate_receive_transfer' do let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } - before do - allow(client) - .to receive(:simulate_receive_transfer) - .with(account_number_id: account_number.id, amount: 10000, currency: 'MXN') - .and_return(expected_transfer) + context 'when in test mode' do + before do + allow(client) + .to receive(:simulate_receive_transfer) + .with(account_number_id: account_number.id, amount: 10000, currency: 'MXN') + .and_return(expected_transfer) + end + + it 'simulates receiving a transfer using account number id' do + result = account_number.simulate_receive_transfer(amount: 10000) + expect(result).to eq(expected_transfer) + end end - it 'simulates receiving a transfer using account number id' do - result = account_number.simulate_receive_transfer(amount: 10000) - expect(result).to eq(expected_transfer) + context 'when not in test mode' do + let(:live_account_number) { described_class.new(**data, mode: 'live', client: client) } + + it 'raises an error' do + expect { live_account_number.simulate_receive_transfer(amount: 10000) } + .to raise_error( + Fintoc::Errors::InvalidRequestError, + /Simulation is only available in test mode/ + ) + end end end end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index d0424ab..d72346f 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -158,23 +158,48 @@ end end + describe '#test_mode?' do + it 'returns true when mode is test' do + expect(account.test_mode?).to be true + end + + it 'returns false when mode is not test' do + live_account = described_class.new(**data, mode: 'live') + expect(live_account.test_mode?).to be false + end + end + describe '#simulate_receive_transfer' do let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } - before do - allow(client) - .to receive(:simulate_receive_transfer) - .with( - account_number_id: account.root_account_number_id, - amount: 10000, - currency: account.currency - ) - .and_return(expected_transfer) - end - - it 'simulates receiving a transfer using account defaults' do - result = account.simulate_receive_transfer(amount: 10000) - expect(result).to eq(expected_transfer) + context 'when in test mode' do + before do + allow(client) + .to receive(:simulate_receive_transfer) + .with( + account_number_id: account.root_account_number_id, + amount: 10000, + currency: account.currency + ) + .and_return(expected_transfer) + end + + it 'simulates receiving a transfer using account currency' do + result = account.simulate_receive_transfer(amount: 10000) + expect(result).to eq(expected_transfer) + end + end + + context 'when not in test mode' do + let(:live_account) { described_class.new(**data, mode: 'live', client: client) } + + it 'raises an error' do + expect { live_account.simulate_receive_transfer(amount: 10000) } + .to raise_error( + Fintoc::Errors::InvalidRequestError, + /Simulation is only available in test mode/ + ) + end end end end From 3d4824b9ef90dee03901757a46bd0dde4c041f1c Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 18:39:54 -0400 Subject: [PATCH 44/72] refactor(transfers-methods/spec): Better mocks for unit tests of methods --- .../transfers/account_numbers_methods_spec.rb | 23 +++++++++++-------- .../transfers/simulation_methods_spec.rb | 5 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb b/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb index c64fead..a297712 100644 --- a/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb +++ b/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb @@ -30,23 +30,25 @@ def patch(*, **) describe '#create_account_number' do before do - allow(client).to receive(:build_account_number) + allow(Fintoc::Transfers::AccountNumber).to receive(:new) end it 'calls build_account_number with the response' do client.create_account_number(account_id: 'acc_123') - expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(mock: 'response', client:) end end describe '#get_account_number' do before do - allow(client).to receive(:build_account_number) + allow(Fintoc::Transfers::AccountNumber).to receive(:new) end it 'calls build_account_number with the response' do client.get_account_number('acno_123') - expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(mock: 'response', client:) end end @@ -55,24 +57,27 @@ def patch(*, **) allow(client) .to receive(:_list_account_numbers) .and_return([{ mock: 'response1' }, { mock: 'response2' }]) - allow(client).to receive(:build_account_number) + allow(Fintoc::Transfers::AccountNumber).to receive(:new) end it 'calls build_account_number for each response' do client.list_account_numbers - expect(client).to have_received(:build_account_number).with({ mock: 'response1' }) - expect(client).to have_received(:build_account_number).with({ mock: 'response2' }) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(mock: 'response1', client:) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(mock: 'response2', client:) end end describe '#update_account_number' do before do - allow(client).to receive(:build_account_number) + allow(Fintoc::Transfers::AccountNumber).to receive(:new) end it 'calls build_account_number with the response' do client.update_account_number('acno_123', description: 'Updated') - expect(client).to have_received(:build_account_number).with({ mock: 'response' }) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(mock: 'response', client:) end end end diff --git a/spec/lib/fintoc/transfers/simulation_methods_spec.rb b/spec/lib/fintoc/transfers/simulation_methods_spec.rb index 91094d6..5aa4068 100644 --- a/spec/lib/fintoc/transfers/simulation_methods_spec.rb +++ b/spec/lib/fintoc/transfers/simulation_methods_spec.rb @@ -22,7 +22,7 @@ def post(*, **) describe '#simulate_receive_transfer' do before do - allow(client).to receive(:build_transfer) + allow(Fintoc::Transfers::Transfer).to receive(:new) end it 'calls build_transfer with the response' do @@ -31,7 +31,8 @@ def post(*, **) amount: 10000, currency: 'MXN' ) - expect(client).to have_received(:build_transfer).with({ mock: 'response' }) + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(mock: 'response', client:) end end end From fd1e5b45be5003fc601c5a8052d73333cbe33ae8 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Wed, 3 Sep 2025 19:27:37 -0400 Subject: [PATCH 45/72] feature(account-verifications): Add AccountVerification resource --- .../resources/account_verification.rb | 53 +++++++++ .../transfers/account_verification_spec.rb | 112 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 lib/fintoc/transfers/resources/account_verification.rb create mode 100644 spec/lib/fintoc/transfers/account_verification_spec.rb diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/transfers/resources/account_verification.rb new file mode 100644 index 0000000..f6386b8 --- /dev/null +++ b/lib/fintoc/transfers/resources/account_verification.rb @@ -0,0 +1,53 @@ +require 'fintoc/utils' + +module Fintoc + module Transfers + class AccountVerification + include Utils + + attr_reader :id, :object, :status, :reason, :transfer_id, :counterparty, :mode, :receipt_url, + :transaction_date + + def initialize( + id:, + object:, + status:, + reason:, + transfer_id:, + counterparty:, + mode:, + receipt_url:, + transaction_date:, + client: nil, + ** + ) + @id = id + @object = object + @status = status + @reason = reason + @transfer_id = transfer_id + @counterparty = counterparty + @mode = mode + @receipt_url = receipt_url + @transaction_date = transaction_date + @client = client + end + + def to_s + "πŸ” Account Verification (#{@id}) - #{@status}" + end + + def pending? + @status == 'pending' + end + + def succeeded? + @status == 'succeeded' + end + + def failed? + @status == 'failed' + end + end + end +end diff --git a/spec/lib/fintoc/transfers/account_verification_spec.rb b/spec/lib/fintoc/transfers/account_verification_spec.rb new file mode 100644 index 0000000..59aca64 --- /dev/null +++ b/spec/lib/fintoc/transfers/account_verification_spec.rb @@ -0,0 +1,112 @@ +require 'fintoc/transfers/resources/account_verification' + +RSpec.describe Fintoc::Transfers::AccountVerification do + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } + let(:client) { Fintoc::Transfers::Client.new(api_key) } + + let(:counterparty_data) do + { + account_number: '123456789123455789', + holder_id: 'FLA1234567890', + holder_name: 'Carmen Marcela', + account_type: 'clabe', + institution: { + id: 'mx_banco_bbva', + name: 'BBVA Mexico', + country: 'mx' + } + } + end + + let(:data) do + { + object: 'account_verification', + id: 'accv_fdme30s11j5k7l1mekq4', + status: 'succeeded', + reason: nil, + transfer_id: 'tr_fdskjldasjkl', + counterparty: counterparty_data, + mode: 'test', + receipt_url: 'https://www.banxico.org.mx/cep/', + transaction_date: '2020-04-17T00:00:00.000Z', + client: client + } + end + + let(:account_verification) { described_class.new(**data) } + + describe '#new' do + it 'creates an instance of AccountVerification' do + expect(account_verification).to be_an_instance_of(described_class) + end + + it 'sets all attributes correctly' do # rubocop:disable RSpec/ExampleLength + expect(account_verification).to have_attributes( + object: 'account_verification', + id: 'accv_fdme30s11j5k7l1mekq4', + status: 'succeeded', + reason: nil, + transfer_id: 'tr_fdskjldasjkl', + counterparty: counterparty_data, + mode: 'test', + receipt_url: 'https://www.banxico.org.mx/cep/', + transaction_date: '2020-04-17T00:00:00.000Z' + ) + end + end + + describe '#to_s' do + it 'returns a string representation' do + expect(account_verification.to_s) + .to eq('πŸ” Account Verification (accv_fdme30s11j5k7l1mekq4) - succeeded') + end + end + + describe 'status methods' do + context 'when status is pending' do + let(:pending_verification) { described_class.new(**data, status: 'pending') } + + it '#pending? returns true' do + expect(pending_verification.pending?).to be true + end + + it '#succeeded? returns false' do + expect(pending_verification.succeeded?).to be false + end + + it '#failed? returns false' do + expect(pending_verification.failed?).to be false + end + end + + context 'when status is succeeded' do + it '#pending? returns false' do + expect(account_verification.pending?).to be false + end + + it '#succeeded? returns true' do + expect(account_verification.succeeded?).to be true + end + + it '#failed? returns false' do + expect(account_verification.failed?).to be false + end + end + + context 'when status is failed' do + let(:failed_verification) { described_class.new(**data, status: 'failed') } + + it '#pending? returns false' do + expect(failed_verification.pending?).to be false + end + + it '#succeeded? returns false' do + expect(failed_verification.succeeded?).to be false + end + + it '#failed? returns true' do + expect(failed_verification.failed?).to be true + end + end + end +end From ab0947458509f334bfc4aa838a640e68682ad8c5 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:10:26 -0400 Subject: [PATCH 46/72] feature(account-verifications): Add account verification methods --- .../client/account_verifications_methods.rb | 39 ++++++++ lib/fintoc/transfers/client/client.rb | 2 + .../account_verifications_methods_spec.rb | 67 ++++++++++++++ spec/lib/fintoc/transfers/client_spec.rb | 2 + .../account_verifications_client_examples.rb | 88 +++++++++++++++++++ ...eturns_an_AccountVerification_instance.yml | 77 ++++++++++++++++ ...eturns_an_AccountVerification_instance.yml | 74 ++++++++++++++++ .../accepts_filtering_parameters.yml | 76 ++++++++++++++++ ...array_of_AccountVerification_instances.yml | 76 ++++++++++++++++ 9 files changed, 501 insertions(+) create mode 100644 lib/fintoc/transfers/client/account_verifications_methods.rb create mode 100644 spec/lib/fintoc/transfers/account_verifications_methods_spec.rb create mode 100644 spec/support/shared_examples/clients/account_verifications_client_examples.rb create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml create mode 100644 spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml diff --git a/lib/fintoc/transfers/client/account_verifications_methods.rb b/lib/fintoc/transfers/client/account_verifications_methods.rb new file mode 100644 index 0000000..3f43e5b --- /dev/null +++ b/lib/fintoc/transfers/client/account_verifications_methods.rb @@ -0,0 +1,39 @@ +require 'fintoc/transfers/resources/account_verification' + +module Fintoc + module Transfers + module AccountVerificationsMethods + def create_account_verification(account_number:) + data = _create_account_verification(account_number:) + build_account_verification(data) + end + + def get_account_verification(account_verification_id) + data = _get_account_verification(account_verification_id) + build_account_verification(data) + end + + def list_account_verifications(**params) + _list_account_verifications(**params).map { |data| build_account_verification(data) } + end + + private + + def _create_account_verification(account_number:) + post(version: :v2, use_jws: true).call('account_verifications', account_number:) + end + + def _get_account_verification(account_verification_id) + get(version: :v2).call("account_verifications/#{account_verification_id}") + end + + def _list_account_verifications(**params) + get(version: :v2).call('account_verifications', **params) + end + + def build_account_verification(data) + Fintoc::Transfers::AccountVerification.new(**data, client: self) + end + end + end +end diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index 03ac06c..2f0c817 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -4,6 +4,7 @@ require 'fintoc/transfers/client/account_numbers_methods' require 'fintoc/transfers/client/transfers_methods' require 'fintoc/transfers/client/simulation_methods' +require 'fintoc/transfers/client/account_verifications_methods' module Fintoc module Transfers @@ -13,6 +14,7 @@ class Client < BaseClient include AccountNumbersMethods include TransfersMethods include SimulationMethods + include AccountVerificationsMethods end end end diff --git a/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb b/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb new file mode 100644 index 0000000..5b172a6 --- /dev/null +++ b/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb @@ -0,0 +1,67 @@ +require 'fintoc/transfers/client/account_verifications_methods' + +RSpec.describe Fintoc::Transfers::AccountVerificationsMethods do + let(:client) { test_class.new(api_key) } + + let(:test_class) do + Class.new do + include Fintoc::Transfers::AccountVerificationsMethods + + def initialize(api_key) + @api_key = api_key + end + + # Mock the base client methods needed for testing + def get(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + + def post(*, **) + proc { |_resource, **_kwargs| { mock: 'response' } } + end + end + end + + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + + describe '#create_account_verification' do + before do + allow(Fintoc::Transfers::AccountVerification).to receive(:new) + end + + it 'calls build_account_verification with the response' do + client.create_account_verification(account_number: '735969000000203226') + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(mock: 'response', client:) + end + end + + describe '#get_account_verification' do + before do + allow(Fintoc::Transfers::AccountVerification).to receive(:new) + end + + it 'calls build_account_verification with the response' do + client.get_account_verification('accv_123') + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(mock: 'response', client:) + end + end + + describe '#list_account_verifications' do + before do + allow(client) + .to receive(:_list_account_verifications) + .and_return([{ mock: 'response1' }, { mock: 'response2' }]) + allow(Fintoc::Transfers::AccountVerification).to receive(:new) + end + + it 'calls build_account_verification for each response item' do + client.list_account_verifications + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(mock: 'response1', client:) + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(mock: 'response2', client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index b0bbac4..44775ad 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -28,4 +28,6 @@ it_behaves_like 'a client with transfers methods' it_behaves_like 'a client with simulation methods' + + it_behaves_like 'a client with account verifications methods' end diff --git a/spec/support/shared_examples/clients/account_verifications_client_examples.rb b/spec/support/shared_examples/clients/account_verifications_client_examples.rb new file mode 100644 index 0000000..186ef7d --- /dev/null +++ b/spec/support/shared_examples/clients/account_verifications_client_examples.rb @@ -0,0 +1,88 @@ +require 'openssl' + +RSpec.shared_examples 'a client with account verifications methods' do + let(:account_verification_id) { 'accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw' } + let(:account_number) { '735969000000203226' } + let(:jws_private_key) do + key_string = "-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNLwwQr/uFToDH +8x1GHlW5gRsngNp8J+sg8vZc5jX+dISZh42CNM2eMSyPOMLSZL08xIA9ISoxjCJb +rpJ7MY9OKOZdrFheclz8e3z/hVcXpmgT0JARYIyKUb2sgoh1JsH3aSQILTc4KuDM +dm0+WIWl9tOqKXm23j9RcYL+WOUCcLYNj3xga39CnXdqT3dy2fMIOJ+vZxfSxPhG +EBTyV6v9jrMbRukhxllTqDc64WkVdt0MOvzFzcSNkGcvHRdrK1w+x5IhfsYGtv+9 +mz+fvmI88qGsb0x8peGVDgWfQjykxrB/8umpQKANn9bqjyay+ogRRwv05dmaB/gm +IycvGXE7AgMBAAECggEAI4gTKcyf3MzkZjvGhP75z175KdUZgMiU4ibQ3POMxBy/ +XaroqXSlatCPK9ojerWxQ5Wvs2ZL3TqsNH49pZHGhD127x/KSci6K4ri8YjQtSq+ ++Tdzy16R194h337XTJpCmqqdb8EMv/BE74NOla5UrpHYw63dAvrnsh3bFlqkhdBZ +E5OBfdLyxGy5FYdewV803a8XGfnDfT7RrsdWhPib8E3i+wix+/dv10AX/+Y6VPpG +2EPXRV63UtmO2EVXyIGT5kSAnzZBJPIB3EYTlm1A86PxQGVD4X8LAUXIj6VRVC8h +B1KXb5YZ9W1vYmKyWUZPyMQHMpUTNGEuU/EtN0aOCQKBgQD+zMd1+3BhoSwBusXb +EK2SBJwn9TfqdUghsFHNz0xjvpAFKpO55qA7XyilZwZeJsOzPQZ33ERCRk18crCd +Q6oWI15xKjPl+Dfxf4UYjokx/iQCCHu8lJ6TXcEwXniIs6CVsUq9QV+s6JBlb3C4 +qD/wwp7VrmbcMLfIUs3nb3tqHQKBgQDOJnGylmqC/l4BCZj9BhLiVg7nioY24lG1 +9DY0/nnnbuMtDQ+8VUKtt93Or8giejOVqj3BZ8/TflkwCQAt9cIvFG50aTVZBo2E +4uPJLGSBLrQqpuUNPR2O239o4RqGgDICkh9TRH9D9GsekLRCgefLRubUyuZZ4pI9 +j1ty2kMpNwKBgGtYJmgEKBJZbkrEPvrNifJMUuVan9X81wiqWaxVOx+CdvZWO6pE +CRk6O8uDHeGofyYR/ZmdiHxLVfWp89ItYYi2GeGfIAIwkpEBYjc4RYB0SwM4Q7js +++mlw+/2vN0KoAqwiIY29nHIAJ1bV6fT6iwqMfRf5yG4vJR+nhR0mQ/ZAoGAfgLJ +5RxEpyXNWF0Bg0i/KlLocWgfelUFFW/d4q7a3TjO7K7bO4fyZjXKA5k3gLup5IZX +kW1fgCvvYIlf7rgWpqiai9XzoiN7RgtaqZHVLZHa12eFA36kHrrVOsq+aBDcgO3I +8CEimetBv0E8rpqxkXQZjWEpRTBVrAOBJsd73ikCgYAwf4fnpTcEa4g6ejmbjkXw +yacTlKFuxyLZHZ5R7D0+Fj19gwm9EzvrRIEcX84ebiJ8P1bL3kycQmLV19zE5B3M +pcsQmZ28/Oa3xCPiy8CDyDuiDbbNfnR1Ot3IbgfFL7xPYySljJbMyl7vhKJIacWs +draAAQ5iJEb5BR8AmL6tAQ== +-----END PRIVATE KEY-----" + OpenSSL::PKey::RSA.new(key_string) + end + + it 'responds to account verification-specific methods' do + expect(client) + .to respond_to(:create_account_verification) + .and respond_to(:get_account_verification) + .and respond_to(:list_account_verifications) + end + + describe '#create_account_verification' do + it 'returns an AccountVerification instance', :vcr do + account_verification = client.create_account_verification(account_number:) + + expect(account_verification) + .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .and have_attributes( + object: 'account_verification', + status: 'pending' + ) + end + end + + describe '#get_account_verification' do + it 'returns an AccountVerification instance', :vcr do + account_verification = client.get_account_verification(account_verification_id) + + expect(account_verification) + .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .and have_attributes( + id: account_verification_id, + object: 'account_verification' + ) + end + end + + describe '#list_account_verifications' do + it 'returns an array of AccountVerification instances', :vcr do + account_verifications = client.list_account_verifications + + expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + expect(account_verifications.size).to be >= 1 + end + + it 'accepts filtering parameters', :vcr do + account_verifications = client.list_account_verifications( + since: '2020-01-01T00:00:00.000Z', + limit: 10 + ) + + expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + end + end +end diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml new file mode 100644 index 0000000..22e08c8 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.fintoc.com/v2/account_verifications + body: + encoding: UTF-8 + string: '{"account_number":"735969000000203226"}' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Fintoc-Jws-Signature: + - eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiNDk3OTQ0MWIxNzg0Mzg4OTlkYzA5MzNhYTcxZjVmZmUiLCJ0cyI6MTc1NzAwMTk1NywiY3JpdCI6WyJ0cyIsIm5vbmNlIl19.VqYcVdHBcQiYaXZFtNKqS5giFTQDW6syn1VHj64ZzEY59Qk4JCT2G8ne0KwqVtNZv6FtUbeCzzfuucR9pGlaVM-iYJiBhy-HOjI9VUDUA_GYvWbs3M_kqowBr3hi5yVWV1Xb6FlFZs_zoIdKWPGYXNPqHEHXT7Qv8LFBZNYgc5r9jjzQy38gNVktmcKFcXTcVRoNzrzRe5VjmPFzLNJ0B8_j_kyvJJdCckQ79UIYKjA9Ocmu6nN1jmiG4SdZkG69cAouXDwZmGSnrQtsWSkQOnU4Mke_m4buVzOGD2zrzmCzu4rY9LmkwbgOxJKO825GadgZWXq4LJPINXr-Eo7Wzw + Connection: + - close + Content-Type: + - application/json; charset=utf-8 + Host: + - api.fintoc.com + response: + status: + code: 201 + message: Created + headers: + Date: + - Thu, 04 Sep 2025 16:05:59 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '402' + Connection: + - close + Cf-Ray: + - 979ebb409a8ea0c8-CPH + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"1658fee2632f17fae8461056906cb5b5" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 524eefcc-c549-49f1-a580-7c40ede80cc7 + X-Runtime: + - '0.437973' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=qfNKs9IPvaEYbQp%2FW973gSQIfZ4FWbQCND8e7nVZDSqeVVzFVlZ8xu2xcS3ClzIZ63J%2FsqM6okeJCgeI6TWwF33xZrrh6cX98RnV5UsKIWcevebo%2BbYvGvvRmzwiTBpQ"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=337737&min_rtt=298128&rtt_var=140090&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3879&recv_bytes=2406&delivery_rate=14289&cwnd=33&unsent_bytes=0&cid=59bf549ecb9beb68&ts=1299&x=0" + body: + encoding: UTF-8 + string: '{"object":"account_verification","id":"accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw","status":"pending","reason":null,"transfer_id":"tr_32F2NNCg5s9JQy5AX5A6toWX9rd","counterparty":{"holder_id":null,"holder_name":null,"account_number":"735969000000203226","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"mode":"test","receipt_url":null,"transaction_date":"2025-09-04T16:05:58Z"}' + recorded_at: Thu, 04 Sep 2025 16:05:59 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml new file mode 100644 index 0000000..4d90e82 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/account_verifications/accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 04 Sep 2025 16:06:50 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '603' + Connection: + - close + Cf-Ray: + - 979ebc832f69c9be-CPH + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"912e11cf6908023f112c73707dbb82cb" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 3f90fdd2-c468-4a93-bb74-763aee816c05 + X-Runtime: + - '0.216110' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=zqdiTM9nU8Qd6m59OzNrTLbjhQMmXzGe0Way81DobYWZZwTSUrKSBDOHAfPmrZx%2BDi4c%2F1jW9bEgvG26J2bxCDro%2FszTccBpILKFysVXlDV2BU1PPe9I8POu2YsanUm4"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=262243&min_rtt=249577&rtt_var=118924&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3880&recv_bytes=1837&delivery_rate=12139&cwnd=33&unsent_bytes=0&cid=28465d7eaadf9d83&ts=1103&x=0" + body: + encoding: UTF-8 + string: '{"object":"account_verification","id":"accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw","status":"succeeded","reason":null,"transfer_id":"tr_32F2NNCg5s9JQy5AX5A6toWX9rd","counterparty":{"holder_id":"PPPR510220DB1","holder_name":"Adrianne + Murray","account_number":"735969000000203226","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"mode":"test","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=ce2zbPihXLg1MobUPCRrTrw9bB97KEYDGHg6rSGK7PaklMO9uV2ancSh1z2p8bIQCcwNCndMOBQx6ISkSOLTcLQ44r6s3qO7aVSNcWSiOp4%3D","transaction_date":"2025-09-04T16:05:58Z"}' + recorded_at: Thu, 04 Sep 2025 16:06:50 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml new file mode 100644 index 0000000..33286f8 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/account_verifications?limit=10&since=2020-01-01T00:00:00.000Z + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 04 Sep 2025 16:06:54 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '605' + Connection: + - close + Cf-Ray: + - 979ebc9b5dbfa94d-SYD + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"9b52bfc2b4c200446ac3f7bc672c6bd4" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 3753ef34-9c63-40f2-afab-150600aa2505 + X-Runtime: + - '0.072507' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=FlX9RxXLN8cEbpqb%2FmxNG5yyxKiTowsxAbk61hhP64pnuDSagg8wwbL1xKXM7fhFdizhe7P8sVMPek7gvwzxyq5tzGj5rLEMTh2IvMbtXRluT9RhbZ5wbcvsuE%2Bc8CCb"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=359899&min_rtt=359292&rtt_var=135168&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3880&recv_bytes=1848&delivery_rate=11856&cwnd=33&unsent_bytes=0&cid=99ecee391b9b8fc8&ts=1091&x=0" + body: + encoding: UTF-8 + string: '[{"object":"account_verification","id":"accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw","status":"succeeded","reason":null,"transfer_id":"tr_32F2NNCg5s9JQy5AX5A6toWX9rd","counterparty":{"holder_id":"PPPR510220DB1","holder_name":"Adrianne + Murray","account_number":"735969000000203226","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"mode":"test","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=ce2zbPihXLg1MobUPCRrTrw9bB97KEYDGHg6rSGK7PaklMO9uV2ancSh1z2p8bIQCcwNCndMOBQx6ISkSOLTcLQ44r6s3qO7aVSNcWSiOp4%3D","transaction_date":"2025-09-04T16:05:58Z"}]' + recorded_at: Thu, 04 Sep 2025 16:06:54 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml new file mode 100644 index 0000000..f90c530 --- /dev/null +++ b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v2/account_verifications + body: + encoding: ASCII-8BIT + string: '' + headers: + Authorization: + - Bearer sk_test_SeCreT-aPi_KeY + User-Agent: + - fintoc-ruby/0.2.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 04 Sep 2025 16:06:52 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '605' + Connection: + - close + Cf-Ray: + - 979ebc8caefbebc6-CPH + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Link: + - '' + Etag: + - W/"9b52bfc2b4c200446ac3f7bc672c6bd4" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - f9aaa487-0ccb-4318-b3dc-73c094c8d744 + X-Runtime: + - '0.746597' + Vary: + - Origin + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=fFpcOJGutfgafODqY5V0pWn72LC8syGZaQSv4HIBjaVHNSFo2SDTLflPrYtNj3%2FKAySKD0yoe5%2F7i50KRkLwZtY%2FPAOLGZEdMp%2Bq2VB9QaNE9xsf7H1vPHGdjYz5RmfQ"}],"group":"cf-nel","max_age":604800}' + Nel: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Server: + - cloudflare + Server-Timing: + - cfL4;desc="?proto=TCP&rtt=260621&min_rtt=247216&rtt_var=119517&sent=7&recv=8&lost=0&retrans=0&sent_bytes=3878&recv_bytes=1804&delivery_rate=12018&cwnd=33&unsent_bytes=0&cid=d4549baa8c4f945c&ts=1552&x=0" + body: + encoding: UTF-8 + string: '[{"object":"account_verification","id":"accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw","status":"succeeded","reason":null,"transfer_id":"tr_32F2NNCg5s9JQy5AX5A6toWX9rd","counterparty":{"holder_id":"PPPR510220DB1","holder_name":"Adrianne + Murray","account_number":"735969000000203226","account_type":"clabe","institution":{"id":"90735","name":"FINTOC","country":"mx"}},"mode":"test","receipt_url":"https://www.banxico.org.mx/cep-beta/go?i=90735\u0026s=dummy\u0026d=ce2zbPihXLg1MobUPCRrTrw9bB97KEYDGHg6rSGK7PaklMO9uV2ancSh1z2p8bIQCcwNCndMOBQx6ISkSOLTcLQ44r6s3qO7aVSNcWSiOp4%3D","transaction_date":"2025-09-04T16:05:58Z"}]' + recorded_at: Thu, 04 Sep 2025 16:06:52 GMT +recorded_with: VCR 6.3.1 From d940e9a135050c035e19d3a67350fc3d50cb2d73 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:11:21 -0400 Subject: [PATCH 47/72] feature(account-verifications): add refresh methods to account verifications --- .../resources/account_verification.rb | 22 ++++++++++ .../transfers/account_verification_spec.rb | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/transfers/resources/account_verification.rb index f6386b8..5749740 100644 --- a/lib/fintoc/transfers/resources/account_verification.rb +++ b/lib/fintoc/transfers/resources/account_verification.rb @@ -37,6 +37,11 @@ def to_s "πŸ” Account Verification (#{@id}) - #{@status}" end + def refresh + fresh_verification = @client.get_account_verification(@id) + refresh_from_verification(fresh_verification) + end + def pending? @status == 'pending' end @@ -48,6 +53,23 @@ def succeeded? def failed? @status == 'failed' end + + private + + def refresh_from_verification(verification) + raise 'Account verification must be the same instance' unless verification.id == @id + + @object = verification.object + @status = verification.status + @reason = verification.reason + @transfer_id = verification.transfer_id + @counterparty = verification.counterparty + @mode = verification.mode + @receipt_url = verification.receipt_url + @transaction_date = verification.transaction_date + + self + end end end end diff --git a/spec/lib/fintoc/transfers/account_verification_spec.rb b/spec/lib/fintoc/transfers/account_verification_spec.rb index 59aca64..de49848 100644 --- a/spec/lib/fintoc/transfers/account_verification_spec.rb +++ b/spec/lib/fintoc/transfers/account_verification_spec.rb @@ -109,4 +109,44 @@ end end end + + describe '#refresh' do + let(:fresh_data) do + data.merge( + status: 'failed', + reason: 'insufficient_funds' + ) + end + + let(:fresh_verification) { described_class.new(**fresh_data) } + + before do + allow(client) + .to receive(:get_account_verification) + .with('accv_fdme30s11j5k7l1mekq4') + .and_return(fresh_verification) + end + + it 'refreshes the account verification data' do + result = account_verification.refresh + + expect(result).to eq(account_verification) + expect(account_verification).to have_attributes( + status: 'failed', + reason: 'insufficient_funds' + ) + end + + it 'raises an error if the verification ID does not match' do + wrong_verification = described_class.new(**fresh_data, id: 'wrong_id') + + allow(client) + .to receive(:get_account_verification) + .with('accv_fdme30s11j5k7l1mekq4') + .and_return(wrong_verification) + + expect { account_verification.refresh } + .to raise_error('Account verification must be the same instance') + end + end end From d92b874033fca598cc3739e31da0400d89a9917a Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:28:58 -0400 Subject: [PATCH 48/72] refactor(transfers): Remove unnecessary inclusions of Utils --- lib/fintoc/transfers/resources/account.rb | 3 --- lib/fintoc/transfers/resources/account_number.rb | 4 ---- lib/fintoc/transfers/resources/account_verification.rb | 4 ---- lib/fintoc/transfers/resources/entity.rb | 4 ---- lib/fintoc/transfers/resources/transfer.rb | 3 --- 5 files changed, 18 deletions(-) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 135beb8..2f7719d 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -1,11 +1,8 @@ require 'money' -require 'fintoc/utils' module Fintoc module Transfers class Account - include Utils - attr_reader :id, :object, :mode, :description, :available_balance, :currency, :is_root, :root_account_number_id, :root_account_number, :status, :entity diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 4663b57..5662238 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -1,10 +1,6 @@ -require 'fintoc/utils' - module Fintoc module Transfers class AccountNumber - include Utils - attr_reader :id, :object, :description, :number, :created_at, :updated_at, :mode, :status, :is_root, :account_id, :metadata diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/transfers/resources/account_verification.rb index 5749740..9f43e3c 100644 --- a/lib/fintoc/transfers/resources/account_verification.rb +++ b/lib/fintoc/transfers/resources/account_verification.rb @@ -1,10 +1,6 @@ -require 'fintoc/utils' - module Fintoc module Transfers class AccountVerification - include Utils - attr_reader :id, :object, :status, :reason, :transfer_id, :counterparty, :mode, :receipt_url, :transaction_date diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/transfers/resources/entity.rb index 2828bb8..a0ebf2c 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/transfers/resources/entity.rb @@ -1,10 +1,6 @@ -require 'fintoc/utils' - module Fintoc module Transfers class Entity - include Utils - attr_reader :object, :mode, :id, :holder_name, :holder_id, :is_root def initialize( diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb index 5274192..dff3033 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -1,11 +1,8 @@ require 'money' -require 'fintoc/utils' module Fintoc module Transfers class Transfer - include Utils - attr_reader :id, :object, :amount, :currency, :direction, :status, :mode, :post_date, :transaction_date, :comment, :reference_id, :receipt_url, :tracking_key, :return_reason, :counterparty, :account_number, From d413acc651d7810ed6c1055a413d30404b30b0bb Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:34:24 -0400 Subject: [PATCH 49/72] refactor(transfer/refresh-resources): Use ArgumentError for resfresh_from_* error on mismatching instances --- lib/fintoc/transfers/resources/account.rb | 4 +++- lib/fintoc/transfers/resources/account_number.rb | 4 +++- .../transfers/resources/account_verification.rb | 4 +++- lib/fintoc/transfers/resources/entity.rb | 4 +++- lib/fintoc/transfers/resources/transfer.rb | 4 +++- spec/lib/fintoc/transfers/account_number_spec.rb | 12 ++++++++++++ spec/lib/fintoc/transfers/account_spec.rb | 8 ++++++++ .../fintoc/transfers/account_verification_spec.rb | 2 +- spec/lib/fintoc/transfers/entity_spec.rb | 8 ++++++++ spec/lib/fintoc/transfers/transfer_spec.rb | 10 ++++++++++ 10 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 2f7719d..00094a3 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -83,7 +83,9 @@ def simulate_receive_transfer(amount:) private def refresh_from_account(account) - raise 'Account must be the same instance' unless account.id == @id + unless account.id == @id + raise ArgumentError, 'Account must be the same instance' + end @object = account.object @mode = account.mode diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 5662238..82aff76 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -83,7 +83,9 @@ def simulate_receive_transfer(amount:, currency: 'MXN') private def refresh_from_account_number(account_number) - raise 'AccountNumber must be the same instance' unless account_number.id == @id + unless account_number.id == @id + raise ArgumentError, 'AccountNumber must be the same instance' + end @object = account_number.object @description = account_number.description diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/transfers/resources/account_verification.rb index 9f43e3c..1881411 100644 --- a/lib/fintoc/transfers/resources/account_verification.rb +++ b/lib/fintoc/transfers/resources/account_verification.rb @@ -53,7 +53,9 @@ def failed? private def refresh_from_verification(verification) - raise 'Account verification must be the same instance' unless verification.id == @id + unless verification.id == @id + raise ArgumentError, 'Account verification must be the same instance' + end @object = verification.object @status = verification.status diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/transfers/resources/entity.rb index a0ebf2c..88cebc5 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/transfers/resources/entity.rb @@ -34,7 +34,9 @@ def refresh private def refresh_from_entity(entity) - raise 'Entity must be the same instance' unless entity.id == @id + unless entity.id == @id + raise ArgumentError, 'Entity must be the same instance' + end @object = entity.object @mode = entity.mode diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb index dff3033..c0f525c 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -102,7 +102,9 @@ def outbound? private def refresh_from_transfer(transfer) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - raise 'Transfer must be the same instance' unless transfer.id == @id + unless transfer.id == @id + raise ArgumentError, 'Transfer must be the same instance' + end @object = transfer.object @amount = transfer.amount diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index cb8b960..3ef5403 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -126,6 +126,18 @@ account_number.refresh expect(client).to have_received(:get_account_number).with('acno_Kasf91034gj1AD') end + + it 'raises an error if the account number ID does not match' do + wrong_account_number = described_class.new(**data, id: 'wrong_id') + + allow(client) + .to receive(:get_account_number) + .with('acno_Kasf91034gj1AD') + .and_return(wrong_account_number) + + expect { account_number.refresh } + .to raise_error(ArgumentError, 'AccountNumber must be the same instance') + end end describe '#update' do diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index d72346f..c06d4ba 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -124,6 +124,14 @@ expect(account.description).to eq('Updated account description') end + + it 'raises an error if the account ID does not match' do + wrong_account = described_class.new(**data, id: 'wrong_id') + + allow(client).to receive(:get_account).with('acc_123').and_return(wrong_account) + + expect { account.refresh }.to raise_error(ArgumentError, 'Account must be the same instance') + end end describe '#update' do diff --git a/spec/lib/fintoc/transfers/account_verification_spec.rb b/spec/lib/fintoc/transfers/account_verification_spec.rb index de49848..97f4148 100644 --- a/spec/lib/fintoc/transfers/account_verification_spec.rb +++ b/spec/lib/fintoc/transfers/account_verification_spec.rb @@ -146,7 +146,7 @@ .and_return(wrong_verification) expect { account_verification.refresh } - .to raise_error('Account verification must be the same instance') + .to raise_error(ArgumentError, 'Account verification must be the same instance') end end end diff --git a/spec/lib/fintoc/transfers/entity_spec.rb b/spec/lib/fintoc/transfers/entity_spec.rb index 321459b..f2cddc0 100644 --- a/spec/lib/fintoc/transfers/entity_spec.rb +++ b/spec/lib/fintoc/transfers/entity_spec.rb @@ -68,5 +68,13 @@ holder_name: 'Updated Company LLC' ) end + + it 'raises an error if the entity ID does not match' do + wrong_entity = described_class.new(**data, id: 'wrong_id') + + allow(client).to receive(:get_entity).with('ent_12345').and_return(wrong_entity) + + expect { entity.refresh }.to raise_error(ArgumentError, 'Entity must be the same instance') + end end end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb index 8abe098..6ccbc8f 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -228,6 +228,16 @@ it 'returns self' do expect(transfer.refresh).to eq(transfer) end + + it 'raises an error if the transfer ID does not match' do + wrong_transfer = described_class.new(**data, id: 'wrong_id') + + allow(client) + .to receive(:get_transfer).with('tr_329NGN1M4If6VvcMRALv4gjAQJx').and_return(wrong_transfer) + + expect { transfer.refresh } + .to raise_error(ArgumentError, 'Transfer must be the same instance') + end end describe '#return_transfer' do From 5f358ba10c09bd33852ecf5d1e76e0b2260589c9 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:20:05 -0400 Subject: [PATCH 50/72] refactor(errors): Update errors url ref --- lib/fintoc/constants.rb | 2 +- spec/lib/fintoc/errors_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fintoc/constants.rb b/lib/fintoc/constants.rb index 83ea22b..fc7e811 100644 --- a/lib/fintoc/constants.rb +++ b/lib/fintoc/constants.rb @@ -1,7 +1,7 @@ module Fintoc module Constants FIELDSUBS = [%w[id id_], %w[type type_]].freeze - GENERAL_DOC_URL = 'https://fintoc.com/docs' + GENERAL_DOC_URL = 'https://docs.fintoc.com/reference/errors' SCHEME = 'https://' BASE_URL = 'api.fintoc.com/v1/' BASE_URL_V2 = 'api.fintoc.com/v2/' diff --git a/spec/lib/fintoc/errors_spec.rb b/spec/lib/fintoc/errors_spec.rb index 547e528..2c6c005 100644 --- a/spec/lib/fintoc/errors_spec.rb +++ b/spec/lib/fintoc/errors_spec.rb @@ -17,6 +17,6 @@ it 'raises a Invalid Request Error with default url doc' do expect { raise Fintoc::Errors::InvalidRequestError.new(error[:message]) } .to(raise_error(an_instance_of(Fintoc::Errors::InvalidRequestError)) - .with_message(%r{https://fintoc.com/docs})) + .with_message(%r{https://docs.fintoc.com/reference/errors})) end end From 8d44fd5994f66bcb4376479c0b463e29307c2822 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 12:21:24 -0400 Subject: [PATCH 51/72] feature(errors): Update list of possible errors --- lib/fintoc/errors.rb | 144 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 11 deletions(-) diff --git a/lib/fintoc/errors.rb b/lib/fintoc/errors.rb index b928ddd..465bf45 100644 --- a/lib/fintoc/errors.rb +++ b/lib/fintoc/errors.rb @@ -19,23 +19,145 @@ def to_s end end + # 400 Bad Request Errors class InvalidRequestError < FintocError; end - class LinkError < FintocError; end - class AuthenticationError < FintocError; end - class InstitutionError < FintocError; end - class ApiError < FintocError; end - class MissingResourceError < FintocError; end - class InvalidLinkTokenError < FintocError; end - class InvalidUsernameError < FintocError; end - class InvalidHolderTypeError < FintocError; end + class InvalidCurrencyError < FintocError; end + class InvalidAmountError < FintocError; end + class InvalidAccountTypeError < FintocError; end + class InvalidAccountNumberError < FintocError; end + class InvalidAccountStatusError < FintocError; end + class InvalidAccountBalanceError < FintocError; end + class InvalidInstitutionIdError < FintocError; end + class CurrencyMismatchError < FintocError; end + class InvalidCommentSizeError < FintocError; end + class InvalidReferenceIdSizeError < FintocError; end class MissingParameterError < FintocError; end + class InvalidPositiveIntegerError < FintocError; end class EmptyStringError < FintocError; end - class UnrecognizedRequestError < FintocError; end + class InvalidStringSizeError < FintocError; end + class InvalidHashError < FintocError; end + class InvalidBooleanError < FintocError; end + class InvalidArrayError < FintocError; end + class InvalidIntegerError < FintocError; end + class InvalidJsonError < FintocError; end + class InvalidParamsError < FintocError; end + class MissingCursorError < FintocError; end + class InvalidEnumError < FintocError; end + class InvalidStringError < FintocError; end + class InvalidUsernameError < FintocError; end + class InvalidLinkTokenError < FintocError; end class InvalidDateError < FintocError; end - class InvalidCredentialsError < FintocError; end - class LockedCredentialsError < FintocError; end + class InvalidHolderIdError < FintocError; end + class InvalidCardNumberError < FintocError; end + class InvalidProductError < FintocError; end + class InvalidWebhookSubscriptionError < FintocError; end + class InvalidIssueTypeError < FintocError; end + class InvalidRefreshTypeError < FintocError; end + class InvalidBusinessProfileTaxIdError < FintocError; end + class InvalidSessionHolderIdError < FintocError; end + class InvalidPaymentRecipientAccountError < FintocError; end + class InvalidPayoutRecipientAccountError < FintocError; end + class InvalidWidgetTokenError < FintocError; end + class InvalidPaymentReferenceNumberError < FintocError; end + class InvalidOnDemandLinkError < FintocError; end + class InvalidHolderTypeError < FintocError; end + class InvalidVoucherDownloadError < FintocError; end + class InvalidModeError < FintocError; end + class InvalidRsaKeyError < FintocError; end + class ExpectedPublicRsaKeyError < FintocError; end + class InvalidCidrBlockError < FintocError; end + class InvalidExpiresAtError < FintocError; end + class InvalidInstallmentsCurrencyError < FintocError; end + class InvalidClabeError < FintocError; end + class MismatchTransferAccountCurrencyError < FintocError; end + + # 401 Unauthorized Errors + class AuthenticationError < FintocError; end class InvalidApiKeyError < FintocError; end + class ExpiredApiKeyError < FintocError; end + class InvalidApiKeyModeError < FintocError; end + class ExpiredExchangeTokenError < FintocError; end + class InvalidExchangeTokenError < FintocError; end + class MissingActiveJwsPublicKeyError < FintocError; end + class InvalidJwsSignatureAlgorithmError < FintocError; end + class InvalidJwsSignatureHeaderError < FintocError; end + class InvalidJwsSignatureNonceError < FintocError; end + class InvalidJwsSignatureTimestampError < FintocError; end + class InvalidJwsSignatureTimestampFormatError < FintocError; end + class InvalidJwsSignatureTimestampValueError < FintocError; end + class MissingJwsSignatureHeaderError < FintocError; end + class JwsNonceAlreadyUsedError < FintocError; end + class InvalidJwsTsError < FintocError; end + + # 402 Payment Required Errors + class PaymentRequiredError < FintocError; end + + # 403 Forbidden Errors + class InvalidAccountError < FintocError; end + class InvalidRecipientAccountError < FintocError; end + class AccountNotActiveError < FintocError; end + class EntityNotOperationalError < FintocError; end + class ForbiddenEntityError < FintocError; end + class ForbiddenAccountError < FintocError; end + class ForbiddenAccountNumberError < FintocError; end + class ForbiddenAccountVerificationError < FintocError; end + class InvalidApiVersionError < FintocError; end + class ProductAccessRequiredError < FintocError; end + class ForbiddenRequestError < FintocError; end + class MissingAllowedCidrBlocksError < FintocError; end + class AllowedCidrBlocksDoesNotContainIpError < FintocError; end + class RecipientBlockedAccountError < FintocError; end + + # 404 Not Found Errors + class MissingResourceError < FintocError; end + class InvalidUrlError < FintocError; end + class OrganizationWithoutEntitiesError < FintocError; end + + # 405 Method Not Allowed Errors + class OperationNotAllowedError < FintocError; end + + # 406 Not Acceptable Errors + class InstitutionCredentialsInvalidError < FintocError; end + class LockedCredentialsError < FintocError; end class UnavailableInstitutionError < FintocError; end + + # 409 Conflict Errors + class InsufficientBalanceError < FintocError; end + class InvalidDuplicatedTransferError < FintocError; end + class InvalidTransferStatusError < FintocError; end + class InvalidTransferDirectionError < FintocError; end + class AccountNumberLimitReachedError < FintocError; end + class AccountCannotBeBlockedError < FintocError; end + + # 422 Unprocessable Entity Errors + class InvalidOtpCodeError < FintocError; end + class OtpNotFoundError < FintocError; end + class OtpBlockedError < FintocError; end + class OtpVerificationFailedError < FintocError; end + class OtpAlreadyExistsError < FintocError; end + class SubscriptionInProgressError < FintocError; end + class OnDemandPolicyRequiredError < FintocError; end + class OnDemandRefreshUnavailableError < FintocError; end + class NotSupportedCountryError < FintocError; end + class NotSupportedCurrencyError < FintocError; end + class NotSupportedModeError < FintocError; end + class NotSupportedProductError < FintocError; end + class RefreshIntentInProgressError < FintocError; end + class RejectedRefreshIntentError < FintocError; end + class SenderBlockedAccountError < FintocError; end + + # 429 Too Many Requests Errors + class RateLimitExceededError < FintocError; end + + # 500 Internal Server Errors class InternalServerError < FintocError; end + class UnrecognizedRequestError < FintocError; end + class CoreResponseError < FintocError; end + + # Legacy Errors (keeping existing ones for backward compatibility and just in case) + class LinkError < FintocError; end + class InstitutionError < FintocError; end + class ApiError < FintocError; end + class InvalidCredentialsError < FintocError; end end end From b4796c1d4b106dc634b1e7395950c538419648b6 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 16:30:21 -0400 Subject: [PATCH 52/72] feature(docs): Update readme docs to include new methods and changes to the api use --- README.md | 192 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 03cc503..d468f76 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,16 @@ Do yourself a favor: go grab some ice cubes by installing this refreshing librar - [**Backward compatibility**](#backward-compatibility) - [Documentation](#documentation) - [Examples](#examples) - - [Get accounts](#get-accounts) - - [Get movements](#get-movements) - - [Get entities](#get-entities) + - [Movements API Examples](#movements-api-examples) + - [Get accounts](#get-accounts) + - [Get movements](#get-movements) + - [Transfers API Examples](#transfers-api-examples) + - [Entities](#entities) + - [Transfer Accounts](#transfer-accounts) + - [Account Numbers](#account-numbers) + - [Transfers](#transfers) + - [Simulation](#simulation) + - [Account Verifications](#account-verifications) - [Development](#development) - [Dependencies](#dependencies) - [Setup](#setup) @@ -57,7 +64,7 @@ Or install it yourself as: require 'fintoc' movements_client = - Fintoc::Clients::MovementsClient.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') + Fintoc::Movements::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') link = movements_client.get_link('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') account = link.find(type: 'checking_account') @@ -76,22 +83,56 @@ The Fintoc Ruby client is organized into separate clients that mirror the offici ### **Movements API Client** +The Movements API client provides access to bank account data and movements: + ```ruby -movements_client = Fintoc::Clients::MovementsClient.new('api_key') +movements_client = Fintoc::Movements::Client.new('api_key') +# Link management links = movements_client.get_links link = movements_client.get_link('link_token') movements_client.delete_link('link_id') + +# Account access account = movements_client.get_account('link_token', 'account_id') ``` ### **Transfers API Client** +The Transfers API client provides access to transfer accounts, entities, and transfer operations: + ```ruby -transfers_client = Fintoc::Clients::TransfersClient.new('api_key') +transfers_client = Fintoc::Transfers::Client.new('api_key') +# Entities entities = transfers_client.get_entities entity = transfers_client.get_entity('entity_id') + +# Transfer Accounts +accounts = transfers_client.list_accounts +account = transfers_client.get_account('account_id') +account = transfers_client.create_account(entity_id: 'entity_id', description: 'My Account') +transfers_client.update_account('account_id', description: 'Updated') + +# Account Numbers +account_numbers = transfers_client.list_account_numbers +account_number = transfers_client.get_account_number('account_number_id') +account_number = transfers_client.create_account_number(account_id: 'account_id', description: 'Main') +transfers_client.update_account_number('account_number_id', description: 'Updated') + +# Transfers +transfers = transfers_client.list_transfers +transfer = transfers_client.get_transfer('transfer_id') +transfer = transfers_client.create_transfer(amount: 1000, currency: 'CLP', account_id: 'account_id', counterparty: {...}) +transfers_client.return_transfer('transfer_id') + +# Simulation +simulation = transfers_client.simulate_receive_transfer(account_number_id: 'account_number_id', amount: 1000, currency: 'CLP') + +# Account Verifications +account_verifications = transfers_client.list_account_verifications +account_verification = transfers_client.get_account_verification('account_verification_id') +account_verification = transfers_client.create_account_verification(account_number: 'account_number') ``` ### **Backward compatibility** @@ -110,12 +151,14 @@ This client supports all Fintoc API endpoints. For complete information about th ## Examples -### Get accounts +### Movements API Examples + +#### Get accounts ```ruby require 'fintoc' -client = Fintoc::Clients::MovementsClient.new('api_key') +client = Fintoc::Movements::Client.new('api_key') link = client.get_link('link_token') puts link.accounts @@ -131,7 +174,7 @@ If you want to find a specific account in a link, you can use **find**. You can ```ruby require 'fintoc' -client = Fintoc::Clients::MovementsClient.new('api_key') +client = Fintoc::Movements::Client.new('api_key') link = client.get_link('link_token') account = link.find(type: 'checking_account') @@ -147,7 +190,7 @@ You can also search for multiple accounts matching a specific criteria with **fi ```ruby require 'fintoc' -client = Fintoc::Clients::MovementsClient.new('api_key') +client = Fintoc::Movements::Client.new('api_key') link = client.get_link('link_token') accounts = link.find_all(currency: 'CLP') ``` @@ -157,19 +200,19 @@ To update the account balance you can use **update_balance**: ```ruby require 'fintoc' -client = Fintoc::Clients::MovementsClient.new('api_key') +client = Fintoc::Movements::Client.new('api_key') link = client.get_link('link_token') account = link.find(number: '1111111') account.update_balance ``` -### Get movements +#### Get movements ```ruby require 'fintoc' require 'time' -client = Fintoc::Clients::MovementsClient.new('api_key') +client = Fintoc::Movements::Client.new('api_key') link = client.get_link('link_token') account = link.find(type: 'checking_account') @@ -186,18 +229,20 @@ account.get_movements(since: '2020-01-01', per_page: 100) Calling **get_movements** without arguments gets the last 30 movements of the account -### Get entities +### Transfers API Examples + +#### Entities ```ruby require 'fintoc' -client = Fintoc::Clients::TransfersClient.new('api_key') +client = Fintoc::Transfers::Client.new('api_key') # Get all entities entities = client.get_entities # Get a specific entity -entity = client.get_entity('ent_12345') +entity = client.get_entity('entity_id') puts entity.holder_name # => "My Company LLC" puts entity.holder_id # => "12345678-9" @@ -208,7 +253,120 @@ You can also list entities with pagination: ```ruby # Get entities with pagination -entities = client.get_entities(limit: 10, starting_after: 'ent_123') +entities = client.get_entities(limit: 10, starting_after: 'entity_id') +``` + +#### Transfer Accounts + +```ruby +require 'fintoc' + +client = Fintoc::Transfers::Client.new('api_key') + +# Create a transfer account +account = client.create_account( + entity_id: 'entity_id', + description: 'My Business Account' +) + +# Get a specific account +account = client.get_account('account_id') + +# List all accounts +accounts = client.list_accounts + +# Update an account +updated_account = client.update_account('account_id', description: 'Updated Description') +``` + +#### Account Numbers + +```ruby +require 'fintoc' + +client = Fintoc::Transfers::Client.new('api_key') + +# Create an account number +account_number = client.create_account_number( + account_id: 'account_id', + description: 'Main account number' +) + +# Get a specific account number +account_number = client.get_account_number('account_number_id') + +# List all account numbers +account_numbers = client.list_account_numbers + +# Update an account number +updated_account_number = client.update_account_number( + 'account_number_id', + description: 'Updated account number' +) +``` + +#### Transfers + +```ruby +require 'fintoc' + +client = Fintoc::Transfers::Client.new('api_key') + +# Create a transfer +transfer = client.create_transfer( + amount: 10000, + currency: 'CLP', + account_id: 'account_id', + counterparty: { + name: 'John Doe', + rut: '12345678-9', + email: 'john@example.com', + bank: 'banco_de_chile', + account_type: 'checking_account', + account_number: '1234567890' + } +) + +# Get a specific transfer +transfer = client.get_transfer('transfer_id') + +# List all transfers +transfers = client.list_transfers + +# Return a transfer +returned_transfer = client.return_transfer('transfer_id') +``` + +#### Simulation + +```ruby +require 'fintoc' + +client = Fintoc::Transfers::Client.new('api_key') + +# Simulate receiving a transfer +simulation = client.simulate_receive_transfer( + account_number_id: 'account_number_id', + amount: 5000, + currency: 'CLP' +) +``` + +#### Account Verifications + +```ruby +require 'fintoc' + +client = Fintoc::Transfers::Client.new('api_key') + +# Create an account verification +account_verification = client.create_account_verification(account_number: 'account_number') + +# Get a specific account verification +account_verification = client.get_account_verification('account_verification_id') + +# List all account verifications +account_verifications = client.list_account_verifications ``` ## Development From eae23328027ab47c123756ce3116a2d818def457 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Thu, 4 Sep 2025 20:27:02 -0400 Subject: [PATCH 53/72] refactor(client): Use managers instead of methods to access all api methods as in Python SDK --- README.md | 114 ++++++------- lib/fintoc/base_client.rb | 2 + lib/fintoc/client.rb | 16 +- lib/fintoc/movements/client/client.rb | 6 +- lib/fintoc/movements/client/links_methods.rb | 40 ----- .../movements/managers/links_manager.rb | 46 ++++++ lib/fintoc/movements/resources/account.rb | 8 +- lib/fintoc/movements/resources/link.rb | 2 +- lib/fintoc/movements/resources/movement.rb | 2 + .../client/account_numbers_methods.rb | 53 ------ .../client/account_verifications_methods.rb | 39 ----- .../transfers/client/accounts_methods.rb | 48 ------ lib/fintoc/transfers/client/client.rb | 41 +++-- .../transfers/client/entities_methods.rb | 30 ---- .../transfers/client/simulation_methods.rb | 23 --- .../transfers/client/transfers_methods.rb | 49 ------ .../managers/account_numbers_manager.rb | 59 +++++++ .../managers/account_verifications_manager.rb | 45 ++++++ .../transfers/managers/accounts_manager.rb | 54 +++++++ .../transfers/managers/entities_manager.rb | 36 +++++ .../transfers/managers/simulate_manager.rb | 30 ++++ .../transfers/managers/transfers_manager.rb | 56 +++++++ lib/fintoc/transfers/resources/account.rb | 6 +- .../transfers/resources/account_number.rb | 6 +- .../resources/account_verification.rb | 2 +- lib/fintoc/transfers/resources/entity.rb | 2 +- lib/fintoc/transfers/resources/transfer.rb | 4 +- spec/lib/fintoc/client_spec.rb | 47 ++---- spec/lib/fintoc/movements/account_spec.rb | 4 +- spec/lib/fintoc/movements/client_spec.rb | 11 +- .../fintoc/movements/links_methods_spec.rb | 47 ------ .../movements/managers/links_manager_spec.rb | 77 +++++++++ .../fintoc/transfers/account_number_spec.rb | 18 +-- .../transfers/account_numbers_methods_spec.rb | 83 ---------- spec/lib/fintoc/transfers/account_spec.rb | 20 +-- .../transfers/account_verification_spec.rb | 8 +- .../account_verifications_methods_spec.rb | 67 -------- .../fintoc/transfers/accounts_methods_spec.rb | 49 ------ spec/lib/fintoc/transfers/client_spec.rb | 12 +- .../fintoc/transfers/entities_methods_spec.rb | 37 ----- spec/lib/fintoc/transfers/entity_spec.rb | 6 +- .../managers/account_numbers_manager_spec.rb | 103 ++++++++++++ .../account_verifications_manager_spec.rb | 72 +++++++++ .../managers/accounts_manager_spec.rb | 111 +++++++++++++ .../managers/entities_manager_spec.rb | 68 ++++++++ .../managers/simulate_manager_spec.rb | 39 +++++ .../managers/transfers_manager_spec.rb | 107 +++++++++++++ .../transfers/simulation_methods_spec.rb | 38 ----- spec/lib/fintoc/transfers/transfer_spec.rb | 9 +- .../account_numbers_client_examples.rb | 100 ++++++------ .../account_verifications_client_examples.rb | 79 ++++----- .../clients/accounts_client_examples.rb | 96 +++++------ .../clients/entities_client_examples.rb | 46 +++--- .../clients/links_client_examples.rb | 51 +++--- .../clients/simulate_client_examples.rb | 32 ++++ .../clients/simulation_client_examples.rb | 23 --- .../clients/transfers_client_examples.rb | 104 ++++++------ .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 .../_get_account/get_a_linked_account.yml | 151 ------------------ .../prints_accounts_to_console.yml | 78 --------- .../returns_an_AccountNumber_instance.yml | 0 .../returns_an_AccountNumber_instance.yml | 0 ...ns_an_array_of_AccountNumber_instances.yml | 0 ...urns_an_updated_AccountNumber_instance.yml | 0 ...eturns_an_AccountVerification_instance.yml | 0 ...eturns_an_AccountVerification_instance.yml | 0 .../_list}/accepts_filtering_parameters.yml | 0 ...array_of_AccountVerification_instances.yml | 0 .../_create}/returns_an_Account_instance.yml | 0 .../_get}/returns_an_Account_instance.yml | 0 .../returns_an_array_of_Account_instances.yml | 0 .../returns_an_updated_Account_instance.yml | 0 .../_get}/returns_an_Entity_instance.yml | 0 .../returns_an_array_of_Entity_instances.yml | 0 ...a_transfer_and_returns_Transfer_object.yml | 0 .../_create}/returns_a_Transfer_instance.yml | 0 .../_get}/returns_a_Transfer_instance.yml | 0 .../_list}/accepts_filtering_parameters.yml | 0 ...returns_an_array_of_Transfer_instances.yml | 0 ...er_instance_with_return_pending_status.yml | 0 81 files changed, 1351 insertions(+), 1261 deletions(-) delete mode 100644 lib/fintoc/movements/client/links_methods.rb create mode 100644 lib/fintoc/movements/managers/links_manager.rb delete mode 100644 lib/fintoc/transfers/client/account_numbers_methods.rb delete mode 100644 lib/fintoc/transfers/client/account_verifications_methods.rb delete mode 100644 lib/fintoc/transfers/client/accounts_methods.rb delete mode 100644 lib/fintoc/transfers/client/entities_methods.rb delete mode 100644 lib/fintoc/transfers/client/simulation_methods.rb delete mode 100644 lib/fintoc/transfers/client/transfers_methods.rb create mode 100644 lib/fintoc/transfers/managers/account_numbers_manager.rb create mode 100644 lib/fintoc/transfers/managers/account_verifications_manager.rb create mode 100644 lib/fintoc/transfers/managers/accounts_manager.rb create mode 100644 lib/fintoc/transfers/managers/entities_manager.rb create mode 100644 lib/fintoc/transfers/managers/simulate_manager.rb create mode 100644 lib/fintoc/transfers/managers/transfers_manager.rb delete mode 100644 spec/lib/fintoc/movements/links_methods_spec.rb create mode 100644 spec/lib/fintoc/movements/managers/links_manager_spec.rb delete mode 100644 spec/lib/fintoc/transfers/account_numbers_methods_spec.rb delete mode 100644 spec/lib/fintoc/transfers/account_verifications_methods_spec.rb delete mode 100644 spec/lib/fintoc/transfers/accounts_methods_spec.rb delete mode 100644 spec/lib/fintoc/transfers/entities_methods_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/entities_manager_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb create mode 100644 spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb delete mode 100644 spec/lib/fintoc/transfers/simulation_methods_spec.rb create mode 100644 spec/support/shared_examples/clients/simulate_client_examples.rb delete mode 100644 spec/support/shared_examples/clients/simulation_client_examples.rb rename spec/vcr/Fintoc_Movements_Client/{behaves_like_a_client_with_links_methods/_get_link => behaves_like_a_client_with_links_manager/_links/_get}/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/Fintoc_Movements_Client/{behaves_like_a_client_with_links_methods/_get_links => behaves_like_a_client_with_links_manager/_links/_list}/get_all_the_links_from_a_given_link_token.yml (100%) delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml delete mode 100644 spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_numbers_methods/_create_account_number => behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create}/returns_an_AccountNumber_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_numbers_methods/_get_account_number => behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get}/returns_an_AccountNumber_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_numbers_methods/_list_account_numbers => behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list}/returns_an_array_of_AccountNumber_instances.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_numbers_methods/_update_account_number => behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update}/returns_an_updated_AccountNumber_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_verifications_methods/_create_account_verification => behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create}/returns_an_AccountVerification_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_verifications_methods/_get_account_verification => behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get}/returns_an_AccountVerification_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_verifications_methods/_list_account_verifications => behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list}/accepts_filtering_parameters.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_account_verifications_methods/_list_account_verifications => behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list}/returns_an_array_of_AccountVerification_instances.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_accounts_methods/_create_account => behaves_like_a_client_with_accounts_manager/_accounts/_create}/returns_an_Account_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_accounts_methods/_get_account => behaves_like_a_client_with_accounts_manager/_accounts/_get}/returns_an_Account_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_accounts_methods/_list_accounts => behaves_like_a_client_with_accounts_manager/_accounts/_list}/returns_an_array_of_Account_instances.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_accounts_methods/_update_account => behaves_like_a_client_with_accounts_manager/_accounts/_update}/returns_an_updated_Account_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_entities_methods/_get_entity => behaves_like_a_client_with_entities_manager/_entities/_get}/returns_an_Entity_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_entities_methods/_get_entities => behaves_like_a_client_with_entities_manager/_entities/_list}/returns_an_array_of_Entity_instances.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer => behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer}/simulates_receiving_a_transfer_and_returns_Transfer_object.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_transfers_methods/_create_transfer => behaves_like_a_client_with_transfers_manager/_transfers/_create}/returns_a_Transfer_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_transfers_methods/_get_transfer => behaves_like_a_client_with_transfers_manager/_transfers/_get}/returns_a_Transfer_instance.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_transfers_methods/_list_transfers => behaves_like_a_client_with_transfers_manager/_transfers/_list}/accepts_filtering_parameters.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_transfers_methods/_list_transfers => behaves_like_a_client_with_transfers_manager/_transfers/_list}/returns_an_array_of_Transfer_instances.yml (100%) rename spec/vcr/Fintoc_Transfers_Client/{behaves_like_a_client_with_transfers_methods/_return_transfer => behaves_like_a_client_with_transfers_manager/_transfers/_return}/returns_a_Transfer_instance_with_return_pending_status.yml (100%) diff --git a/README.md b/README.md index d468f76..8553b6f 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Do yourself a favor: go grab some ice cubes by installing this refreshing librar - [Transfer Accounts](#transfer-accounts) - [Account Numbers](#account-numbers) - [Transfers](#transfers) - - [Simulation](#simulation) + - [Simulate](#simulate) - [Account Verifications](#account-verifications) - [Development](#development) - [Dependencies](#dependencies) @@ -65,14 +65,14 @@ require 'fintoc' movements_client = Fintoc::Movements::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') -link = movements_client.get_link('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') +link = movements_client.links.get('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') account = link.find(type: 'checking_account') # Get the last 30 movements -movements = account.get_movements +movements = account.momvements.list # Or get all the movements since a specific date -movements = account.get_movements(since: '2020-08-15') +movements = account.movements.list(since: '2020-08-15') ``` And that’s it! @@ -89,12 +89,12 @@ The Movements API client provides access to bank account data and movements: movements_client = Fintoc::Movements::Client.new('api_key') # Link management -links = movements_client.get_links -link = movements_client.get_link('link_token') -movements_client.delete_link('link_id') +links = movements_client.links.list +link = movements_client.links.get('link_token') +movements_client.links.delete('link_id') # Account access -account = movements_client.get_account('link_token', 'account_id') +account = movements_client.accounts.get('link_token', 'account_id') ``` ### **Transfers API Client** @@ -105,34 +105,34 @@ The Transfers API client provides access to transfer accounts, entities, and tra transfers_client = Fintoc::Transfers::Client.new('api_key') # Entities -entities = transfers_client.get_entities -entity = transfers_client.get_entity('entity_id') +entities = transfers_client.entities.list +entity = transfers_client.entities.get('entity_id') # Transfer Accounts -accounts = transfers_client.list_accounts -account = transfers_client.get_account('account_id') -account = transfers_client.create_account(entity_id: 'entity_id', description: 'My Account') -transfers_client.update_account('account_id', description: 'Updated') +accounts = transfers_client.accounts.list +account = transfers_client.accounts.get('account_id') +account = transfers_client.accounts.create(entity_id: 'entity_id', description: 'My Account') +transfers_client.accounts.update('account_id', description: 'Updated') # Account Numbers -account_numbers = transfers_client.list_account_numbers -account_number = transfers_client.get_account_number('account_number_id') -account_number = transfers_client.create_account_number(account_id: 'account_id', description: 'Main') -transfers_client.update_account_number('account_number_id', description: 'Updated') +account_numbers = transfers_client.account_numbers.list +account_number = transfers_client.account_numbers.get('account_number_id') +account_number = transfers_client.account_numbers.create(account_id: 'account_id', description: 'Main') +transfers_client.account_numbers.update('account_number_id', description: 'Updated') # Transfers -transfers = transfers_client.list_transfers -transfer = transfers_client.get_transfer('transfer_id') -transfer = transfers_client.create_transfer(amount: 1000, currency: 'CLP', account_id: 'account_id', counterparty: {...}) -transfers_client.return_transfer('transfer_id') +transfers = transfers_client.transfers.list +transfer = transfers_client.transfers.get('transfer_id') +transfer = transfers_client.transfers.create(amount: 1000, currency: 'CLP', account_id: 'account_id', counterparty: {...}) +transfers_client.transfers.return('transfer_id') -# Simulation -simulation = transfers_client.simulate_receive_transfer(account_number_id: 'account_number_id', amount: 1000, currency: 'CLP') +# Simulate +simulated_transfer = transfers_client.simulate.receive_transfer(account_number_id: 'account_number_id', amount: 1000, currency: 'CLP') # Account Verifications -account_verifications = transfers_client.list_account_verifications -account_verification = transfers_client.get_account_verification('account_verification_id') -account_verification = transfers_client.create_account_verification(account_number: 'account_number') +account_verifications = transfers_client.account_verifications.list +account_verification = transfers_client.account_verifications.get('account_verification_id') +account_verification = transfers_client.account_verifications.create(account_number: 'account_number') ``` ### **Backward compatibility** @@ -159,12 +159,12 @@ This client supports all Fintoc API endpoints. For complete information about th require 'fintoc' client = Fintoc::Movements::Client.new('api_key') -link = client.get_link('link_token') +link = client.links.get('link_token') puts link.accounts # Or... you can pretty print all the accounts in a Link -link = client.get_link('link_token') +link = client.links.get('link_token') link.show_accounts ``` @@ -175,7 +175,7 @@ If you want to find a specific account in a link, you can use **find**. You can require 'fintoc' client = Fintoc::Movements::Client.new('api_key') -link = client.get_link('link_token') +link = client.links.get('link_token') account = link.find(type: 'checking_account') # Or by number @@ -191,7 +191,7 @@ You can also search for multiple accounts matching a specific criteria with **fi require 'fintoc' client = Fintoc::Movements::Client.new('api_key') -link = client.get_link('link_token') +link = client.links.get('link_token') accounts = link.find_all(currency: 'CLP') ``` @@ -201,7 +201,7 @@ To update the account balance you can use **update_balance**: require 'fintoc' client = Fintoc::Movements::Client.new('api_key') -link = client.get_link('link_token') +link = client.links.get('link_token') account = link.find(number: '1111111') account.update_balance ``` @@ -213,21 +213,21 @@ require 'fintoc' require 'time' client = Fintoc::Movements::Client.new('api_key') -link = client.get_link('link_token') +link = client.links.get('link_token') account = link.find(type: 'checking_account') # You can get the account movements since a specific DateTime yesterday = DateTime.now - 1 -account.get_movements(since: yesterday) +account.movements.list(since: yesterday) # Or you can use an ISO 8601 formatted string representation of the Date -account.get_movements(since: '2020-01-01') +account.movements.list(since: '2020-01-01') # You can also set how many movements you want per_page -account.get_movements(since: '2020-01-01', per_page: 100) +account.movements.list(since: '2020-01-01', per_page: 100) ``` -Calling **get_movements** without arguments gets the last 30 movements of the account +Calling **movements.list** without arguments gets the last 30 movements of the account ### Transfers API Examples @@ -239,10 +239,10 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Get all entities -entities = client.get_entities +entities = client.entities.list # Get a specific entity -entity = client.get_entity('entity_id') +entity = client.entities.get('entity_id') puts entity.holder_name # => "My Company LLC" puts entity.holder_id # => "12345678-9" @@ -253,7 +253,7 @@ You can also list entities with pagination: ```ruby # Get entities with pagination -entities = client.get_entities(limit: 10, starting_after: 'entity_id') +entities = client.entities.list(limit: 10, starting_after: 'entity_id') ``` #### Transfer Accounts @@ -264,19 +264,19 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Create a transfer account -account = client.create_account( +account = client.accounts.create( entity_id: 'entity_id', description: 'My Business Account' ) # Get a specific account -account = client.get_account('account_id') +account = client.accounts.get('account_id') # List all accounts -accounts = client.list_accounts +accounts = client.accounts.list # Update an account -updated_account = client.update_account('account_id', description: 'Updated Description') +updated_account = client.accounts.update('account_id', description: 'Updated Description') ``` #### Account Numbers @@ -287,19 +287,19 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Create an account number -account_number = client.create_account_number( +account_number = client.account_numbers.create( account_id: 'account_id', description: 'Main account number' ) # Get a specific account number -account_number = client.get_account_number('account_number_id') +account_number = client.account_numbers.get('account_number_id') # List all account numbers -account_numbers = client.list_account_numbers +account_numbers = client.account_numbers.list # Update an account number -updated_account_number = client.update_account_number( +updated_account_number = client.account_numbers.update( 'account_number_id', description: 'Updated account number' ) @@ -313,7 +313,7 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Create a transfer -transfer = client.create_transfer( +transfer = client.transfers.create( amount: 10000, currency: 'CLP', account_id: 'account_id', @@ -328,16 +328,16 @@ transfer = client.create_transfer( ) # Get a specific transfer -transfer = client.get_transfer('transfer_id') +transfer = client.transfers.get('transfer_id') # List all transfers -transfers = client.list_transfers +transfers = client.transfers.list # Return a transfer -returned_transfer = client.return_transfer('transfer_id') +returned_transfer = client.transfers.return('transfer_id') ``` -#### Simulation +#### Simulate ```ruby require 'fintoc' @@ -345,7 +345,7 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Simulate receiving a transfer -simulation = client.simulate_receive_transfer( +simulated_transfer = client.simulate.receive_transfer( account_number_id: 'account_number_id', amount: 5000, currency: 'CLP' @@ -360,13 +360,13 @@ require 'fintoc' client = Fintoc::Transfers::Client.new('api_key') # Create an account verification -account_verification = client.create_account_verification(account_number: 'account_number') +account_verification = client.account_verifications.create(account_number: 'account_number') # Get a specific account verification -account_verification = client.get_account_verification('account_verification_id') +account_verification = client.account_verifications.get('account_verification_id') # List all account verifications -account_verifications = client.list_account_verifications +account_verifications = client.account_verifications.list ``` ## Development diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index ebe01f5..2bf40d9 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -10,6 +10,8 @@ module Fintoc class BaseClient include Utils + attr_accessor :default_params + def initialize(api_key, jws_private_key: nil) @api_key = api_key @user_agent = "fintoc-ruby/#{Fintoc::VERSION}" diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 166d31c..973e853 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -15,27 +15,19 @@ def initialize(api_key, jws_private_key: nil) # Delegate common methods to maintain backward compatibility def get_link(link_token) - @movements.get_link(link_token) + @movements.links.get(link_token) end def get_links - @movements.get_links + @movements.links.list end def delete_link(link_id) - @movements.delete_link(link_id) + @movements.links.delete(link_id) end def get_account(link_token, account_id) - @movements.get_account(link_token, account_id) - end - - def get_entity(entity_id) - @transfers.get_entity(entity_id) - end - - def get_entities(**params) - @transfers.get_entities(**params) + @movements.links.get(link_token).find(id: account_id) end def to_s diff --git a/lib/fintoc/movements/client/client.rb b/lib/fintoc/movements/client/client.rb index fef5bf2..8c53c22 100644 --- a/lib/fintoc/movements/client/client.rb +++ b/lib/fintoc/movements/client/client.rb @@ -1,10 +1,12 @@ require 'fintoc/base_client' -require 'fintoc/movements/client/links_methods' +require 'fintoc/movements/managers/links_manager' module Fintoc module Movements class Client < BaseClient - include LinksMethods + def links + @links ||= Managers::LinksManager.new(self) + end end end end diff --git a/lib/fintoc/movements/client/links_methods.rb b/lib/fintoc/movements/client/links_methods.rb deleted file mode 100644 index 632b1b6..0000000 --- a/lib/fintoc/movements/client/links_methods.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'fintoc/movements/resources/link' - -module Fintoc - module Movements - module LinksMethods - def get_link(link_token) - data = { **_get_link(link_token), link_token: link_token } - build_link(data) - end - - def get_links - _get_links.map { |data| build_link(data) } - end - - def delete_link(link_id) - delete.call("links/#{link_id}") - end - - def get_account(link_token, account_id) - get_link(link_token).find(id: account_id) - end - - private - - def _get_link(link_token) - get.call("links/#{link_token}") - end - - def _get_links - get.call('links') - end - - def build_link(data) - param = Utils.pick(data, 'link_token') - @default_params.update(param) - Fintoc::Movements::Link.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/movements/managers/links_manager.rb b/lib/fintoc/movements/managers/links_manager.rb new file mode 100644 index 0000000..da51335 --- /dev/null +++ b/lib/fintoc/movements/managers/links_manager.rb @@ -0,0 +1,46 @@ +require 'fintoc/movements/resources/link' + +module Fintoc + module Movements + module Managers + class LinksManager + def initialize(client) + @client = client + end + + def get(link_token) + data = { **_get_link(link_token), link_token: link_token } + build_link(data) + end + + def list + _get_links.map { |data| build_link(data) } + end + + def delete(link_id) + _delete_link(link_id) + end + + private + + def _get_link(link_token) + @client.get(version: :v1).call("links/#{link_token}") + end + + def _get_links + @client.get(version: :v1).call('links') + end + + def _delete_link(link_id) + @client.delete(version: :v1).call("links/#{link_id}") + end + + def build_link(data) + param = Utils.pick(data, 'link_token') + @client.default_params.update(param) + Fintoc::Movements::Link.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/movements/resources/account.rb b/lib/fintoc/movements/resources/account.rb index c76f1d9..32d8481 100644 --- a/lib/fintoc/movements/resources/account.rb +++ b/lib/fintoc/movements/resources/account.rb @@ -45,7 +45,9 @@ def update_balance end def get_movements(**params) - _get_movements(**params).lazy.map { |movement| Fintoc::Movements::Movement.new(**movement) } + _get_movements(**params).lazy.map do + |movement| Fintoc::Movements::Movement.new(**movement, client: @client) + end end def update_movements(**params) @@ -77,11 +79,11 @@ def to_s private def get_account - @client.get.call("accounts/#{@id}") + @client.get(version: :v1).call("accounts/#{@id}") end def _get_movements(**params) - first = @client.get.call("accounts/#{@id}/movements", **params) + first = @client.get(version: :v1).call("accounts/#{@id}/movements", **params) return first if params.empty? first + Utils.flatten(@client.fetch_next) diff --git a/lib/fintoc/movements/resources/link.rb b/lib/fintoc/movements/resources/link.rb index ed55d21..13326ac 100644 --- a/lib/fintoc/movements/resources/link.rb +++ b/lib/fintoc/movements/resources/link.rb @@ -33,7 +33,7 @@ def initialize( @accounts = if accounts.nil? [] else - accounts.map { |data| Fintoc::Movements::Account.new(**data, client: client) } + accounts.map { |data| Fintoc::Movements::Account.new(**data, client:) } end @token = link_token @client = client diff --git a/lib/fintoc/movements/resources/movement.rb b/lib/fintoc/movements/resources/movement.rb index 45bd93f..ce415b4 100644 --- a/lib/fintoc/movements/resources/movement.rb +++ b/lib/fintoc/movements/resources/movement.rb @@ -20,6 +20,7 @@ def initialize( recipient_account:, sender_account:, comment:, + client: nil, ** ) @id = id @@ -36,6 +37,7 @@ def initialize( end @sender_account = Fintoc::Movements::TransferAccount.new(**sender_account) if sender_account @comment = comment + @client = client end def ==(other) diff --git a/lib/fintoc/transfers/client/account_numbers_methods.rb b/lib/fintoc/transfers/client/account_numbers_methods.rb deleted file mode 100644 index 956b30b..0000000 --- a/lib/fintoc/transfers/client/account_numbers_methods.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'fintoc/transfers/resources/account_number' - -module Fintoc - module Transfers - module AccountNumbersMethods - def create_account_number(account_id:, description: nil, metadata: nil, **params) - data = _create_account_number(account_id:, description:, metadata:, **params) - build_account_number(data) - end - - def get_account_number(account_number_id) - data = _get_account_number(account_number_id) - build_account_number(data) - end - - def list_account_numbers(**params) - _list_account_numbers(**params).map { |data| build_account_number(data) } - end - - def update_account_number(account_number_id, **params) - data = _update_account_number(account_number_id, **params) - build_account_number(data) - end - - private - - def _create_account_number(account_id:, description: nil, metadata: nil, **params) - request_params = { account_id: } - request_params[:description] = description if description - request_params[:metadata] = metadata if metadata - request_params.merge!(params) - - post(version: :v2).call('account_numbers', **request_params) - end - - def _get_account_number(account_number_id) - get(version: :v2).call("account_numbers/#{account_number_id}") - end - - def _list_account_numbers(**params) - get(version: :v2).call('account_numbers', **params) - end - - def _update_account_number(account_number_id, **params) - patch(version: :v2).call("account_numbers/#{account_number_id}", **params) - end - - def build_account_number(data) - Fintoc::Transfers::AccountNumber.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/client/account_verifications_methods.rb b/lib/fintoc/transfers/client/account_verifications_methods.rb deleted file mode 100644 index 3f43e5b..0000000 --- a/lib/fintoc/transfers/client/account_verifications_methods.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'fintoc/transfers/resources/account_verification' - -module Fintoc - module Transfers - module AccountVerificationsMethods - def create_account_verification(account_number:) - data = _create_account_verification(account_number:) - build_account_verification(data) - end - - def get_account_verification(account_verification_id) - data = _get_account_verification(account_verification_id) - build_account_verification(data) - end - - def list_account_verifications(**params) - _list_account_verifications(**params).map { |data| build_account_verification(data) } - end - - private - - def _create_account_verification(account_number:) - post(version: :v2, use_jws: true).call('account_verifications', account_number:) - end - - def _get_account_verification(account_verification_id) - get(version: :v2).call("account_verifications/#{account_verification_id}") - end - - def _list_account_verifications(**params) - get(version: :v2).call('account_verifications', **params) - end - - def build_account_verification(data) - Fintoc::Transfers::AccountVerification.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/client/accounts_methods.rb b/lib/fintoc/transfers/client/accounts_methods.rb deleted file mode 100644 index ee9e683..0000000 --- a/lib/fintoc/transfers/client/accounts_methods.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'fintoc/transfers/resources/account' - -module Fintoc - module Transfers - module AccountsMethods - def create_account(entity_id:, description:, **params) - data = _create_account(entity_id:, description:, **params) - build_account(data) - end - - def get_account(account_id) - data = _get_account(account_id) - build_account(data) - end - - def list_accounts(**params) - _list_accounts(**params).map { |data| build_account(data) } - end - - def update_account(account_id, **params) - data = _update_account(account_id, **params) - build_account(data) - end - - private - - def _create_account(entity_id:, description:, **params) - post(version: :v2).call('accounts', entity_id:, description:, **params) - end - - def _get_account(account_id) - get(version: :v2).call("accounts/#{account_id}") - end - - def _list_accounts(**params) - get(version: :v2).call('accounts', **params) - end - - def _update_account(account_id, **params) - patch(version: :v2).call("accounts/#{account_id}", **params) - end - - def build_account(data) - Fintoc::Transfers::Account.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/transfers/client/client.rb index 2f0c817..cc5f5ea 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/transfers/client/client.rb @@ -1,20 +1,37 @@ require 'fintoc/base_client' -require 'fintoc/transfers/client/entities_methods' -require 'fintoc/transfers/client/accounts_methods' -require 'fintoc/transfers/client/account_numbers_methods' -require 'fintoc/transfers/client/transfers_methods' -require 'fintoc/transfers/client/simulation_methods' -require 'fintoc/transfers/client/account_verifications_methods' +require 'fintoc/transfers/managers/entities_manager' +require 'fintoc/transfers/managers/accounts_manager' +require 'fintoc/transfers/managers/account_numbers_manager' +require 'fintoc/transfers/managers/transfers_manager' +require 'fintoc/transfers/managers/simulate_manager' +require 'fintoc/transfers/managers/account_verifications_manager' module Fintoc module Transfers class Client < BaseClient - include EntitiesMethods - include AccountsMethods - include AccountNumbersMethods - include TransfersMethods - include SimulationMethods - include AccountVerificationsMethods + def entities + @entities ||= Managers::EntitiesManager.new(self) + end + + def accounts + @accounts ||= Managers::AccountsManager.new(self) + end + + def account_numbers + @account_numbers ||= Managers::AccountNumbersManager.new(self) + end + + def transfers + @transfers ||= Managers::TransfersManager.new(self) + end + + def simulate + @simulate ||= Managers::SimulateManager.new(self) + end + + def account_verifications + @account_verifications ||= Managers::AccountVerificationsManager.new(self) + end end end end diff --git a/lib/fintoc/transfers/client/entities_methods.rb b/lib/fintoc/transfers/client/entities_methods.rb deleted file mode 100644 index 5821508..0000000 --- a/lib/fintoc/transfers/client/entities_methods.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'fintoc/transfers/resources/entity' - -module Fintoc - module Transfers - module EntitiesMethods - def get_entity(entity_id) - data = _get_entity(entity_id) - build_entity(data) - end - - def get_entities(**params) - _get_entities(**params).map { |data| build_entity(data) } - end - - private - - def _get_entity(entity_id) - get(version: :v2).call("entities/#{entity_id}") - end - - def _get_entities(**params) - get(version: :v2).call('entities', **params) - end - - def build_entity(data) - Fintoc::Transfers::Entity.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/client/simulation_methods.rb b/lib/fintoc/transfers/client/simulation_methods.rb deleted file mode 100644 index 58d7b90..0000000 --- a/lib/fintoc/transfers/client/simulation_methods.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'fintoc/transfers/resources/transfer' - -module Fintoc - module Transfers - module SimulationMethods - def simulate_receive_transfer(account_number_id:, amount:, currency:) - data = _simulate_receive_transfer(account_number_id:, amount:, currency:) - build_transfer(data) - end - - private - - def _simulate_receive_transfer(account_number_id:, amount:, currency:) - post(version: :v2) - .call('simulate/receive_transfer', account_number_id:, amount:, currency:) - end - - def build_transfer(data) - Fintoc::Transfers::Transfer.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/client/transfers_methods.rb b/lib/fintoc/transfers/client/transfers_methods.rb deleted file mode 100644 index 7ab1e07..0000000 --- a/lib/fintoc/transfers/client/transfers_methods.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'fintoc/transfers/resources/transfer' - -module Fintoc - module Transfers - module TransfersMethods - def create_transfer(amount:, currency:, account_id:, counterparty:, **params) - data = _create_transfer(amount:, currency:, account_id:, counterparty:, **params) - build_transfer(data) - end - - def get_transfer(transfer_id) - data = _get_transfer(transfer_id) - build_transfer(data) - end - - def list_transfers(**params) - _list_transfers(**params).map { |data| build_transfer(data) } - end - - def return_transfer(transfer_id) - data = _return_transfer(transfer_id) - build_transfer(data) - end - - private - - def _create_transfer(amount:, currency:, account_id:, counterparty:, **params) - post(version: :v2, use_jws: true) - .call('transfers', amount:, currency:, account_id:, counterparty:, **params) - end - - def _get_transfer(transfer_id) - get(version: :v2).call("transfers/#{transfer_id}") - end - - def _list_transfers(**params) - get(version: :v2).call('transfers', **params) - end - - def _return_transfer(transfer_id) - post(version: :v2, use_jws: true).call('transfers/return', transfer_id:) - end - - def build_transfer(data) - Fintoc::Transfers::Transfer.new(**data, client: self) - end - end - end -end diff --git a/lib/fintoc/transfers/managers/account_numbers_manager.rb b/lib/fintoc/transfers/managers/account_numbers_manager.rb new file mode 100644 index 0000000..3370874 --- /dev/null +++ b/lib/fintoc/transfers/managers/account_numbers_manager.rb @@ -0,0 +1,59 @@ +require 'fintoc/transfers/resources/account_number' + +module Fintoc + module Transfers + module Managers + class AccountNumbersManager + def initialize(client) + @client = client + end + + def create(account_id:, description: nil, metadata: nil, **params) + data = _create_account_number(account_id:, description:, metadata:, **params) + build_account_number(data) + end + + def get(account_number_id) + data = _get_account_number(account_number_id) + build_account_number(data) + end + + def list(**params) + _list_account_numbers(**params).map { |data| build_account_number(data) } + end + + def update(account_number_id, **params) + data = _update_account_number(account_number_id, **params) + build_account_number(data) + end + + private + + def _create_account_number(account_id:, description: nil, metadata: nil, **params) + request_params = { account_id: } + request_params[:description] = description if description + request_params[:metadata] = metadata if metadata + request_params.merge!(params) + + @client.post(version: :v2).call('account_numbers', **request_params) + end + + def _get_account_number(account_number_id) + @client.get(version: :v2).call("account_numbers/#{account_number_id}") + end + + def _list_account_numbers(**params) + @client.get(version: :v2).call('account_numbers', **params) + end + + def _update_account_number(account_number_id, **params) + @client.patch(version: :v2).call("account_numbers/#{account_number_id}", **params) + end + + def build_account_number(data) + Fintoc::Transfers::AccountNumber.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/managers/account_verifications_manager.rb b/lib/fintoc/transfers/managers/account_verifications_manager.rb new file mode 100644 index 0000000..70d275c --- /dev/null +++ b/lib/fintoc/transfers/managers/account_verifications_manager.rb @@ -0,0 +1,45 @@ +require 'fintoc/transfers/resources/account_verification' + +module Fintoc + module Transfers + module Managers + class AccountVerificationsManager + def initialize(client) + @client = client + end + + def create(account_number:) + data = _create_account_verification(account_number:) + build_account_verification(data) + end + + def get(account_verification_id) + data = _get_account_verification(account_verification_id) + build_account_verification(data) + end + + def list(**params) + _list_account_verifications(**params).map { |data| build_account_verification(data) } + end + + private + + def _create_account_verification(account_number:) + @client.post(version: :v2, use_jws: true).call('account_verifications', account_number:) + end + + def _get_account_verification(account_verification_id) + @client.get(version: :v2).call("account_verifications/#{account_verification_id}") + end + + def _list_account_verifications(**params) + @client.get(version: :v2).call('account_verifications', **params) + end + + def build_account_verification(data) + Fintoc::Transfers::AccountVerification.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/managers/accounts_manager.rb b/lib/fintoc/transfers/managers/accounts_manager.rb new file mode 100644 index 0000000..08b1f73 --- /dev/null +++ b/lib/fintoc/transfers/managers/accounts_manager.rb @@ -0,0 +1,54 @@ +require 'fintoc/transfers/resources/account' + +module Fintoc + module Transfers + module Managers + class AccountsManager + def initialize(client) + @client = client + end + + def create(entity_id:, description:, **params) + data = _create_account(entity_id:, description:, **params) + build_account(data) + end + + def get(account_id) + data = _get_account(account_id) + build_account(data) + end + + def list(**params) + _list_accounts(**params).map { |data| build_account(data) } + end + + def update(account_id, **params) + data = _update_account(account_id, **params) + build_account(data) + end + + private + + def _create_account(entity_id:, description:, **params) + @client.post(version: :v2).call('accounts', entity_id:, description:, **params) + end + + def _get_account(account_id) + @client.get(version: :v2).call("accounts/#{account_id}") + end + + def _list_accounts(**params) + @client.get(version: :v2).call('accounts', **params) + end + + def _update_account(account_id, **params) + @client.patch(version: :v2).call("accounts/#{account_id}", **params) + end + + def build_account(data) + Fintoc::Transfers::Account.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/managers/entities_manager.rb b/lib/fintoc/transfers/managers/entities_manager.rb new file mode 100644 index 0000000..f45dc14 --- /dev/null +++ b/lib/fintoc/transfers/managers/entities_manager.rb @@ -0,0 +1,36 @@ +require 'fintoc/transfers/resources/entity' + +module Fintoc + module Transfers + module Managers + class EntitiesManager + def initialize(client) + @client = client + end + + def get(entity_id) + data = _get_entity(entity_id) + build_entity(data) + end + + def list(**params) + _list_entities(**params).map { |data| build_entity(data) } + end + + private + + def _get_entity(entity_id) + @client.get(version: :v2).call("entities/#{entity_id}") + end + + def _list_entities(**params) + @client.get(version: :v2).call('entities', **params) + end + + def build_entity(data) + Fintoc::Transfers::Entity.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/managers/simulate_manager.rb b/lib/fintoc/transfers/managers/simulate_manager.rb new file mode 100644 index 0000000..24558d8 --- /dev/null +++ b/lib/fintoc/transfers/managers/simulate_manager.rb @@ -0,0 +1,30 @@ +require 'fintoc/transfers/resources/transfer' + +module Fintoc + module Transfers + module Managers + class SimulateManager + def initialize(client) + @client = client + end + + def receive_transfer(account_number_id:, amount:, currency:) + data = _simulate_receive_transfer(account_number_id:, amount:, currency:) + build_transfer(data) + end + + private + + def _simulate_receive_transfer(account_number_id:, amount:, currency:) + @client + .post(version: :v2) + .call('simulate/receive_transfer', account_number_id:, amount:, currency:) + end + + def build_transfer(data) + Fintoc::Transfers::Transfer.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/managers/transfers_manager.rb b/lib/fintoc/transfers/managers/transfers_manager.rb new file mode 100644 index 0000000..e38cfcb --- /dev/null +++ b/lib/fintoc/transfers/managers/transfers_manager.rb @@ -0,0 +1,56 @@ +require 'fintoc/transfers/resources/transfer' + +module Fintoc + module Transfers + module Managers + class TransfersManager + def initialize(client) + @client = client + end + + def create(amount:, currency:, account_id:, counterparty:, **params) + data = _create_transfer(amount:, currency:, account_id:, counterparty:, **params) + build_transfer(data) + end + + def get(transfer_id) + data = _get_transfer(transfer_id) + build_transfer(data) + end + + def list(**params) + _list_transfers(**params).map { |data| build_transfer(data) } + end + + def return(transfer_id) + data = _return_transfer(transfer_id) + build_transfer(data) + end + + private + + def _create_transfer(amount:, currency:, account_id:, counterparty:, **params) + @client + .post(version: :v2, use_jws: true) + .call('transfers', amount:, currency:, account_id:, counterparty:, **params) + end + + def _get_transfer(transfer_id) + @client.get(version: :v2).call("transfers/#{transfer_id}") + end + + def _list_transfers(**params) + @client.get(version: :v2).call('transfers', **params) + end + + def _return_transfer(transfer_id) + @client.post(version: :v2, use_jws: true).call('transfers/return', transfer_id:) + end + + def build_transfer(data) + Fintoc::Transfers::Transfer.new(**data, client: @client) + end + end + end + end +end diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/transfers/resources/account.rb index 00094a3..b247f91 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/transfers/resources/account.rb @@ -40,7 +40,7 @@ def to_s end def refresh - fresh_account = @client.get_account(@id) + fresh_account = @client.accounts.get(@id) refresh_from_account(fresh_account) end @@ -48,7 +48,7 @@ def update(description: nil) params = {} params[:description] = description if description - updated_account = @client.update_account(@id, **params) + updated_account = @client.accounts.update(@id, **params) refresh_from_account(updated_account) end @@ -73,7 +73,7 @@ def simulate_receive_transfer(amount:) raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode' end - @client.simulate_receive_transfer( + @client.simulate.receive_transfer( account_number_id: @root_account_number_id, amount:, currency: @currency diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/transfers/resources/account_number.rb index 82aff76..99a9992 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/transfers/resources/account_number.rb @@ -38,7 +38,7 @@ def to_s end def refresh - fresh_account_number = @client.get_account_number(@id) + fresh_account_number = @client.account_numbers.get(@id) refresh_from_account_number(fresh_account_number) end @@ -48,7 +48,7 @@ def update(description: nil, status: nil, metadata: nil) params[:status] = status if status params[:metadata] = metadata if metadata - updated_account_number = @client.update_account_number(@id, **params) + updated_account_number = @client.account_numbers.update(@id, **params) refresh_from_account_number(updated_account_number) end @@ -73,7 +73,7 @@ def simulate_receive_transfer(amount:, currency: 'MXN') raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode' end - @client.simulate_receive_transfer( + @client.simulate.receive_transfer( account_number_id: @id, amount:, currency: diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/transfers/resources/account_verification.rb index 1881411..8e62ceb 100644 --- a/lib/fintoc/transfers/resources/account_verification.rb +++ b/lib/fintoc/transfers/resources/account_verification.rb @@ -34,7 +34,7 @@ def to_s end def refresh - fresh_verification = @client.get_account_verification(@id) + fresh_verification = @client.account_verifications.get(@id) refresh_from_verification(fresh_verification) end diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/transfers/resources/entity.rb index 88cebc5..ab308bf 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/transfers/resources/entity.rb @@ -27,7 +27,7 @@ def to_s end def refresh - fresh_entity = @client.get_entity(@id) + fresh_entity = @client.entities.get(@id) refresh_from_entity(fresh_entity) end diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/transfers/resources/transfer.rb index c0f525c..81ef72d 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/transfers/resources/transfer.rb @@ -58,12 +58,12 @@ def to_s end def refresh - fresh_transfer = @client.get_transfer(@id) + fresh_transfer = @client.transfers.get(@id) refresh_from_transfer(fresh_transfer) end def return_transfer - returned_transfer = @client.return_transfer(@id) + returned_transfer = @client.transfers.return(@id) refresh_from_transfer(returned_transfer) end diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 169ca46..bb40b27 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -20,17 +20,16 @@ describe 'client separation' do it 'allows direct access to movements client' do - expect(client.movements) - .to respond_to(:get_link) - .and respond_to(:get_links) - .and respond_to(:delete_link) - .and respond_to(:get_account) + expect(client.movements.links) + .to respond_to(:get) + .and respond_to(:list) + .and respond_to(:delete) end it 'allows direct access to transfers client' do - expect(client.transfers) - .to respond_to(:get_entity) - .and respond_to(:get_entities) + expect(client.transfers.entities) + .to respond_to(:get) + .and respond_to(:list) end it 'maintains backward compatibility through delegation' do @@ -39,37 +38,25 @@ .and respond_to(:get_links) .and respond_to(:delete_link) .and respond_to(:get_account) - .and respond_to(:get_entity) - .and respond_to(:get_entities) end end describe 'delegation to movements client' do + let(:link) { instance_double(Fintoc::Movements::Link) } + let(:account) { instance_double(Fintoc::Movements::Account) } + before do - allow(client.movements).to receive(:get_link).with('token').and_return('link') - allow(client.movements).to receive(:get_links).and_return(['links']) - allow(client.movements).to receive(:delete_link).with('link_id').and_return(true) - allow(client.movements) - .to receive(:get_account).with('token', 'account_id').and_return('account') + allow(client.movements.links).to receive(:get).with('token').and_return(link) + allow(client.movements.links).to receive(:list).and_return([link]) + allow(client.movements.links).to receive(:delete).with('link_id').and_return(true) + allow(link).to receive(:find).with(id: 'account_id').and_return(account) end it 'delegates movements methods to movements client' do - expect(client.get_link('token')).to eq('link') - expect(client.get_links).to eq(['links']) + expect(client.get_link('token')).to eq(link) + expect(client.get_links).to eq([link]) expect(client.delete_link('link_id')).to be(true) - expect(client.get_account('token', 'account_id')).to eq('account') - end - end - - describe 'delegation to transfers client' do - before do - allow(client.transfers).to receive(:get_entity).with('entity_id').and_return('entity') - allow(client.transfers).to receive(:get_entities).with(limit: 10).and_return(['entities']) - end - - it 'delegates transfers methods to transfers client' do - expect(client.get_entity('entity_id')).to eq('entity') - expect(client.get_entities(limit: 10)).to eq(['entities']) + expect(client.get_account('token', 'account_id')).to eq(account) end end end diff --git a/spec/lib/fintoc/movements/account_spec.rb b/spec/lib/fintoc/movements/account_spec.rb index a900edd..fb4b85f 100644 --- a/spec/lib/fintoc/movements/account_spec.rb +++ b/spec/lib/fintoc/movements/account_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Fintoc::Movements::Account do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { Fintoc::Client.new(api_key) } + let(:client) { Fintoc::Movements::Client.new(api_key) } let(:data) do { @@ -25,7 +25,7 @@ end let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } - let(:link) { client.get_link(link_token) } + let(:link) { client.links.get(link_token) } let(:account) { described_class.new(**data) } let(:linked_account) { link.find(type: 'checking_account') } diff --git a/spec/lib/fintoc/movements/client_spec.rb b/spec/lib/fintoc/movements/client_spec.rb index d2a4026..649336c 100644 --- a/spec/lib/fintoc/movements/client_spec.rb +++ b/spec/lib/fintoc/movements/client_spec.rb @@ -19,12 +19,11 @@ end it 'responds to movements-specific methods' do - expect(client) - .to respond_to(:get_link) - .and respond_to(:get_links) - .and respond_to(:delete_link) - .and respond_to(:get_account) + expect(client.links) + .to respond_to(:get) + .and respond_to(:list) + .and respond_to(:delete) end - it_behaves_like 'a client with links methods' + it_behaves_like 'a client with links manager' end diff --git a/spec/lib/fintoc/movements/links_methods_spec.rb b/spec/lib/fintoc/movements/links_methods_spec.rb deleted file mode 100644 index 16b187b..0000000 --- a/spec/lib/fintoc/movements/links_methods_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'fintoc/movements/client/links_methods' - -RSpec.describe Fintoc::Movements::LinksMethods do - # Test class that includes the module for testing - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Movements::LinksMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def get(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def delete - proc { |_resource, **_kwargs| { mock: 'deleted' } } - end - - def pick(data, key) - { key => data[key] } if data[key] - end - end - end - - describe 'module inclusion' do - it 'provides link-related methods' do - expect(client) - .to respond_to(:get_link) - .and respond_to(:get_links) - .and respond_to(:delete_link) - .and respond_to(:get_account) - end - end - - describe 'private methods' do - it 'provides private helper methods' do - expect(client.private_methods) - .to include(:_get_link, :_get_links, :build_link) - end - end -end diff --git a/spec/lib/fintoc/movements/managers/links_manager_spec.rb b/spec/lib/fintoc/movements/managers/links_manager_spec.rb new file mode 100644 index 0000000..0360931 --- /dev/null +++ b/spec/lib/fintoc/movements/managers/links_manager_spec.rb @@ -0,0 +1,77 @@ +require 'fintoc/movements/managers/links_manager' + +RSpec.describe Fintoc::Movements::Managers::LinksManager do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { Fintoc::Movements::Client.new(api_key) } + let(:get_proc) { instance_double(Proc) } + let(:post_proc) { instance_double(Proc) } + let(:delete_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:link_id) { 'link_123' } + let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } + let(:first_link_data) do + { + id: link_id, + object: 'link', + link_token: link_token + } + end + let(:second_link_data) do + { + id: 'link_456', + object: 'link', + link_token: link_token + } + end + + before do + allow(client).to receive(:get).with(version: :v1).and_return(get_proc) + allow(client).to receive(:post).with(version: :v1).and_return(post_proc) + allow(client).to receive(:delete).with(version: :v1).and_return(delete_proc) + + allow(get_proc) + .to receive(:call) + .with("links/#{link_token}") + .and_return(first_link_data) + allow(get_proc) + .to receive(:call) + .with('links') + .and_return([first_link_data, second_link_data]) + allow(post_proc) + .to receive(:call) + .with('links', link_token:) + .and_return(first_link_data) + allow(delete_proc) + .to receive(:call) + .with("links/#{link_id}") + .and_return(true) + + allow(Fintoc::Movements::Link).to receive(:new) + end + + describe '#links' do + describe '#get' do + it 'calls build_link with the response' do + manager.get(link_token) + expect(Fintoc::Movements::Link) + .to have_received(:new).with(**first_link_data, client:) + end + end + + describe '#list' do + it 'calls build_link with the response' do + manager.list + expect(Fintoc::Movements::Link) + .to have_received(:new).with(**first_link_data, client:) + expect(Fintoc::Movements::Link) + .to have_received(:new).with(**second_link_data, client:) + end + end + + describe '#delete' do + it 'calls build_link with the response' do + expect(manager.delete(link_id)).to be true + end + end + end +end diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/transfers/account_number_spec.rb index 3ef5403..8b164fb 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/transfers/account_number_spec.rb @@ -111,8 +111,8 @@ let(:refreshed_account_number) { described_class.new(**refreshed_data) } before do - allow(client) - .to receive(:get_account_number) + allow(client.account_numbers) + .to receive(:get) .with('acno_Kasf91034gj1AD') .and_return(refreshed_account_number) end @@ -124,14 +124,14 @@ it 'calls get_account_number with the correct id' do account_number.refresh - expect(client).to have_received(:get_account_number).with('acno_Kasf91034gj1AD') + expect(client.account_numbers).to have_received(:get).with('acno_Kasf91034gj1AD') end it 'raises an error if the account number ID does not match' do wrong_account_number = described_class.new(**data, id: 'wrong_id') - allow(client) - .to receive(:get_account_number) + allow(client.account_numbers) + .to receive(:get) .with('acno_Kasf91034gj1AD') .and_return(wrong_account_number) @@ -150,7 +150,7 @@ let(:updated_account_number) { described_class.new(**updated_data) } before do - allow(client).to receive(:update_account_number).and_return(updated_account_number) + allow(client.account_numbers).to receive(:update).and_return(updated_account_number) end it 'updates all provided parameters' do @@ -166,7 +166,7 @@ account_number .update(description: new_description, status: new_status, metadata: new_metadata) - expect(client).to have_received(:update_account_number).with( + expect(client.account_numbers).to have_received(:update).with( account_number.id, description: new_description, status: new_status, @@ -191,8 +191,8 @@ context 'when in test mode' do before do - allow(client) - .to receive(:simulate_receive_transfer) + allow(client.simulate) + .to receive(:receive_transfer) .with(account_number_id: account_number.id, amount: 10000, currency: 'MXN') .and_return(expected_transfer) end diff --git a/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb b/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb deleted file mode 100644 index a297712..0000000 --- a/spec/lib/fintoc/transfers/account_numbers_methods_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'fintoc/transfers/client/account_numbers_methods' - -RSpec.describe Fintoc::Transfers::AccountNumbersMethods do - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Transfers::AccountNumbersMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def get(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def post(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def patch(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - end - end - - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - - describe '#create_account_number' do - before do - allow(Fintoc::Transfers::AccountNumber).to receive(:new) - end - - it 'calls build_account_number with the response' do - client.create_account_number(account_id: 'acc_123') - expect(Fintoc::Transfers::AccountNumber) - .to have_received(:new).with(mock: 'response', client:) - end - end - - describe '#get_account_number' do - before do - allow(Fintoc::Transfers::AccountNumber).to receive(:new) - end - - it 'calls build_account_number with the response' do - client.get_account_number('acno_123') - expect(Fintoc::Transfers::AccountNumber) - .to have_received(:new).with(mock: 'response', client:) - end - end - - describe '#list_account_numbers' do - before do - allow(client) - .to receive(:_list_account_numbers) - .and_return([{ mock: 'response1' }, { mock: 'response2' }]) - allow(Fintoc::Transfers::AccountNumber).to receive(:new) - end - - it 'calls build_account_number for each response' do - client.list_account_numbers - expect(Fintoc::Transfers::AccountNumber) - .to have_received(:new).with(mock: 'response1', client:) - expect(Fintoc::Transfers::AccountNumber) - .to have_received(:new).with(mock: 'response2', client:) - end - end - - describe '#update_account_number' do - before do - allow(Fintoc::Transfers::AccountNumber).to receive(:new) - end - - it 'calls build_account_number with the response' do - client.update_account_number('acno_123', description: 'Updated') - expect(Fintoc::Transfers::AccountNumber) - .to have_received(:new).with(mock: 'response', client:) - end - end -end diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/transfers/account_spec.rb index c06d4ba..f71ac6f 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/transfers/account_spec.rb @@ -112,7 +112,7 @@ let(:updated_account) { described_class.new(**updated_data, client: client) } before do - allow(client).to receive(:get_account).with('acc_123').and_return(updated_account) + allow(client.accounts).to receive(:get).with('acc_123').and_return(updated_account) end it 'refreshes the account with updated data from the API' do @@ -120,7 +120,7 @@ account.refresh - expect(client).to have_received(:get_account).with('acc_123') + expect(client.accounts).to have_received(:get).with('acc_123') expect(account.description).to eq('Updated account description') end @@ -128,7 +128,7 @@ it 'raises an error if the account ID does not match' do wrong_account = described_class.new(**data, id: 'wrong_id') - allow(client).to receive(:get_account).with('acc_123').and_return(wrong_account) + allow(client.accounts).to receive(:get).with('acc_123').and_return(wrong_account) expect { account.refresh }.to raise_error(ArgumentError, 'Account must be the same instance') end @@ -140,7 +140,7 @@ let(:updated_account) { described_class.new(**updated_data, client: client) } before do - allow(client).to receive(:update_account) do |_id, params| + allow(client.accounts).to receive(:update) do |_id, params| updated_data_for_call = { **data, **params } described_class.new(**updated_data_for_call, client: client) end @@ -151,8 +151,8 @@ account.update(description: 'New account description') - expect(client) - .to have_received(:update_account) + expect(client.accounts) + .to have_received(:update) .with('acc_123', description: 'New account description') expect(account.description).to eq('New account description') @@ -160,8 +160,8 @@ it 'only sends provided parameters' do account.update(description: 'Test description') - expect(client) - .to have_received(:update_account) + expect(client.accounts) + .to have_received(:update) .with('acc_123', description: 'Test description') end end @@ -182,8 +182,8 @@ context 'when in test mode' do before do - allow(client) - .to receive(:simulate_receive_transfer) + allow(client.simulate) + .to receive(:receive_transfer) .with( account_number_id: account.root_account_number_id, amount: 10000, diff --git a/spec/lib/fintoc/transfers/account_verification_spec.rb b/spec/lib/fintoc/transfers/account_verification_spec.rb index 97f4148..d865f66 100644 --- a/spec/lib/fintoc/transfers/account_verification_spec.rb +++ b/spec/lib/fintoc/transfers/account_verification_spec.rb @@ -121,8 +121,8 @@ let(:fresh_verification) { described_class.new(**fresh_data) } before do - allow(client) - .to receive(:get_account_verification) + allow(client.account_verifications) + .to receive(:get) .with('accv_fdme30s11j5k7l1mekq4') .and_return(fresh_verification) end @@ -140,8 +140,8 @@ it 'raises an error if the verification ID does not match' do wrong_verification = described_class.new(**fresh_data, id: 'wrong_id') - allow(client) - .to receive(:get_account_verification) + allow(client.account_verifications) + .to receive(:get) .with('accv_fdme30s11j5k7l1mekq4') .and_return(wrong_verification) diff --git a/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb b/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb deleted file mode 100644 index 5b172a6..0000000 --- a/spec/lib/fintoc/transfers/account_verifications_methods_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'fintoc/transfers/client/account_verifications_methods' - -RSpec.describe Fintoc::Transfers::AccountVerificationsMethods do - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Transfers::AccountVerificationsMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def get(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def post(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - end - end - - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - - describe '#create_account_verification' do - before do - allow(Fintoc::Transfers::AccountVerification).to receive(:new) - end - - it 'calls build_account_verification with the response' do - client.create_account_verification(account_number: '735969000000203226') - expect(Fintoc::Transfers::AccountVerification) - .to have_received(:new).with(mock: 'response', client:) - end - end - - describe '#get_account_verification' do - before do - allow(Fintoc::Transfers::AccountVerification).to receive(:new) - end - - it 'calls build_account_verification with the response' do - client.get_account_verification('accv_123') - expect(Fintoc::Transfers::AccountVerification) - .to have_received(:new).with(mock: 'response', client:) - end - end - - describe '#list_account_verifications' do - before do - allow(client) - .to receive(:_list_account_verifications) - .and_return([{ mock: 'response1' }, { mock: 'response2' }]) - allow(Fintoc::Transfers::AccountVerification).to receive(:new) - end - - it 'calls build_account_verification for each response item' do - client.list_account_verifications - expect(Fintoc::Transfers::AccountVerification) - .to have_received(:new).with(mock: 'response1', client:) - expect(Fintoc::Transfers::AccountVerification) - .to have_received(:new).with(mock: 'response2', client:) - end - end -end diff --git a/spec/lib/fintoc/transfers/accounts_methods_spec.rb b/spec/lib/fintoc/transfers/accounts_methods_spec.rb deleted file mode 100644 index b244b66..0000000 --- a/spec/lib/fintoc/transfers/accounts_methods_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'fintoc/transfers/client/accounts_methods' - -RSpec.describe Fintoc::Transfers::AccountsMethods do - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Transfers::AccountsMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def get(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def post(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - - def patch(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - end - end - - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - - describe 'module inclusion' do - it 'provides account-related methods' do - expect(client) - .to respond_to(:create_account) - .and respond_to(:get_account) - .and respond_to(:list_accounts) - .and respond_to(:update_account) - end - end - - describe 'private methods' do - it 'provides private helper methods' do - expect(client.private_methods) - .to include( - :_create_account, :_get_account, :_list_accounts, :_update_account, :build_account - ) - end - end -end diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/transfers/client_spec.rb index 44775ad..5002069 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/transfers/client_spec.rb @@ -19,15 +19,15 @@ end end - it_behaves_like 'a client with entities methods' + it_behaves_like 'a client with entities manager' - it_behaves_like 'a client with accounts methods' + it_behaves_like 'a client with accounts manager' - it_behaves_like 'a client with account numbers methods' + it_behaves_like 'a client with account numbers manager' - it_behaves_like 'a client with transfers methods' + it_behaves_like 'a client with transfers manager' - it_behaves_like 'a client with simulation methods' + it_behaves_like 'a client with simulate manager' - it_behaves_like 'a client with account verifications methods' + it_behaves_like 'a client with account verifications manager' end diff --git a/spec/lib/fintoc/transfers/entities_methods_spec.rb b/spec/lib/fintoc/transfers/entities_methods_spec.rb deleted file mode 100644 index 2fe143a..0000000 --- a/spec/lib/fintoc/transfers/entities_methods_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'fintoc/transfers/client/entities_methods' - -RSpec.describe Fintoc::Transfers::EntitiesMethods do - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Transfers::EntitiesMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def get(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - end - end - - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - - describe 'module inclusion' do - it 'provides entity-related methods' do - expect(client) - .to respond_to(:get_entity) - .and respond_to(:get_entities) - end - end - - describe 'private methods' do - it 'provides private helper methods' do - expect(client.private_methods) - .to include(:_get_entity, :_get_entities, :build_entity) - end - end -end diff --git a/spec/lib/fintoc/transfers/entity_spec.rb b/spec/lib/fintoc/transfers/entity_spec.rb index f2cddc0..59ddc6d 100644 --- a/spec/lib/fintoc/transfers/entity_spec.rb +++ b/spec/lib/fintoc/transfers/entity_spec.rb @@ -52,7 +52,7 @@ let(:updated_entity) { described_class.new(**updated_data, client: client) } before do - allow(client).to receive(:get_entity).with('ent_12345').and_return(updated_entity) + allow(client.entities).to receive(:get).with('ent_12345').and_return(updated_entity) end it 'refreshes the entity with updated data from the API' do @@ -62,7 +62,7 @@ entity.refresh - expect(client).to have_received(:get_entity).with('ent_12345') + expect(client.entities).to have_received(:get).with('ent_12345') expect(entity).to have_attributes( holder_name: 'Updated Company LLC' @@ -72,7 +72,7 @@ it 'raises an error if the entity ID does not match' do wrong_entity = described_class.new(**data, id: 'wrong_id') - allow(client).to receive(:get_entity).with('ent_12345').and_return(wrong_entity) + allow(client.entities).to receive(:get).with('ent_12345').and_return(wrong_entity) expect { entity.refresh }.to raise_error(ArgumentError, 'Entity must be the same instance') end diff --git a/spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb b/spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb new file mode 100644 index 0000000..353dc8c --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb @@ -0,0 +1,103 @@ +require 'fintoc/transfers/managers/account_numbers_manager' + +RSpec.describe Fintoc::Transfers::Managers::AccountNumbersManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:get_proc) { instance_double(Proc) } + let(:post_proc) { instance_double(Proc) } + let(:patch_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:account_number_id) { 'acno_123' } + let(:account_id) { 'acc_123' } + let(:first_account_number_data) do + { + id: account_number_id, + object: 'account_number', + description: 'My account number', + number: '1234567890', + created_at: '2021-01-01', + updated_at: '2021-01-01', + mode: 'test', + status: 'enabled', + is_root: false, + account_id: account_id, + metadata: {} + } + end + let(:second_account_number_data) do + { + id: 'acno_456', + object: 'account_number', + description: 'My second account number', + number: '0987654321', + created_at: '2021-01-02', + updated_at: '2021-01-02', + mode: 'test', + status: 'enabled', + is_root: false, + account_id: account_id, + metadata: {} + } + end + let(:updated_account_number_data) do + first_account_number_data.merge(description: 'Updated description') + end + + before do + allow(client).to receive(:get).with(version: :v2).and_return(get_proc) + allow(client).to receive(:post).with(version: :v2).and_return(post_proc) + allow(client).to receive(:patch).with(version: :v2).and_return(patch_proc) + + allow(get_proc) + .to receive(:call) + .with("account_numbers/#{account_number_id}") + .and_return(first_account_number_data) + allow(get_proc) + .to receive(:call) + .with('account_numbers') + .and_return([first_account_number_data, second_account_number_data]) + allow(patch_proc) + .to receive(:call) + .with("account_numbers/#{account_number_id}", description: 'Updated description') + .and_return(updated_account_number_data) + allow(post_proc) + .to receive(:call) + .with('account_numbers', account_id:, description: 'My account number', metadata: {}) + .and_return(first_account_number_data) + + allow(Fintoc::Transfers::AccountNumber).to receive(:new) + end + + describe '#create' do + it 'calls build_account_number with the response' do + manager.create(account_id: 'acc_123', description: 'My account number', metadata: {}) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(**first_account_number_data, client:) + end + end + + describe '#get' do + it 'calls build_account_number with the response' do + manager.get('acno_123') + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(**first_account_number_data, client:) + end + end + + describe '#list' do + it 'calls build_account_number for each response' do + manager.list + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(**first_account_number_data, client:) + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(**second_account_number_data, client:) + end + end + + describe '#update' do + it 'calls build_account_number with the response' do + manager.update('acno_123', description: 'Updated description') + expect(Fintoc::Transfers::AccountNumber) + .to have_received(:new).with(**updated_account_number_data, client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb b/spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb new file mode 100644 index 0000000..4befd11 --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb @@ -0,0 +1,72 @@ +require 'fintoc/transfers/managers/account_verifications_manager' + +RSpec.describe Fintoc::Transfers::Managers::AccountVerificationsManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:get_proc) { instance_double(Proc) } + let(:post_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:account_verification_id) { 'accv_123' } + let(:account_number) { '735969000000203226' } + let(:first_account_verification_data) do + { + id: account_verification_id, + object: 'account_verification', + account_number: account_number, + status: 'pending' + } + end + let(:second_account_verification_data) do + { + id: 'accv_456', + object: 'account_verification', + account_number: account_number, + status: 'pending' + } + end + + before do + allow(client).to receive(:get).with(version: :v2).and_return(get_proc) + allow(client).to receive(:post).with(version: :v2, use_jws: true).and_return(post_proc) + + allow(get_proc) + .to receive(:call) + .with("account_verifications/#{account_verification_id}") + .and_return(first_account_verification_data) + allow(get_proc) + .to receive(:call) + .with('account_verifications') + .and_return([first_account_verification_data, second_account_verification_data]) + allow(post_proc) + .to receive(:call) + .with('account_verifications', account_number:) + .and_return(first_account_verification_data) + + allow(Fintoc::Transfers::AccountVerification).to receive(:new) + end + + describe '#create' do + it 'calls build_account_verification with the response' do + manager.create(account_number: '735969000000203226') + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(**first_account_verification_data, client:) + end + end + + describe '#get' do + it 'calls build_account_verification with the response' do + manager.get('accv_123') + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(**first_account_verification_data, client:) + end + end + + describe '#list' do + it 'calls build_account_verification for each response item' do + manager.list + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(**first_account_verification_data, client:) + expect(Fintoc::Transfers::AccountVerification) + .to have_received(:new).with(**second_account_verification_data, client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb b/spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb new file mode 100644 index 0000000..205ade7 --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb @@ -0,0 +1,111 @@ +require 'fintoc/transfers/managers/accounts_manager' + +RSpec.describe Fintoc::Transfers::Managers::AccountsManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:get_proc) { instance_double(Proc) } + let(:post_proc) { instance_double(Proc) } + let(:patch_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:account_id) { 'acc_123' } + let(:entity_id) { 'ent_123' } + let(:first_account_data) do + { + id: account_id, + name: 'My account', + official_name: 'My account', + number: '1234567890', + holder_id: '1234567890', + holder_name: 'My account', + type: 'checking', + currency: 'MXN', + refreshed_at: '2021-01-01', + balance: { + available: 100000, + current: 100000, + limit: 0 + }, + movements: [] + } + end + let(:second_account_data) do + { + id: 'acc_456', + name: 'My second account', + official_name: 'My second account', + number: '0987654321', + holder_id: '1234567890', + holder_name: 'My account', + type: 'checking', + currency: 'MXN', + refreshed_at: '2021-01-01', + balance: { + available: 100000, + current: 100000, + limit: 0 + }, + movements: [] + } + end + let(:updated_account_data) do + first_account_data.merge(name: 'Updated name') + end + + before do + allow(client).to receive(:get).with(version: :v2).and_return(get_proc) + allow(client).to receive(:post).with(version: :v2).and_return(post_proc) + allow(client).to receive(:patch).with(version: :v2).and_return(patch_proc) + + allow(get_proc) + .to receive(:call) + .with("accounts/#{account_id}") + .and_return(first_account_data) + allow(get_proc) + .to receive(:call) + .with('accounts') + .and_return([first_account_data, second_account_data]) + allow(patch_proc) + .to receive(:call) + .with("accounts/#{account_id}", name: 'Updated name') + .and_return(updated_account_data) + allow(post_proc) + .to receive(:call) + .with('accounts', entity_id:, description: 'My account') + .and_return(first_account_data) + + allow(Fintoc::Transfers::Account).to receive(:new) + end + + describe '#create' do + it 'calls build_account with the response' do + manager.create(entity_id:, description: 'My account') + expect(Fintoc::Transfers::Account) + .to have_received(:new).with(**first_account_data, client:) + end + end + + describe '#get' do + it 'calls build_account with the response' do + manager.get(account_id) + expect(Fintoc::Transfers::Account) + .to have_received(:new).with(**first_account_data, client:) + end + end + + describe '#list' do + it 'calls build_account for each response' do + manager.list + expect(Fintoc::Transfers::Account) + .to have_received(:new).with(**first_account_data, client:) + expect(Fintoc::Transfers::Account) + .to have_received(:new).with(**second_account_data, client:) + end + end + + describe '#update' do + it 'calls build_account with the response' do + manager.update(account_id, name: 'Updated name') + expect(Fintoc::Transfers::Account) + .to have_received(:new).with(**updated_account_data, client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/managers/entities_manager_spec.rb b/spec/lib/fintoc/transfers/managers/entities_manager_spec.rb new file mode 100644 index 0000000..db46739 --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/entities_manager_spec.rb @@ -0,0 +1,68 @@ +require 'fintoc/transfers/managers/entities_manager' + +RSpec.describe Fintoc::Transfers::Managers::EntitiesManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:get_proc) { instance_double(Proc) } + + let(:manager) { described_class.new(client) } + let(:entity_id) { 'ent_31t0VhhrAXASFQTVYfCfIBnljbT' } + + let(:first_entity_data) do + { + object: 'entity', + mode: 'live', + id: entity_id, + holder_name: 'Fintoc', + holder_id: '12345678-9', + is_root: true + } + end + + let(:second_entity_data) do + { + object: 'entity', + mode: 'live', + id: 'ent_1234567890', + holder_name: 'Fintoc', + holder_id: '12345678-9', + is_root: false + } + end + + let(:entities_data) { [first_entity_data, second_entity_data] } + + before do + allow(client).to receive(:get).with(version: :v2).and_return(get_proc) + + allow(get_proc).to receive(:call).with("entities/#{entity_id}").and_return(first_entity_data) + allow(get_proc).to receive(:call).with('entities').and_return(entities_data) + + allow(Fintoc::Transfers::Entity).to receive(:new) + end + + describe '#get' do + it 'fetches and builds an entity' do + manager.get(entity_id) + + expect(Fintoc::Transfers::Entity).to have_received(:new).with(**first_entity_data, client:) + end + end + + describe '#list' do + it 'fetches and builds a list of entities' do + manager.list + + expect(Fintoc::Transfers::Entity).to have_received(:new).with(**first_entity_data, client:) + expect(Fintoc::Transfers::Entity).to have_received(:new).with(**second_entity_data, client:) + end + + it 'passes parameters to the API call' do + params = { page: 2, per_page: 50 } + allow(get_proc).to receive(:call).with('entities', **params).and_return(entities_data) + + manager.list(**params) + + expect(get_proc).to have_received(:call).with('entities', **params) + end + end +end diff --git a/spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb b/spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb new file mode 100644 index 0000000..03b709e --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb @@ -0,0 +1,39 @@ +require 'fintoc/transfers/managers/simulate_manager' + +RSpec.describe Fintoc::Transfers::Managers::SimulateManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:post_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:account_number_id) { 'acno_123' } + let(:amount) { 10000 } + let(:currency) { 'MXN' } + let(:transfer_data) do + { + id: 'trf_123', + object: 'transfer', + amount: 10000, + currency: 'MXN', + account_id: 'acc_123', + reference_id: '123456' + } + end + + before do + allow(client).to receive(:post).with(version: :v2).and_return(post_proc) + + allow(post_proc) + .to receive(:call) + .with('simulate/receive_transfer', account_number_id:, amount:, currency:) + .and_return(transfer_data) + + allow(Fintoc::Transfers::Transfer).to receive(:new) + end + + describe '#receive_transfer' do + it 'calls build_transfer with the response' do + manager.receive_transfer(account_number_id:, amount:, currency:) + + expect(Fintoc::Transfers::Transfer).to have_received(:new).with(**transfer_data, client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb b/spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb new file mode 100644 index 0000000..a059db4 --- /dev/null +++ b/spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb @@ -0,0 +1,107 @@ +require 'fintoc/transfers/managers/account_verifications_manager' + +RSpec.describe Fintoc::Transfers::Managers::TransfersManager do + let(:client) { instance_double(Fintoc::BaseClient) } + let(:get_proc) { instance_double(Proc) } + let(:post_proc) { instance_double(Proc) } + let(:manager) { described_class.new(client) } + let(:counterparty) do + { + holder_id: 'LFHU290523OG0', + holder_name: 'Jon Snow', + account_number: '735969000000203297', + account_type: 'clabe', + institution_id: '40012' + } + end + let(:transfer_id) { 'trf_123' } + let(:first_transfer_data) do + { + id: transfer_id, + object: 'transfer', + amount: 10000, + currency: 'MXN', + account_id: 'acc_123', + counterparty:, + reference_id: '123456' + } + end + let(:second_transfer_data) do + { + id: 'trf_456', + object: 'transfer', + amount: 50000, + currency: 'MXN', + account_id: 'acc_456', + counterparty:, + reference_id: '123457' + } + end + let(:returned_transfer_data) do + first_transfer_data.merge(status: 'returned') + end + + before do + allow(client).to receive(:get).with(version: :v2).and_return(get_proc) + allow(client).to receive(:post).with(version: :v2, use_jws: true).and_return(post_proc) + + allow(get_proc) + .to receive(:call) + .with("transfers/#{transfer_id}") + .and_return(first_transfer_data) + allow(get_proc) + .to receive(:call) + .with('transfers') + .and_return([first_transfer_data, second_transfer_data]) + allow(post_proc) + .to receive(:call) + .with( + 'transfers', + amount: 10000, + currency: 'MXN', + account_id: 'acc_123', + counterparty: + ) + .and_return(first_transfer_data) + allow(post_proc) + .to receive(:call) + .with('transfers/return', transfer_id:) + .and_return(first_transfer_data) + + allow(Fintoc::Transfers::Transfer).to receive(:new) + end + + describe '#create' do + it 'calls build_transfer with the response' do + manager.create(amount: 10000, currency: 'MXN', account_id: 'acc_123', counterparty:) + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(**first_transfer_data, client:) + end + end + + describe '#get' do + it 'calls build_transfer with the response' do + manager.get('trf_123') + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(**first_transfer_data, client:) + end + end + + describe '#list' do + it 'calls build_transfer for each response item' do + manager.list + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(**first_transfer_data, client:) + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(**second_transfer_data, client:) + end + end + + describe '#return' do + it 'calls build_transfer with the response' do + manager.return('trf_123') + expect(Fintoc::Transfers::Transfer) + .to have_received(:new).with(**first_transfer_data, client:) + end + end +end diff --git a/spec/lib/fintoc/transfers/simulation_methods_spec.rb b/spec/lib/fintoc/transfers/simulation_methods_spec.rb deleted file mode 100644 index 5aa4068..0000000 --- a/spec/lib/fintoc/transfers/simulation_methods_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'fintoc/transfers/client/simulation_methods' - -RSpec.describe Fintoc::Transfers::SimulationMethods do - let(:client) { test_class.new(api_key) } - - let(:test_class) do - Class.new do - include Fintoc::Transfers::SimulationMethods - - def initialize(api_key) - @api_key = api_key - end - - # Mock the base client methods needed for testing - def post(*, **) - proc { |_resource, **_kwargs| { mock: 'response' } } - end - end - end - - let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - - describe '#simulate_receive_transfer' do - before do - allow(Fintoc::Transfers::Transfer).to receive(:new) - end - - it 'calls build_transfer with the response' do - client.simulate_receive_transfer( - account_number_id: 'acno_123', - amount: 10000, - currency: 'MXN' - ) - expect(Fintoc::Transfers::Transfer) - .to have_received(:new).with(mock: 'response', client:) - end - end -end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/transfers/transfer_spec.rb index 6ccbc8f..7bdfe23 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/transfers/transfer_spec.rb @@ -218,7 +218,7 @@ let(:refreshed_transfer) { described_class.new(**refreshed_data) } before do - allow(client).to receive(:get_transfer).with(data[:id]).and_return(refreshed_transfer) + allow(client.transfers).to receive(:get).with(data[:id]).and_return(refreshed_transfer) end it 'refreshes the transfer data' do @@ -232,8 +232,8 @@ it 'raises an error if the transfer ID does not match' do wrong_transfer = described_class.new(**data, id: 'wrong_id') - allow(client) - .to receive(:get_transfer).with('tr_329NGN1M4If6VvcMRALv4gjAQJx').and_return(wrong_transfer) + allow(client.transfers) + .to receive(:get).with('tr_329NGN1M4If6VvcMRALv4gjAQJx').and_return(wrong_transfer) expect { transfer.refresh } .to raise_error(ArgumentError, 'Transfer must be the same instance') @@ -245,7 +245,7 @@ let(:returned_transfer) { described_class.new(**returned_data) } before do - allow(client).to receive(:return_transfer).with(data[:id]).and_return(returned_transfer) + allow(client.transfers).to receive(:return).with(data[:id]).and_return(returned_transfer) end it 'returns the transfer and updates status' do @@ -255,6 +255,7 @@ it 'returns self' do expect(transfer.return_transfer).to eq(transfer) + expect(transfer.status).to eq('return_pending') end end end diff --git a/spec/support/shared_examples/clients/account_numbers_client_examples.rb b/spec/support/shared_examples/clients/account_numbers_client_examples.rb index c92b4d4..b68a8f7 100644 --- a/spec/support/shared_examples/clients/account_numbers_client_examples.rb +++ b/spec/support/shared_examples/clients/account_numbers_client_examples.rb @@ -1,67 +1,71 @@ -RSpec.shared_examples 'a client with account numbers methods' do +RSpec.shared_examples 'a client with account numbers manager' do let(:account_number_id) { 'acno_326dzRGqxLee3j9TkaBBBMfs2i0' } let(:account_id) { 'acc_31yYL7h9LVPg121AgFtCyJPDsgM' } it 'responds to account number-specific methods' do - expect(client) - .to respond_to(:create_account_number) - .and respond_to(:get_account_number) - .and respond_to(:list_account_numbers) - .and respond_to(:update_account_number) + expect(client).to respond_to(:account_numbers) + expect(client.account_numbers).to be_a(Fintoc::Transfers::Managers::AccountNumbersManager) + expect(client.account_numbers) + .to respond_to(:create) + .and respond_to(:get) + .and respond_to(:list) + .and respond_to(:update) end - describe '#create_account_number' do - it 'returns an AccountNumber instance', :vcr do - account_number = client.create_account_number( - account_id:, description: 'Test account number', metadata: { test_id: '12345' } - ) - - expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) - .and have_attributes( - account_id:, - description: 'Test account number', - object: 'account_number' + describe '#account_numbers' do + describe '#create' do + it 'returns an AccountNumber instance', :vcr do + account_number = client.account_numbers.create( + account_id:, description: 'Test account number', metadata: { test_id: '12345' } ) + + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + account_id:, + description: 'Test account number', + object: 'account_number' + ) + end end - end - describe '#get_account_number' do - it 'returns an AccountNumber instance', :vcr do - account_number = client.get_account_number(account_number_id) + describe '#get' do + it 'returns an AccountNumber instance', :vcr do + account_number = client.account_numbers.get(account_number_id) - expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) - .and have_attributes( - id: account_number_id, - object: 'account_number' - ) + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + id: account_number_id, + object: 'account_number' + ) + end end - end - describe '#list_account_numbers' do - it 'returns an array of AccountNumber instances', :vcr do - account_numbers = client.list_account_numbers + describe '#list' do + it 'returns an array of AccountNumber instances', :vcr do + account_numbers = client.account_numbers.list - expect(account_numbers).to all(be_a(Fintoc::Transfers::AccountNumber)) - expect(account_numbers.size).to be >= 1 - expect(account_numbers.first.id).to eq(account_number_id) + expect(account_numbers).to all(be_a(Fintoc::Transfers::AccountNumber)) + expect(account_numbers.size).to be >= 1 + expect(account_numbers.first.id).to eq(account_number_id) + end end - end - describe '#update_account_number' do - it 'returns an updated AccountNumber instance', :vcr do - updated_description = 'Updated account number description' - account_number = client.update_account_number( - account_number_id, description: updated_description - ) - - expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) - .and have_attributes( - id: account_number_id, - description: updated_description + describe '#update' do + it 'returns an updated AccountNumber instance', :vcr do + updated_description = 'Updated account number description' + account_number = client.account_numbers.update( + account_number_id, description: updated_description ) + + expect(account_number) + .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .and have_attributes( + id: account_number_id, + description: updated_description + ) + end end end end diff --git a/spec/support/shared_examples/clients/account_verifications_client_examples.rb b/spec/support/shared_examples/clients/account_verifications_client_examples.rb index 186ef7d..5ddbee4 100644 --- a/spec/support/shared_examples/clients/account_verifications_client_examples.rb +++ b/spec/support/shared_examples/clients/account_verifications_client_examples.rb @@ -1,6 +1,6 @@ require 'openssl' -RSpec.shared_examples 'a client with account verifications methods' do +RSpec.shared_examples 'a client with account verifications manager' do let(:account_verification_id) { 'accv_32F2NLQOOwbeOvfuw8Y1zZCfGdw' } let(:account_number) { '735969000000203226' } let(:jws_private_key) do @@ -36,53 +36,58 @@ end it 'responds to account verification-specific methods' do - expect(client) - .to respond_to(:create_account_verification) - .and respond_to(:get_account_verification) - .and respond_to(:list_account_verifications) + expect(client).to respond_to(:account_verifications) + expect(client.account_verifications) + .to be_a(Fintoc::Transfers::Managers::AccountVerificationsManager) + expect(client.account_verifications) + .to respond_to(:create) + .and respond_to(:get) + .and respond_to(:list) end - describe '#create_account_verification' do - it 'returns an AccountVerification instance', :vcr do - account_verification = client.create_account_verification(account_number:) + describe '#account_verifications' do + describe '#create' do + it 'returns an AccountVerification instance', :vcr do + account_verification = client.account_verifications.create(account_number:) - expect(account_verification) - .to be_an_instance_of(Fintoc::Transfers::AccountVerification) - .and have_attributes( - object: 'account_verification', - status: 'pending' - ) + expect(account_verification) + .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .and have_attributes( + object: 'account_verification', + status: 'pending' + ) + end end - end - describe '#get_account_verification' do - it 'returns an AccountVerification instance', :vcr do - account_verification = client.get_account_verification(account_verification_id) + describe '#get' do + it 'returns an AccountVerification instance', :vcr do + account_verification = client.account_verifications.get(account_verification_id) - expect(account_verification) - .to be_an_instance_of(Fintoc::Transfers::AccountVerification) - .and have_attributes( - id: account_verification_id, - object: 'account_verification' - ) + expect(account_verification) + .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .and have_attributes( + id: account_verification_id, + object: 'account_verification' + ) + end end - end - describe '#list_account_verifications' do - it 'returns an array of AccountVerification instances', :vcr do - account_verifications = client.list_account_verifications + describe '#list' do + it 'returns an array of AccountVerification instances', :vcr do + account_verifications = client.account_verifications.list - expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) - expect(account_verifications.size).to be >= 1 - end + expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + expect(account_verifications.size).to be >= 1 + end - it 'accepts filtering parameters', :vcr do - account_verifications = client.list_account_verifications( - since: '2020-01-01T00:00:00.000Z', - limit: 10 - ) + it 'accepts filtering parameters', :vcr do + account_verifications = client.account_verifications.list( + since: '2020-01-01T00:00:00.000Z', + limit: 10 + ) - expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + end end end end diff --git a/spec/support/shared_examples/clients/accounts_client_examples.rb b/spec/support/shared_examples/clients/accounts_client_examples.rb index 9a5cfe9..fb787f0 100644 --- a/spec/support/shared_examples/clients/accounts_client_examples.rb +++ b/spec/support/shared_examples/clients/accounts_client_examples.rb @@ -1,63 +1,67 @@ -RSpec.shared_examples 'a client with accounts methods' do +RSpec.shared_examples 'a client with accounts manager' do let(:account_id) { 'acc_31yYL7h9LVPg121AgFtCyJPDsgM' } let(:entity_id) { 'ent_31t0VhhrAXASFQTVYfCfIBnljbT' } it 'responds to account-specific methods' do - expect(client) - .to respond_to(:create_account) - .and respond_to(:get_account) - .and respond_to(:list_accounts) - .and respond_to(:update_account) + expect(client).to respond_to(:accounts) + expect(client.accounts).to be_a(Fintoc::Transfers::Managers::AccountsManager) + expect(client.accounts) + .to respond_to(:create) + .and respond_to(:get) + .and respond_to(:list) + .and respond_to(:update) end - describe '#create_account' do - it 'returns an Account instance', :vcr do - account = client.create_account(entity_id:, description: 'Test account') - - expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) - .and have_attributes( - description: 'Test account', - currency: 'MXN', - status: 'active' - ) + describe '#accounts' do + describe '#create' do + it 'returns an Account instance', :vcr do + account = client.accounts.create(entity_id:, description: 'Test account') + + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + description: 'Test account', + currency: 'MXN', + status: 'active' + ) + end end - end - describe '#get_account' do - it 'returns an Account instance', :vcr do - account = client.get_account(account_id) + describe '#get' do + it 'returns an Account instance', :vcr do + account = client.accounts.get(account_id) - expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) - .and have_attributes( - id: account_id, - description: 'Test account' - ) + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + id: account_id, + description: 'Test account' + ) + end end - end - describe '#list_accounts' do - it 'returns an array of Account instances', :vcr do - accounts = client.list_accounts + describe '#list' do + it 'returns an array of Account instances', :vcr do + accounts = client.accounts.list - expect(accounts).to all(be_a(Fintoc::Transfers::Account)) - expect(accounts.size).to be >= 1 - expect(accounts.first.id).to eq(account_id) + expect(accounts).to all(be_a(Fintoc::Transfers::Account)) + expect(accounts.size).to be >= 1 + expect(accounts.first.id).to eq(account_id) + end end - end - describe '#update_account' do - it 'returns an updated Account instance', :vcr do - updated_description = 'Updated account description' - account = client.update_account(account_id, description: updated_description) - - expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) - .and have_attributes( - id: account_id, - description: updated_description - ) + describe '#update' do + it 'returns an updated Account instance', :vcr do + updated_description = 'Updated account description' + account = client.accounts.update(account_id, description: updated_description) + + expect(account) + .to be_an_instance_of(Fintoc::Transfers::Account) + .and have_attributes( + id: account_id, + description: updated_description + ) + end end end end diff --git a/spec/support/shared_examples/clients/entities_client_examples.rb b/spec/support/shared_examples/clients/entities_client_examples.rb index f88d071..7c3117c 100644 --- a/spec/support/shared_examples/clients/entities_client_examples.rb +++ b/spec/support/shared_examples/clients/entities_client_examples.rb @@ -1,32 +1,36 @@ -RSpec.shared_examples 'a client with entities methods' do +RSpec.shared_examples 'a client with entities manager' do let(:entity_id) { 'ent_31t0VhhrAXASFQTVYfCfIBnljbT' } - it 'responds to entity-specific methods' do - expect(client) - .to respond_to(:get_entity) - .and respond_to(:get_entities) + it 'provides an entities manager' do + expect(client).to respond_to(:entities) + expect(client.entities).to be_a(Fintoc::Transfers::Managers::EntitiesManager) + expect(client.entities) + .to respond_to(:get) + .and respond_to(:list) end - describe '#get_entity' do - it 'returns an Entity instance', :vcr do - entity = client.get_entity(entity_id) + describe '#entities' do + describe '#get' do + it 'returns an Entity instance', :vcr do + entity = client.entities.get(entity_id) - expect(entity) - .to be_an_instance_of(Fintoc::Transfers::Entity) - .and have_attributes( - id: entity_id, - holder_name: 'Fintoc' - ) + expect(entity) + .to be_an_instance_of(Fintoc::Transfers::Entity) + .and have_attributes( + id: entity_id, + holder_name: 'Fintoc' + ) + end end - end - describe '#get_entities' do - it 'returns an array of Entity instances', :vcr do - entities = client.get_entities + describe '#list' do + it 'returns an array of Entity instances', :vcr do + entities = client.entities.list - expect(entities).to all(be_a(Fintoc::Transfers::Entity)) - expect(entities.size).to eq(1) - expect(entities.first.id).to eq('ent_31t0VhhrAXASFQTVYfCfIBnljbT') + expect(entities).to all(be_a(Fintoc::Transfers::Entity)) + expect(entities.size).to eq(1) + expect(entities.first.id).to eq('ent_31t0VhhrAXASFQTVYfCfIBnljbT') + end end end end diff --git a/spec/support/shared_examples/clients/links_client_examples.rb b/spec/support/shared_examples/clients/links_client_examples.rb index 58a45d3..32ac82b 100644 --- a/spec/support/shared_examples/clients/links_client_examples.rb +++ b/spec/support/shared_examples/clients/links_client_examples.rb @@ -1,43 +1,28 @@ -RSpec.shared_examples 'a client with links methods' do +RSpec.shared_examples 'a client with links manager' do let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } it 'responds to link-specific methods' do - expect(client) - .to respond_to(:get_link) - .and respond_to(:get_links) - .and respond_to(:delete_link) - .and respond_to(:get_account) + expect(client).to respond_to(:links) + expect(client.links).to be_a(Fintoc::Movements::Managers::LinksManager) + expect(client.links) + .to respond_to(:get) + .and respond_to(:list) + .and respond_to(:delete) end - describe '#get_link' do - it 'get the link from a given link token', :vcr do - link = client.get_link(link_token) - expect(link).to be_an_instance_of(Fintoc::Movements::Link) + describe '#links' do + describe '#get' do + it 'get the link from a given link token', :vcr do + link = client.links.get(link_token) + expect(link).to be_an_instance_of(Fintoc::Movements::Link) + end end - end - - describe '#get_links' do - it 'get all the links from a given link token', :vcr do - links = client.get_links - expect(links).to all(be_a(Fintoc::Movements::Link)) - end - end - - describe '#get_account' do - it 'get a linked account', :vcr do - link = client.get_link(link_token) - account = link.find(type: 'checking_account') - returned_account = client.get_account(link_token, account.id) - expect(returned_account).to be_an_instance_of(Fintoc::Movements::Account) - end - end - describe '#get_accounts', :vcr do - it 'prints accounts to console' do - link = client.get_link(link_token) - expect do - link.show_accounts - end.to output(start_with('This links has 1 account')).to_stdout + describe '#list' do + it 'get all the links from a given link token', :vcr do + links = client.links.list + expect(links).to all(be_a(Fintoc::Movements::Link)) + end end end end diff --git a/spec/support/shared_examples/clients/simulate_client_examples.rb b/spec/support/shared_examples/clients/simulate_client_examples.rb new file mode 100644 index 0000000..0989c83 --- /dev/null +++ b/spec/support/shared_examples/clients/simulate_client_examples.rb @@ -0,0 +1,32 @@ +RSpec.shared_examples 'a client with simulate manager' do + it 'responds to simulate-specific methods' do + expect(client).to respond_to(:simulate) + expect(client.simulate).to be_a(Fintoc::Transfers::Managers::SimulateManager) + expect(client.simulate) + .to respond_to(:receive_transfer) + end + + describe '#simulate' do + describe '#receive_transfer' do + let(:simulate_transfer_data) do + { + account_number_id: 'acno_326dzRGqxLee3j9TkaBBBMfs2i0', + amount: 10000, + currency: 'MXN' + } + end + + it 'simulates receiving a transfer and returns Transfer object', :vcr do + transfer = client.simulate.receive_transfer(**simulate_transfer_data) + + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + amount: simulate_transfer_data[:amount], + currency: simulate_transfer_data[:currency], + account_number: include(id: simulate_transfer_data[:account_number_id]) + ) + end + end + end +end diff --git a/spec/support/shared_examples/clients/simulation_client_examples.rb b/spec/support/shared_examples/clients/simulation_client_examples.rb deleted file mode 100644 index 29aacad..0000000 --- a/spec/support/shared_examples/clients/simulation_client_examples.rb +++ /dev/null @@ -1,23 +0,0 @@ -RSpec.shared_examples 'a client with simulation methods' do - describe '#simulate_receive_transfer' do - let(:simulate_transfer_data) do - { - account_number_id: 'acno_326dzRGqxLee3j9TkaBBBMfs2i0', - amount: 10000, - currency: 'MXN' - } - end - - it 'simulates receiving a transfer and returns Transfer object', :vcr do - transfer = client.simulate_receive_transfer(**simulate_transfer_data) - - expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) - .and have_attributes( - amount: simulate_transfer_data[:amount], - currency: simulate_transfer_data[:currency], - account_number: include(id: simulate_transfer_data[:account_number_id]) - ) - end - end -end diff --git a/spec/support/shared_examples/clients/transfers_client_examples.rb b/spec/support/shared_examples/clients/transfers_client_examples.rb index fbf6a5d..6e033d4 100644 --- a/spec/support/shared_examples/clients/transfers_client_examples.rb +++ b/spec/support/shared_examples/clients/transfers_client_examples.rb @@ -1,6 +1,6 @@ require 'openssl' -RSpec.shared_examples 'a client with transfers methods' do +RSpec.shared_examples 'a client with transfers manager' do let(:jws_private_key) do key_string = "-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNLwwQr/uFToDH @@ -57,70 +57,74 @@ end it 'responds to transfer-specific methods' do - expect(client) - .to respond_to(:create_transfer) - .and respond_to(:get_transfer) - .and respond_to(:list_transfers) - .and respond_to(:return_transfer) + expect(client).to respond_to(:transfers) + expect(client.transfers).to be_a(Fintoc::Transfers::Managers::TransfersManager) + expect(client.transfers) + .to respond_to(:create) + .and respond_to(:get) + .and respond_to(:list) + .and respond_to(:return) end - describe '#create_transfer' do - it 'returns a Transfer instance', :vcr do - transfer = client.create_transfer(**transfer_data) + describe '#transfers' do + describe '#create' do + it 'returns a Transfer instance', :vcr do + transfer = client.transfers.create(**transfer_data) - expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) - .and have_attributes( - amount: 50000, - currency: 'MXN', - comment: 'Test payment', - reference_id: '123456', - status: 'pending' - ) + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + amount: 50000, + currency: 'MXN', + comment: 'Test payment', + reference_id: '123456', + status: 'pending' + ) + end end - end - describe '#get_transfer' do - it 'returns a Transfer instance', :vcr do - transfer = client.get_transfer(transfer_id) + describe '#get' do + it 'returns a Transfer instance', :vcr do + transfer = client.transfers.get(transfer_id) - expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) - .and have_attributes( - id: transfer_id, - object: 'transfer' - ) + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + id: transfer_id, + object: 'transfer' + ) + end end - end - describe '#list_transfers' do - it 'returns an array of Transfer instances', :vcr do - transfers = client.list_transfers + describe '#list' do + it 'returns an array of Transfer instances', :vcr do + transfers = client.transfers.list - expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) - expect(transfers.size).to be >= 1 - end + expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers.size).to be >= 1 + end - it 'accepts filtering parameters', :vcr do - transfers = client.list_transfers(status: 'succeeded', direction: 'outbound') + it 'accepts filtering parameters', :vcr do + transfers = client.transfers.list(status: 'succeeded', direction: 'outbound') - expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) - expect(transfers).to all(have_attributes(status: 'succeeded', direction: 'outbound')) + expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers).to all(have_attributes(status: 'succeeded', direction: 'outbound')) + end end - end - describe '#return_transfer' do - let(:transfer_id) { 'tr_329R3l5JksDkoevCGTOBsugCsnb' } + describe '#return' do + let(:transfer_id) { 'tr_329R3l5JksDkoevCGTOBsugCsnb' } - it 'returns a Transfer instance with return_pending status', :vcr do - transfer = client.return_transfer(transfer_id) + it 'returns a Transfer instance with return_pending status', :vcr do + transfer = client.transfers.return(transfer_id) - expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) - .and have_attributes( - id: transfer_id, - status: 'return_pending' - ) + expect(transfer) + .to be_an_instance_of(Fintoc::Transfers::Transfer) + .and have_attributes( + id: transfer_id, + status: 'return_pending' + ) + end end end end diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_link/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_link/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_links/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_links/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml deleted file mode 100644 index d3b9db9..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_account/get_a_linked_account.yml +++ /dev/null @@ -1,151 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 18 Sep 2020 19:32:32 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=def4c4e84aea25dab831e626b094e47e11600457551; expires=Sun, 18-Oct-20 - 19:32:31 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"897ae694e5399ed740681eeb2602478d" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - 87980ff7-a12c-4524-89e2-c66062907680 - X-Runtime: - - '0.379295' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 05444d46ad0000681fd9284200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d4d7e511dc0681f-EZE - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. - Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}]}' - recorded_at: Fri, 18 Sep 2020 19:32:32 GMT -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W?link_token=6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Fri, 18 Sep 2020 19:32:33 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - Set-Cookie: - - __cfduid=dbfae5f92e0ae1b5fe843ee4eba3eb1ce1600457552; expires=Sun, 18-Oct-20 - 19:32:32 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"897ae694e5399ed740681eeb2602478d" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - 5155665b-825d-4a62-84a4-910f4750e187 - X-Runtime: - - '0.336331' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 05444d4bea0000e51adc97e200000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Server: - - cloudflare - Cf-Ray: - - 5d4d7e597cb8e51a-ARI - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. - Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}]}' - recorded_at: Fri, 18 Sep 2020 19:32:33 GMT -recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml b/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml deleted file mode 100644 index 33c5caf..0000000 --- a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_methods/_get_accounts/prints_accounts_to_console.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api.fintoc.com/v1/links/6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W - body: - encoding: UTF-8 - string: '' - headers: - Authorization: - - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx - User-Agent: - - fintoc-ruby/0.1.0 - Connection: - - close - Host: - - api.fintoc.com - response: - status: - code: 200 - message: OK - headers: - Date: - - Tue, 08 Jun 2021 15:18:13 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - close - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - Etag: - - W/"3c5aa7315fb87065354f5169836f2c13" - Cache-Control: - - max-age=0, private, must-revalidate - X-Request-Id: - - '06048509-ff44-4c97-bea5-a0fd0c7cdc63' - X-Runtime: - - '0.296151' - Strict-Transport-Security: - - max-age=31536000; includeSubDomains - Vary: - - Origin - Via: - - 1.1 vegur - Cf-Cache-Status: - - DYNAMIC - Cf-Request-Id: - - 0a8dccf4390000cfbfa3336000000001 - Expect-Ct: - - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" - Report-To: - - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v2?s=Blsb52h5pNeOJDXBJHlt%2Bx3thBGcMHFbdTc3ivN%2FaVZt1fGU9KzIoFH5kwA6zGkcnt2bto%2BwIyUioFDYbOGEa7lO%2B1Gltk8gQI%2BsatfY8X9z1rLlka8ArFY2UQ%3D%3D"}],"group":"cf-nel","max_age":604800}' - Nel: - - '{"report_to":"cf-nel","max_age":604800}' - Server: - - cloudflare - Cf-Ray: - - 65c31766c84fcfbf-SCL - body: - encoding: UTF-8 - string: '{"id":"6n12zLmai3lLE9Dq","holder_id":"782592211","username":"416148503","holder_type":"business","created_at":"2020-08-18T17:37:24.550Z","institution":{"id":"cl_banco_de_chile","name":"Banco - de Chile","country":"cl"},"link_token":null,"mode":"test","active":true,"status":"active","object":"link","accounts":[{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta - Corriente","official_name":"Cuenta Corriente","balance":{"available":78785969,"current":78785969,"limit":78785969},"holder_id":"416148503","holder_name":"Teodoro - Carbajal Hidalgo","currency":"CLP","refreshed_at":null,"object":"account"}]}' - recorded_at: Tue, 08 Jun 2021 15:18:13 GMT -recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_create_account_number/returns_an_AccountNumber_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_get_account_number/returns_an_AccountNumber_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_list_account_numbers/returns_an_array_of_AccountNumber_instances.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_methods/_update_account_number/returns_an_updated_AccountNumber_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_create_account_verification/returns_an_AccountVerification_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_get_account_verification/returns_an_AccountVerification_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/accepts_filtering_parameters.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_methods/_list_account_verifications/returns_an_array_of_AccountVerification_instances.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_create_account/returns_an_Account_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_get_account/returns_an_Account_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_list_accounts/returns_an_array_of_Account_instances.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_methods/_update_account/returns_an_updated_Account_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entity/returns_an_Entity_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_methods/_get_entities/returns_an_array_of_Entity_instances.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulation_methods/_simulate_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_create_transfer/returns_a_Transfer_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_get_transfer/returns_a_Transfer_instance.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/accepts_filtering_parameters.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_list_transfers/returns_an_array_of_Transfer_instances.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml b/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_methods/_return_transfer/returns_a_Transfer_instance_with_return_pending_status.yml rename to spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml From 51f359f554491b9a88e7f597c6c4bdaa2daff0c4 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 11:11:13 -0400 Subject: [PATCH 54/72] fix(readme): Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8553b6f..db367a0 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ link = movements_client.links.get('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp85 account = link.find(type: 'checking_account') # Get the last 30 movements -movements = account.momvements.list +movements = account.movements.list # Or get all the movements since a specific date movements = account.movements.list(since: '2020-08-15') From 8bca17f48357cc9efcf13154e23a95fe00f9f917 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 11:51:27 -0400 Subject: [PATCH 55/72] refactor(client): Prefer having v1/v2 clients instead of movements/transfers/others for consistency with other SDKs --- README.md | 161 +++++++++--------- lib/fintoc/client.rb | 31 ++-- lib/fintoc/{movements => v1}/client/client.rb | 4 +- .../managers/links_manager.rb | 6 +- .../{movements => v1}/resources/account.rb | 12 +- .../{movements => v1}/resources/balance.rb | 4 +- .../resources/institution.rb | 4 +- .../{movements => v1}/resources/link.rb | 10 +- .../{movements => v1}/resources/movement.rb | 8 +- .../resources/transfer_account.rb | 6 +- lib/fintoc/{transfers => v2}/client/client.rb | 14 +- .../managers/account_numbers_manager.rb | 6 +- .../managers/account_verifications_manager.rb | 6 +- .../managers/accounts_manager.rb | 6 +- .../managers/entities_manager.rb | 6 +- .../managers/simulate_manager.rb | 6 +- .../managers/transfers_manager.rb | 6 +- .../{transfers => v2}/resources/account.rb | 2 +- .../resources/account_number.rb | 2 +- .../resources/account_verification.rb | 2 +- .../{transfers => v2}/resources/entity.rb | 2 +- .../{transfers => v2}/resources/transfer.rb | 2 +- spec/lib/fintoc/client_spec.rb | 29 ++-- .../fintoc/{movements => v1}/account_spec.rb | 12 +- .../fintoc/{movements => v1}/balance_spec.rb | 4 +- .../fintoc/{movements => v1}/client_spec.rb | 6 +- .../{movements => v1}/institution_spec.rb | 4 +- .../lib/fintoc/{movements => v1}/link_spec.rb | 6 +- .../managers/links_manager_spec.rb | 14 +- .../fintoc/{movements => v1}/movement_spec.rb | 4 +- .../transfer_account_spec.rb | 4 +- .../{transfers => v2}/account_number_spec.rb | 8 +- .../fintoc/{transfers => v2}/account_spec.rb | 8 +- .../account_verification_spec.rb | 6 +- .../fintoc/{transfers => v2}/client_spec.rb | 6 +- .../fintoc/{transfers => v2}/entity_spec.rb | 6 +- .../managers/account_numbers_manager_spec.rb | 16 +- .../account_verifications_manager_spec.rb | 14 +- .../managers/accounts_manager_spec.rb | 16 +- .../managers/entities_manager_spec.rb | 12 +- .../managers/simulate_manager_spec.rb | 8 +- .../managers/transfers_manager_spec.rb | 16 +- .../fintoc/{transfers => v2}/transfer_spec.rb | 6 +- .../account_numbers_client_examples.rb | 10 +- .../account_verifications_client_examples.rb | 10 +- .../clients/accounts_client_examples.rb | 10 +- .../clients/entities_client_examples.rb | 6 +- .../clients/links_client_examples.rb | 6 +- .../clients/simulate_client_examples.rb | 4 +- .../clients/transfers_client_examples.rb | 12 +- .../get_the_last_30_account_s_movements.yml | 0 ...get_account_s_movements_with_arguments.yml | 0 .../get_the_last_30_account_s_movements.yml | 0 .../update_account_s_movements.yml | 0 .../get_the_link_from_a_given_link_token.yml | 0 ..._all_the_links_from_a_given_link_token.yml | 0 .../returns_an_AccountNumber_instance.yml | 0 .../returns_an_AccountNumber_instance.yml | 0 ...ns_an_array_of_AccountNumber_instances.yml | 0 ...urns_an_updated_AccountNumber_instance.yml | 0 ...eturns_an_AccountVerification_instance.yml | 0 ...eturns_an_AccountVerification_instance.yml | 0 .../_list/accepts_filtering_parameters.yml | 0 ...array_of_AccountVerification_instances.yml | 0 .../_create/returns_an_Account_instance.yml | 0 .../_get/returns_an_Account_instance.yml | 0 .../returns_an_array_of_Account_instances.yml | 0 .../returns_an_updated_Account_instance.yml | 0 .../_get/returns_an_Entity_instance.yml | 0 .../returns_an_array_of_Entity_instances.yml | 0 ...a_transfer_and_returns_Transfer_object.yml | 0 .../_create/returns_a_Transfer_instance.yml | 0 .../_get/returns_a_Transfer_instance.yml | 0 .../_list/accepts_filtering_parameters.yml | 0 ...returns_an_array_of_Transfer_instances.yml | 0 ...er_instance_with_return_pending_status.yml | 0 76 files changed, 292 insertions(+), 277 deletions(-) rename lib/fintoc/{movements => v1}/client/client.rb (71%) rename lib/fintoc/{movements => v1}/managers/links_manager.rb (88%) rename lib/fintoc/{movements => v1}/resources/account.rb (86%) rename lib/fintoc/{movements => v1}/resources/balance.rb (82%) rename lib/fintoc/{movements => v1}/resources/institution.rb (80%) rename lib/fintoc/{movements => v1}/resources/link.rb (87%) rename lib/fintoc/{movements => v1}/resources/movement.rb (83%) rename lib/fintoc/{movements => v1}/resources/transfer_account.rb (71%) rename lib/fintoc/{transfers => v2}/client/client.rb (65%) rename lib/fintoc/{transfers => v2}/managers/account_numbers_manager.rb (92%) rename lib/fintoc/{transfers => v2}/managers/account_verifications_manager.rb (88%) rename lib/fintoc/{transfers => v2}/managers/accounts_manager.rb (90%) rename lib/fintoc/{transfers => v2}/managers/entities_manager.rb (83%) rename lib/fintoc/{transfers => v2}/managers/simulate_manager.rb (83%) rename lib/fintoc/{transfers => v2}/managers/transfers_manager.rb (91%) rename lib/fintoc/{transfers => v2}/resources/account.rb (99%) rename lib/fintoc/{transfers => v2}/resources/account_number.rb (99%) rename lib/fintoc/{transfers => v2}/resources/account_verification.rb (98%) rename lib/fintoc/{transfers => v2}/resources/entity.rb (98%) rename lib/fintoc/{transfers => v2}/resources/transfer.rb (99%) rename spec/lib/fintoc/{movements => v1}/account_spec.rb (83%) rename spec/lib/fintoc/{movements => v1}/balance_spec.rb (78%) rename spec/lib/fintoc/{movements => v1}/client_spec.rb (82%) rename spec/lib/fintoc/{movements => v1}/institution_spec.rb (79%) rename spec/lib/fintoc/{movements => v1}/link_spec.rb (92%) rename spec/lib/fintoc/{movements => v1}/managers/links_manager_spec.rb (84%) rename spec/lib/fintoc/{movements => v1}/movement_spec.rb (97%) rename spec/lib/fintoc/{movements => v1}/transfer_account_spec.rb (78%) rename spec/lib/fintoc/{transfers => v2}/account_number_spec.rb (96%) rename spec/lib/fintoc/{transfers => v2}/account_spec.rb (96%) rename spec/lib/fintoc/{transfers => v2}/account_verification_spec.rb (95%) rename spec/lib/fintoc/{transfers => v2}/client_spec.rb (85%) rename spec/lib/fintoc/{transfers => v2}/entity_spec.rb (92%) rename spec/lib/fintoc/{transfers => v2}/managers/account_numbers_manager_spec.rb (87%) rename spec/lib/fintoc/{transfers => v2}/managers/account_verifications_manager_spec.rb (83%) rename spec/lib/fintoc/{transfers => v2}/managers/accounts_manager_spec.rb (88%) rename spec/lib/fintoc/{transfers => v2}/managers/entities_manager_spec.rb (75%) rename spec/lib/fintoc/{transfers => v2}/managers/simulate_manager_spec.rb (76%) rename spec/lib/fintoc/{transfers => v2}/managers/transfers_manager_spec.rb (87%) rename spec/lib/fintoc/{transfers => v2}/transfer_spec.rb (98%) rename spec/vcr/{Fintoc_Movements_Account => Fintoc_V1_Account}/_get_movements/get_the_last_30_account_s_movements.yml (100%) rename spec/vcr/{Fintoc_Movements_Account => Fintoc_V1_Account}/_movement_with_since_argument/get_account_s_movements_with_arguments.yml (100%) rename spec/vcr/{Fintoc_Movements_Account => Fintoc_V1_Account}/_movements/get_the_last_30_account_s_movements.yml (100%) rename spec/vcr/{Fintoc_Movements_Account => Fintoc_V1_Account}/_update_balance/update_account_s_movements.yml (100%) rename spec/vcr/{Fintoc_Movements_Client => Fintoc_V1_Client}/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Movements_Client => Fintoc_V1_Client}/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml (100%) rename spec/vcr/{Fintoc_Transfers_Client => Fintoc_V2_Client}/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml (100%) diff --git a/README.md b/README.md index db367a0..d5f0cfc 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ Do yourself a favor: go grab some ice cubes by installing this refreshing librar - [How to Install](#how-to-install) - [Quickstart](#quickstart) - [Client Architecture](#client-architecture) - - [**Movements API Client**](#movements-api-client) - - [**Transfers API Client**](#transfers-api-client) + - [**API V1 Client**](#api-v1-client) + - [**API V2 Client**](#api-v2-client) - [**Backward compatibility**](#backward-compatibility) - [Documentation](#documentation) - [Examples](#examples) @@ -63,9 +63,8 @@ Or install it yourself as: ```ruby require 'fintoc' -movements_client = - Fintoc::Movements::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') -link = movements_client.links.get('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') +client = Fintoc::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') +link = client_v1.links.get('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') account = link.find(type: 'checking_account') # Get the last 30 movements @@ -81,73 +80,87 @@ And that’s it! The Fintoc Ruby client is organized into separate clients that mirror the official API structure: -### **Movements API Client** +### **API V1 Client** -The Movements API client provides access to bank account data and movements: +The API client currently provides access to part of the Movements API: ```ruby -movements_client = Fintoc::Movements::Client.new('api_key') +client = Fintoc::V1::Client.new('api_key') # Link management -links = movements_client.links.list -link = movements_client.links.get('link_token') -movements_client.links.delete('link_id') +links = client_v1.links.list +link = client_v1.links.get('link_token') +client_v1.links.delete('link_id') # Account access -account = movements_client.accounts.get('link_token', 'account_id') +account = link.find(id: account_id) ``` -### **Transfers API Client** +### **API V2 Client** -The Transfers API client provides access to transfer accounts, entities, and transfer operations: +The API V2 client currently provides access to part of the Transfers API: ```ruby -transfers_client = Fintoc::Transfers::Client.new('api_key') +client = Fintoc::V1::Client.new('api_key') # Entities -entities = transfers_client.entities.list -entity = transfers_client.entities.get('entity_id') +entities = client_v2.entities.list +entity = client_v2.entities.get('entity_id') # Transfer Accounts -accounts = transfers_client.accounts.list -account = transfers_client.accounts.get('account_id') -account = transfers_client.accounts.create(entity_id: 'entity_id', description: 'My Account') -transfers_client.accounts.update('account_id', description: 'Updated') +accounts = client_v2.accounts.list +account = client_v2.accounts.get('account_id') +account = client_v2.accounts.create(entity_id: 'entity_id', description: 'My Account') +client_v2.accounts.update('account_id', description: 'Updated') # Account Numbers -account_numbers = transfers_client.account_numbers.list -account_number = transfers_client.account_numbers.get('account_number_id') -account_number = transfers_client.account_numbers.create(account_id: 'account_id', description: 'Main') -transfers_client.account_numbers.update('account_number_id', description: 'Updated') +account_numbers = client_v2.account_numbers.list +account_number = client_v2.account_numbers.get('account_number_id') +account_number = client_v2.account_numbers.create(account_id: 'account_id', description: 'Main') +client_v2.account_numbers.update('account_number_id', description: 'Updated') # Transfers -transfers = transfers_client.transfers.list -transfer = transfers_client.transfers.get('transfer_id') -transfer = transfers_client.transfers.create(amount: 1000, currency: 'CLP', account_id: 'account_id', counterparty: {...}) -transfers_client.transfers.return('transfer_id') +transfers = client_v2.transfers.list +transfer = client_v2.transfers.get('transfer_id') +transfer = client_v2.transfers.create( + amount: 1000, + currency: 'CLP', + account_id: 'account_id', + counterparty: {...} +) +client_v2.transfers.return('transfer_id') # Simulate -simulated_transfer = transfers_client.simulate.receive_transfer(account_number_id: 'account_number_id', amount: 1000, currency: 'CLP') +simulated_transfer = client_v2.simulate.receive_transfer( + account_number_id: 'account_number_id', + amount: 1000, + currency: 'CLP' +) # Account Verifications -account_verifications = transfers_client.account_verifications.list -account_verification = transfers_client.account_verifications.get('account_verification_id') -account_verification = transfers_client.account_verifications.create(account_number: 'account_number') +account_verifications = client_v2.account_verifications.list +account_verification = client_v2.account_verifications.get('account_verification_id') +account_verification = client_v2.account_verifications.create(account_number: 'account_number') + +# TODO: Movements ``` ### **Backward compatibility** -The previous `Fintoc::Client` class is kept for backward compatibility purposes. +The methods of the previous `Fintoc::Client` class implementation are kept for backward compatibility purposes. ```ruby -client = Fintoc::Client.new('api_key') +client = Fintoc::V1::Client.new('api_key') +link = client.get_link('link_token') links = client.get_links +client.delete_link(link.id) +account = client.get_account('link_token', 'account_id') ``` ## Documentation -This client supports all Fintoc API endpoints. For complete information about the API, head to the [docs](https://docs.fintoc.com/reference). +This client does not support all Fintoc API endpoints yet. For complete information about the API, head to the [docs](https://docs.fintoc.com/reference). ## Examples @@ -158,13 +171,13 @@ This client supports all Fintoc API endpoints. For complete information about th ```ruby require 'fintoc' -client = Fintoc::Movements::Client.new('api_key') -link = client.links.get('link_token') +client = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') puts link.accounts # Or... you can pretty print all the accounts in a Link -link = client.links.get('link_token') +link = client_v1.links.get('link_token') link.show_accounts ``` @@ -174,8 +187,8 @@ If you want to find a specific account in a link, you can use **find**. You can ```ruby require 'fintoc' -client = Fintoc::Movements::Client.new('api_key') -link = client.links.get('link_token') +client = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') account = link.find(type: 'checking_account') # Or by number @@ -190,8 +203,8 @@ You can also search for multiple accounts matching a specific criteria with **fi ```ruby require 'fintoc' -client = Fintoc::Movements::Client.new('api_key') -link = client.links.get('link_token') +client = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') accounts = link.find_all(currency: 'CLP') ``` @@ -200,8 +213,8 @@ To update the account balance you can use **update_balance**: ```ruby require 'fintoc' -client = Fintoc::Movements::Client.new('api_key') -link = client.links.get('link_token') +client = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') account = link.find(number: '1111111') account.update_balance ``` @@ -212,8 +225,8 @@ account.update_balance require 'fintoc' require 'time' -client = Fintoc::Movements::Client.new('api_key') -link = client.links.get('link_token') +client = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') account = link.find(type: 'checking_account') # You can get the account movements since a specific DateTime @@ -236,24 +249,20 @@ Calling **movements.list** without arguments gets the last 30 movements of the a ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Get all entities -entities = client.entities.list +entities = client_v2.entities.list # Get a specific entity -entity = client.entities.get('entity_id') - -puts entity.holder_name # => "My Company LLC" -puts entity.holder_id # => "12345678-9" -puts entity.is_root # => true +entity = client_v2.entities.get('entity_id') ``` You can also list entities with pagination: ```ruby # Get entities with pagination -entities = client.entities.list(limit: 10, starting_after: 'entity_id') +entities = client_v2.entities.list(limit: 10, starting_after: 'entity_id') ``` #### Transfer Accounts @@ -261,22 +270,22 @@ entities = client.entities.list(limit: 10, starting_after: 'entity_id') ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Create a transfer account -account = client.accounts.create( +account = client_v2.accounts.create( entity_id: 'entity_id', description: 'My Business Account' ) # Get a specific account -account = client.accounts.get('account_id') +account = client_v2.accounts.get('account_id') # List all accounts -accounts = client.accounts.list +accounts = client_v2.accounts.list # Update an account -updated_account = client.accounts.update('account_id', description: 'Updated Description') +updated_account = client_v2.accounts.update('account_id', description: 'Updated Description') ``` #### Account Numbers @@ -284,22 +293,22 @@ updated_account = client.accounts.update('account_id', description: 'Updated Des ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Create an account number -account_number = client.account_numbers.create( +account_number = client_v2.account_numbers.create( account_id: 'account_id', description: 'Main account number' ) # Get a specific account number -account_number = client.account_numbers.get('account_number_id') +account_number = client_v2.account_numbers.get('account_number_id') # List all account numbers -account_numbers = client.account_numbers.list +account_numbers = client_v2.account_numbers.list # Update an account number -updated_account_number = client.account_numbers.update( +updated_account_number = client_v2.account_numbers.update( 'account_number_id', description: 'Updated account number' ) @@ -310,10 +319,10 @@ updated_account_number = client.account_numbers.update( ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Create a transfer -transfer = client.transfers.create( +transfer = client_v2.transfers.create( amount: 10000, currency: 'CLP', account_id: 'account_id', @@ -328,13 +337,13 @@ transfer = client.transfers.create( ) # Get a specific transfer -transfer = client.transfers.get('transfer_id') +transfer = client_v2.transfers.get('transfer_id') # List all transfers -transfers = client.transfers.list +transfers = client_v2.transfers.list # Return a transfer -returned_transfer = client.transfers.return('transfer_id') +returned_transfer = client_v2.transfers.return('transfer_id') ``` #### Simulate @@ -342,10 +351,10 @@ returned_transfer = client.transfers.return('transfer_id') ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Simulate receiving a transfer -simulated_transfer = client.simulate.receive_transfer( +simulated_transfer = client_v2.simulate.receive_transfer( account_number_id: 'account_number_id', amount: 5000, currency: 'CLP' @@ -357,16 +366,16 @@ simulated_transfer = client.simulate.receive_transfer( ```ruby require 'fintoc' -client = Fintoc::Transfers::Client.new('api_key') +client_v2 = Fintoc::V2::Client.new('api_key', 'jws_private_key') # Create an account verification -account_verification = client.account_verifications.create(account_number: 'account_number') +account_verification = client_v2.account_verifications.create(account_number: 'account_number') # Get a specific account verification -account_verification = client.account_verifications.get('account_verification_id') +account_verification = client_v2.account_verifications.get('account_verification_id') # List all account verifications -account_verifications = client.account_verifications.list +account_verifications = client_v2.account_verifications.list ``` ## Development diff --git a/lib/fintoc/client.rb b/lib/fintoc/client.rb index 973e853..b24bae7 100644 --- a/lib/fintoc/client.rb +++ b/lib/fintoc/client.rb @@ -1,37 +1,40 @@ -require 'fintoc/movements/client/client' -require 'fintoc/transfers/client/client' +require 'fintoc/v1/client/client' +require 'fintoc/v2/client/client' module Fintoc class Client - # Deprecated in favor of Fintoc::Movements::Client and Fintoc::Transfers::Client - # It should not be used anymore, but it will be kept for now for backward compatibility + def initialize(api_key, jws_private_key: nil) + @api_key = api_key + @jws_private_key = jws_private_key + end - attr_reader :movements, :transfers + def v1 + @v1 ||= Fintoc::V1::Client.new(@api_key) + end - def initialize(api_key, jws_private_key: nil) - @movements = Fintoc::Movements::Client.new(api_key) - @transfers = Fintoc::Transfers::Client.new(api_key, jws_private_key: jws_private_key) + def v2 + @v2 ||= Fintoc::V2::Client.new(@api_key, jws_private_key: @jws_private_key) end - # Delegate common methods to maintain backward compatibility + # These methods are kept for backward compatibility def get_link(link_token) - @movements.links.get(link_token) + @v1.links.get(link_token) end def get_links - @movements.links.list + @v1.links.list end def delete_link(link_id) - @movements.links.delete(link_id) + @v1.links.delete(link_id) end def get_account(link_token, account_id) - @movements.links.get(link_token).find(id: account_id) + @v1.links.get(link_token).find(id: account_id) end def to_s - "Fintoc::Client(movements: #{@movements}, transfers: #{@transfers})" + "Fintoc::Client(v1: #{@v1}, v2: #{@v2})" end end end diff --git a/lib/fintoc/movements/client/client.rb b/lib/fintoc/v1/client/client.rb similarity index 71% rename from lib/fintoc/movements/client/client.rb rename to lib/fintoc/v1/client/client.rb index 8c53c22..b9c8c37 100644 --- a/lib/fintoc/movements/client/client.rb +++ b/lib/fintoc/v1/client/client.rb @@ -1,8 +1,8 @@ require 'fintoc/base_client' -require 'fintoc/movements/managers/links_manager' +require 'fintoc/v1/managers/links_manager' module Fintoc - module Movements + module V1 class Client < BaseClient def links @links ||= Managers::LinksManager.new(self) diff --git a/lib/fintoc/movements/managers/links_manager.rb b/lib/fintoc/v1/managers/links_manager.rb similarity index 88% rename from lib/fintoc/movements/managers/links_manager.rb rename to lib/fintoc/v1/managers/links_manager.rb index da51335..e7c53d3 100644 --- a/lib/fintoc/movements/managers/links_manager.rb +++ b/lib/fintoc/v1/managers/links_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/movements/resources/link' +require 'fintoc/v1/resources/link' module Fintoc - module Movements + module V1 module Managers class LinksManager def initialize(client) @@ -38,7 +38,7 @@ def _delete_link(link_id) def build_link(data) param = Utils.pick(data, 'link_token') @client.default_params.update(param) - Fintoc::Movements::Link.new(**data, client: @client) + Fintoc::V1::Link.new(**data, client: @client) end end end diff --git a/lib/fintoc/movements/resources/account.rb b/lib/fintoc/v1/resources/account.rb similarity index 86% rename from lib/fintoc/movements/resources/account.rb rename to lib/fintoc/v1/resources/account.rb index 32d8481..eb214e0 100644 --- a/lib/fintoc/movements/resources/account.rb +++ b/lib/fintoc/v1/resources/account.rb @@ -1,10 +1,10 @@ require 'tabulate' require 'fintoc/utils' -require 'fintoc/movements/resources/movement' -require 'fintoc/movements/resources/balance' +require 'fintoc/v1/resources/movement' +require 'fintoc/v1/resources/balance' module Fintoc - module Movements + module V1 class Account include Utils @@ -35,18 +35,18 @@ def initialize( @type = type @currency = currency @refreshed_at = DateTime.iso8601(refreshed_at) if refreshed_at - @balance = Fintoc::Movements::Balance.new(**balance) + @balance = Fintoc::V1::Balance.new(**balance) @movements = movements || [] @client = client end def update_balance - @balance = Fintoc::Movements::Balance.new(**get_account[:balance]) + @balance = Fintoc::V1::Balance.new(**get_account[:balance]) end def get_movements(**params) _get_movements(**params).lazy.map do - |movement| Fintoc::Movements::Movement.new(**movement, client: @client) + |movement| Fintoc::V1::Movement.new(**movement, client: @client) end end diff --git a/lib/fintoc/movements/resources/balance.rb b/lib/fintoc/v1/resources/balance.rb similarity index 82% rename from lib/fintoc/movements/resources/balance.rb rename to lib/fintoc/v1/resources/balance.rb index 9e83490..4811a77 100644 --- a/lib/fintoc/movements/resources/balance.rb +++ b/lib/fintoc/v1/resources/balance.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Fintoc - module Movements + module V1 class Balance attr_reader :available, :current, :limit @@ -20,7 +20,7 @@ def to_s end def inspect - "" + "" end end end diff --git a/lib/fintoc/movements/resources/institution.rb b/lib/fintoc/v1/resources/institution.rb similarity index 80% rename from lib/fintoc/movements/resources/institution.rb rename to lib/fintoc/v1/resources/institution.rb index ad8dad8..b4b6737 100644 --- a/lib/fintoc/movements/resources/institution.rb +++ b/lib/fintoc/v1/resources/institution.rb @@ -1,5 +1,5 @@ module Fintoc - module Movements + module V1 class Institution attr_reader :id, :name, :country @@ -14,7 +14,7 @@ def to_s end def inspect - "" + "" end end end diff --git a/lib/fintoc/movements/resources/link.rb b/lib/fintoc/v1/resources/link.rb similarity index 87% rename from lib/fintoc/movements/resources/link.rb rename to lib/fintoc/v1/resources/link.rb index 13326ac..ec390ce 100644 --- a/lib/fintoc/movements/resources/link.rb +++ b/lib/fintoc/v1/resources/link.rb @@ -1,11 +1,11 @@ require 'date' require 'tabulate' require 'fintoc/utils' -require 'fintoc/movements/resources/account' -require 'fintoc/movements/resources/institution' +require 'fintoc/v1/resources/account' +require 'fintoc/v1/resources/institution' module Fintoc - module Movements + module V1 class Link attr_reader :id, :username, :holder_type, :institution, :created_at, :mode, :accounts, :link_token @@ -27,13 +27,13 @@ def initialize( @id = id @username = username @holder_type = holder_type - @institution = Fintoc::Movements::Institution.new(**institution) + @institution = Fintoc::V1::Institution.new(**institution) @created_at = Date.iso8601(created_at) @mode = mode @accounts = if accounts.nil? [] else - accounts.map { |data| Fintoc::Movements::Account.new(**data, client:) } + accounts.map { |data| Fintoc::V1::Account.new(**data, client:) } end @token = link_token @client = client diff --git a/lib/fintoc/movements/resources/movement.rb b/lib/fintoc/v1/resources/movement.rb similarity index 83% rename from lib/fintoc/movements/resources/movement.rb rename to lib/fintoc/v1/resources/movement.rb index ce415b4..f752858 100644 --- a/lib/fintoc/movements/resources/movement.rb +++ b/lib/fintoc/v1/resources/movement.rb @@ -1,8 +1,8 @@ require 'date' -require 'fintoc/movements/resources/transfer_account' +require 'fintoc/v1/resources/transfer_account' module Fintoc - module Movements + module V1 class Movement attr_reader :id, :amount, :currency, :description, :reference_id, :post_date, :transaction_date, :type, :recipient_account, @@ -33,9 +33,9 @@ def initialize( @reference_id = reference_id @recipient_account = if recipient_account - Fintoc::Movements::TransferAccount.new(**recipient_account) + Fintoc::V1::TransferAccount.new(**recipient_account) end - @sender_account = Fintoc::Movements::TransferAccount.new(**sender_account) if sender_account + @sender_account = Fintoc::V1::TransferAccount.new(**sender_account) if sender_account @comment = comment @client = client end diff --git a/lib/fintoc/movements/resources/transfer_account.rb b/lib/fintoc/v1/resources/transfer_account.rb similarity index 71% rename from lib/fintoc/movements/resources/transfer_account.rb rename to lib/fintoc/v1/resources/transfer_account.rb index 077bc2c..ff11d69 100644 --- a/lib/fintoc/movements/resources/transfer_account.rb +++ b/lib/fintoc/v1/resources/transfer_account.rb @@ -1,7 +1,7 @@ -require 'fintoc/movements/resources/institution' +require 'fintoc/v1/resources/institution' module Fintoc - module Movements + module V1 class TransferAccount attr_reader :holder_id, :holder_name, :number, :institution @@ -9,7 +9,7 @@ def initialize(holder_id:, holder_name:, number:, institution:, **) @holder_id = holder_id @holder_name = holder_name @number = number - @institution = institution && Fintoc::Movements::Institution.new(**institution) + @institution = institution && Fintoc::V1::Institution.new(**institution) end def id diff --git a/lib/fintoc/transfers/client/client.rb b/lib/fintoc/v2/client/client.rb similarity index 65% rename from lib/fintoc/transfers/client/client.rb rename to lib/fintoc/v2/client/client.rb index cc5f5ea..7e17cb8 100644 --- a/lib/fintoc/transfers/client/client.rb +++ b/lib/fintoc/v2/client/client.rb @@ -1,13 +1,13 @@ require 'fintoc/base_client' -require 'fintoc/transfers/managers/entities_manager' -require 'fintoc/transfers/managers/accounts_manager' -require 'fintoc/transfers/managers/account_numbers_manager' -require 'fintoc/transfers/managers/transfers_manager' -require 'fintoc/transfers/managers/simulate_manager' -require 'fintoc/transfers/managers/account_verifications_manager' +require 'fintoc/v2/managers/entities_manager' +require 'fintoc/v2/managers/accounts_manager' +require 'fintoc/v2/managers/account_numbers_manager' +require 'fintoc/v2/managers/transfers_manager' +require 'fintoc/v2/managers/simulate_manager' +require 'fintoc/v2/managers/account_verifications_manager' module Fintoc - module Transfers + module V2 class Client < BaseClient def entities @entities ||= Managers::EntitiesManager.new(self) diff --git a/lib/fintoc/transfers/managers/account_numbers_manager.rb b/lib/fintoc/v2/managers/account_numbers_manager.rb similarity index 92% rename from lib/fintoc/transfers/managers/account_numbers_manager.rb rename to lib/fintoc/v2/managers/account_numbers_manager.rb index 3370874..58ec4e7 100644 --- a/lib/fintoc/transfers/managers/account_numbers_manager.rb +++ b/lib/fintoc/v2/managers/account_numbers_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/account_number' +require 'fintoc/v2/resources/account_number' module Fintoc - module Transfers + module V2 module Managers class AccountNumbersManager def initialize(client) @@ -51,7 +51,7 @@ def _update_account_number(account_number_id, **params) end def build_account_number(data) - Fintoc::Transfers::AccountNumber.new(**data, client: @client) + Fintoc::V2::AccountNumber.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/managers/account_verifications_manager.rb b/lib/fintoc/v2/managers/account_verifications_manager.rb similarity index 88% rename from lib/fintoc/transfers/managers/account_verifications_manager.rb rename to lib/fintoc/v2/managers/account_verifications_manager.rb index 70d275c..ffd8e15 100644 --- a/lib/fintoc/transfers/managers/account_verifications_manager.rb +++ b/lib/fintoc/v2/managers/account_verifications_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/account_verification' +require 'fintoc/v2/resources/account_verification' module Fintoc - module Transfers + module V2 module Managers class AccountVerificationsManager def initialize(client) @@ -37,7 +37,7 @@ def _list_account_verifications(**params) end def build_account_verification(data) - Fintoc::Transfers::AccountVerification.new(**data, client: @client) + Fintoc::V2::AccountVerification.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/managers/accounts_manager.rb b/lib/fintoc/v2/managers/accounts_manager.rb similarity index 90% rename from lib/fintoc/transfers/managers/accounts_manager.rb rename to lib/fintoc/v2/managers/accounts_manager.rb index 08b1f73..6761f79 100644 --- a/lib/fintoc/transfers/managers/accounts_manager.rb +++ b/lib/fintoc/v2/managers/accounts_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/account' +require 'fintoc/v2/resources/account' module Fintoc - module Transfers + module V2 module Managers class AccountsManager def initialize(client) @@ -46,7 +46,7 @@ def _update_account(account_id, **params) end def build_account(data) - Fintoc::Transfers::Account.new(**data, client: @client) + Fintoc::V2::Account.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/managers/entities_manager.rb b/lib/fintoc/v2/managers/entities_manager.rb similarity index 83% rename from lib/fintoc/transfers/managers/entities_manager.rb rename to lib/fintoc/v2/managers/entities_manager.rb index f45dc14..4cd0071 100644 --- a/lib/fintoc/transfers/managers/entities_manager.rb +++ b/lib/fintoc/v2/managers/entities_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/entity' +require 'fintoc/v2/resources/entity' module Fintoc - module Transfers + module V2 module Managers class EntitiesManager def initialize(client) @@ -28,7 +28,7 @@ def _list_entities(**params) end def build_entity(data) - Fintoc::Transfers::Entity.new(**data, client: @client) + Fintoc::V2::Entity.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/managers/simulate_manager.rb b/lib/fintoc/v2/managers/simulate_manager.rb similarity index 83% rename from lib/fintoc/transfers/managers/simulate_manager.rb rename to lib/fintoc/v2/managers/simulate_manager.rb index 24558d8..b9bbd03 100644 --- a/lib/fintoc/transfers/managers/simulate_manager.rb +++ b/lib/fintoc/v2/managers/simulate_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/transfer' +require 'fintoc/v2/resources/transfer' module Fintoc - module Transfers + module V2 module Managers class SimulateManager def initialize(client) @@ -22,7 +22,7 @@ def _simulate_receive_transfer(account_number_id:, amount:, currency:) end def build_transfer(data) - Fintoc::Transfers::Transfer.new(**data, client: @client) + Fintoc::V2::Transfer.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/managers/transfers_manager.rb b/lib/fintoc/v2/managers/transfers_manager.rb similarity index 91% rename from lib/fintoc/transfers/managers/transfers_manager.rb rename to lib/fintoc/v2/managers/transfers_manager.rb index e38cfcb..adb5a5c 100644 --- a/lib/fintoc/transfers/managers/transfers_manager.rb +++ b/lib/fintoc/v2/managers/transfers_manager.rb @@ -1,7 +1,7 @@ -require 'fintoc/transfers/resources/transfer' +require 'fintoc/v2/resources/transfer' module Fintoc - module Transfers + module V2 module Managers class TransfersManager def initialize(client) @@ -48,7 +48,7 @@ def _return_transfer(transfer_id) end def build_transfer(data) - Fintoc::Transfers::Transfer.new(**data, client: @client) + Fintoc::V2::Transfer.new(**data, client: @client) end end end diff --git a/lib/fintoc/transfers/resources/account.rb b/lib/fintoc/v2/resources/account.rb similarity index 99% rename from lib/fintoc/transfers/resources/account.rb rename to lib/fintoc/v2/resources/account.rb index b247f91..9601a71 100644 --- a/lib/fintoc/transfers/resources/account.rb +++ b/lib/fintoc/v2/resources/account.rb @@ -1,7 +1,7 @@ require 'money' module Fintoc - module Transfers + module V2 class Account attr_reader :id, :object, :mode, :description, :available_balance, :currency, :is_root, :root_account_number_id, :root_account_number, :status, :entity diff --git a/lib/fintoc/transfers/resources/account_number.rb b/lib/fintoc/v2/resources/account_number.rb similarity index 99% rename from lib/fintoc/transfers/resources/account_number.rb rename to lib/fintoc/v2/resources/account_number.rb index 99a9992..a0e4df1 100644 --- a/lib/fintoc/transfers/resources/account_number.rb +++ b/lib/fintoc/v2/resources/account_number.rb @@ -1,5 +1,5 @@ module Fintoc - module Transfers + module V2 class AccountNumber attr_reader :id, :object, :description, :number, :created_at, :updated_at, :mode, :status, :is_root, :account_id, :metadata diff --git a/lib/fintoc/transfers/resources/account_verification.rb b/lib/fintoc/v2/resources/account_verification.rb similarity index 98% rename from lib/fintoc/transfers/resources/account_verification.rb rename to lib/fintoc/v2/resources/account_verification.rb index 8e62ceb..f326809 100644 --- a/lib/fintoc/transfers/resources/account_verification.rb +++ b/lib/fintoc/v2/resources/account_verification.rb @@ -1,5 +1,5 @@ module Fintoc - module Transfers + module V2 class AccountVerification attr_reader :id, :object, :status, :reason, :transfer_id, :counterparty, :mode, :receipt_url, :transaction_date diff --git a/lib/fintoc/transfers/resources/entity.rb b/lib/fintoc/v2/resources/entity.rb similarity index 98% rename from lib/fintoc/transfers/resources/entity.rb rename to lib/fintoc/v2/resources/entity.rb index ab308bf..29cc562 100644 --- a/lib/fintoc/transfers/resources/entity.rb +++ b/lib/fintoc/v2/resources/entity.rb @@ -1,5 +1,5 @@ module Fintoc - module Transfers + module V2 class Entity attr_reader :object, :mode, :id, :holder_name, :holder_id, :is_root diff --git a/lib/fintoc/transfers/resources/transfer.rb b/lib/fintoc/v2/resources/transfer.rb similarity index 99% rename from lib/fintoc/transfers/resources/transfer.rb rename to lib/fintoc/v2/resources/transfer.rb index 81ef72d..0d78514 100644 --- a/lib/fintoc/transfers/resources/transfer.rb +++ b/lib/fintoc/v2/resources/transfer.rb @@ -1,7 +1,7 @@ require 'money' module Fintoc - module Transfers + module V2 class Transfer attr_reader :id, :object, :amount, :currency, :direction, :status, :mode, :post_date, :transaction_date, :comment, :reference_id, :receipt_url, diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index bb40b27..39d7fb3 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -1,11 +1,12 @@ require 'fintoc/client' -require 'fintoc/movements/resources/link' -require 'fintoc/movements/resources/account' -require 'fintoc/movements/resources/movement' +require 'fintoc/v1/resources/link' +require 'fintoc/v1/resources/account' +require 'fintoc/v1/resources/movement' RSpec.describe Fintoc::Client do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { described_class.new(api_key) } + let(:jws_private_key) { OpenSSL::PKey::RSA.new(2048) } + let(:client) { described_class.new(api_key, jws_private_key: jws_private_key) } describe '.new' do it 'create an instance Client' do @@ -13,21 +14,23 @@ end it 'creates movements and transfers clients' do - expect(client.movements).to be_an_instance_of(Fintoc::Movements::Client) - expect(client.transfers).to be_an_instance_of(Fintoc::Transfers::Client) + expect(client).to respond_to(:v1) + expect(client.v1).to be_an_instance_of(Fintoc::V1::Client) + expect(client).to respond_to(:v2) + expect(client.v2).to be_an_instance_of(Fintoc::V2::Client) end end describe 'client separation' do it 'allows direct access to movements client' do - expect(client.movements.links) + expect(client.v1.links) .to respond_to(:get) .and respond_to(:list) .and respond_to(:delete) end it 'allows direct access to transfers client' do - expect(client.transfers.entities) + expect(client.v2.entities) .to respond_to(:get) .and respond_to(:list) end @@ -42,13 +45,13 @@ end describe 'delegation to movements client' do - let(:link) { instance_double(Fintoc::Movements::Link) } - let(:account) { instance_double(Fintoc::Movements::Account) } + let(:link) { instance_double(Fintoc::V1::Link) } + let(:account) { instance_double(Fintoc::V1::Account) } before do - allow(client.movements.links).to receive(:get).with('token').and_return(link) - allow(client.movements.links).to receive(:list).and_return([link]) - allow(client.movements.links).to receive(:delete).with('link_id').and_return(true) + allow(client.v1.links).to receive(:get).with('token').and_return(link) + allow(client.v1.links).to receive(:list).and_return([link]) + allow(client.v1.links).to receive(:delete).with('link_id').and_return(true) allow(link).to receive(:find).with(id: 'account_id').and_return(account) end diff --git a/spec/lib/fintoc/movements/account_spec.rb b/spec/lib/fintoc/v1/account_spec.rb similarity index 83% rename from spec/lib/fintoc/movements/account_spec.rb rename to spec/lib/fintoc/v1/account_spec.rb index fb4b85f..889a889 100644 --- a/spec/lib/fintoc/movements/account_spec.rb +++ b/spec/lib/fintoc/v1/account_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/movements/resources/account' +require 'fintoc/v1/resources/account' -RSpec.describe Fintoc::Movements::Account do +RSpec.describe Fintoc::V1::Account do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { Fintoc::Movements::Client.new(api_key) } + let(:client) { Fintoc::V1::Client.new(api_key) } let(:data) do { @@ -49,7 +49,7 @@ it "get the last 30 account's movements", :vcr do movements = linked_account.get_movements expect(movements.size).to be <= 30 - expect(movements).to all(be_a(Fintoc::Movements::Movement)) + expect(movements).to all(be_a(Fintoc::V1::Movement)) end end @@ -57,14 +57,14 @@ it "get account's movements with arguments", :vcr do movements = linked_account.get_movements(since: '2020-08-15') linked_account.show_movements - expect(movements).to all(be_a(Fintoc::Movements::Movement)) + expect(movements).to all(be_a(Fintoc::V1::Movement)) end end describe '#update_balance' do it "update account's movements", :vcr do movements = linked_account.update_movements - expect(movements).to all(be_a(Fintoc::Movements::Movement)) + expect(movements).to all(be_a(Fintoc::V1::Movement)) end end end diff --git a/spec/lib/fintoc/movements/balance_spec.rb b/spec/lib/fintoc/v1/balance_spec.rb similarity index 78% rename from spec/lib/fintoc/movements/balance_spec.rb rename to spec/lib/fintoc/v1/balance_spec.rb index 199d52a..30675d7 100644 --- a/spec/lib/fintoc/movements/balance_spec.rb +++ b/spec/lib/fintoc/v1/balance_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/resources/balance' +require 'fintoc/v1/resources/balance' -RSpec.describe Fintoc::Movements::Balance do +RSpec.describe Fintoc::V1::Balance do let(:data) { { available: 1000, current: 500, limit: 10 } } let(:balance) { described_class.new(**data) } diff --git a/spec/lib/fintoc/movements/client_spec.rb b/spec/lib/fintoc/v1/client_spec.rb similarity index 82% rename from spec/lib/fintoc/movements/client_spec.rb rename to spec/lib/fintoc/v1/client_spec.rb index 649336c..7754cae 100644 --- a/spec/lib/fintoc/movements/client_spec.rb +++ b/spec/lib/fintoc/v1/client_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/client/client' +require 'fintoc/v1/client/client' -RSpec.describe Fintoc::Movements::Client do +RSpec.describe Fintoc::V1::Client do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } let(:client) { described_class.new(api_key) } @@ -13,7 +13,7 @@ describe '#to_s' do it 'returns a formatted string representation' do expect(client.to_s) - .to include('Fintoc::Movements::Client') + .to include('Fintoc::V1::Client') .and include('πŸ”‘=') end end diff --git a/spec/lib/fintoc/movements/institution_spec.rb b/spec/lib/fintoc/v1/institution_spec.rb similarity index 79% rename from spec/lib/fintoc/movements/institution_spec.rb rename to spec/lib/fintoc/v1/institution_spec.rb index 93e42f1..d2aa9f2 100644 --- a/spec/lib/fintoc/movements/institution_spec.rb +++ b/spec/lib/fintoc/v1/institution_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/resources/institution' +require 'fintoc/v1/resources/institution' -RSpec.describe Fintoc::Movements::Institution do +RSpec.describe Fintoc::V1::Institution do let(:data) do { id: 'cl_banco_de_chile', name: 'Banco de Chile', country: 'cl' } end diff --git a/spec/lib/fintoc/movements/link_spec.rb b/spec/lib/fintoc/v1/link_spec.rb similarity index 92% rename from spec/lib/fintoc/movements/link_spec.rb rename to spec/lib/fintoc/v1/link_spec.rb index 5d357c0..3a04446 100644 --- a/spec/lib/fintoc/movements/link_spec.rb +++ b/spec/lib/fintoc/v1/link_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/resources/link' +require 'fintoc/v1/resources/link' -RSpec.describe Fintoc::Movements::Link do +RSpec.describe Fintoc::V1::Link do let(:data) do { id: 'nMNejK7BT8oGbvO4', @@ -60,7 +60,7 @@ it 'returns and valid checking account if the arg is type: "checking_account"' do checking_account = link.find(type: 'checking_account') data_acc = data[:accounts][0] - expect(checking_account).to be_an_instance_of(Fintoc::Movements::Account) + expect(checking_account).to be_an_instance_of(Fintoc::V1::Account) expect(checking_account.to_s) .to( eq( diff --git a/spec/lib/fintoc/movements/managers/links_manager_spec.rb b/spec/lib/fintoc/v1/managers/links_manager_spec.rb similarity index 84% rename from spec/lib/fintoc/movements/managers/links_manager_spec.rb rename to spec/lib/fintoc/v1/managers/links_manager_spec.rb index 0360931..725c637 100644 --- a/spec/lib/fintoc/movements/managers/links_manager_spec.rb +++ b/spec/lib/fintoc/v1/managers/links_manager_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/movements/managers/links_manager' +require 'fintoc/v1/managers/links_manager' -RSpec.describe Fintoc::Movements::Managers::LinksManager do +RSpec.describe Fintoc::V1::Managers::LinksManager do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { Fintoc::Movements::Client.new(api_key) } + let(:client) { Fintoc::V1::Client.new(api_key) } let(:get_proc) { instance_double(Proc) } let(:post_proc) { instance_double(Proc) } let(:delete_proc) { instance_double(Proc) } @@ -46,14 +46,14 @@ .with("links/#{link_id}") .and_return(true) - allow(Fintoc::Movements::Link).to receive(:new) + allow(Fintoc::V1::Link).to receive(:new) end describe '#links' do describe '#get' do it 'calls build_link with the response' do manager.get(link_token) - expect(Fintoc::Movements::Link) + expect(Fintoc::V1::Link) .to have_received(:new).with(**first_link_data, client:) end end @@ -61,9 +61,9 @@ describe '#list' do it 'calls build_link with the response' do manager.list - expect(Fintoc::Movements::Link) + expect(Fintoc::V1::Link) .to have_received(:new).with(**first_link_data, client:) - expect(Fintoc::Movements::Link) + expect(Fintoc::V1::Link) .to have_received(:new).with(**second_link_data, client:) end end diff --git a/spec/lib/fintoc/movements/movement_spec.rb b/spec/lib/fintoc/v1/movement_spec.rb similarity index 97% rename from spec/lib/fintoc/movements/movement_spec.rb rename to spec/lib/fintoc/v1/movement_spec.rb index 6ba258b..5b9872c 100644 --- a/spec/lib/fintoc/movements/movement_spec.rb +++ b/spec/lib/fintoc/v1/movement_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/resources/movement' +require 'fintoc/v1/resources/movement' -RSpec.describe Fintoc::Movements::Movement do +RSpec.describe Fintoc::V1::Movement do let(:data) do { id: 'BO381oEATXonG6bj', diff --git a/spec/lib/fintoc/movements/transfer_account_spec.rb b/spec/lib/fintoc/v1/transfer_account_spec.rb similarity index 78% rename from spec/lib/fintoc/movements/transfer_account_spec.rb rename to spec/lib/fintoc/v1/transfer_account_spec.rb index eab3037..20ee680 100644 --- a/spec/lib/fintoc/movements/transfer_account_spec.rb +++ b/spec/lib/fintoc/v1/transfer_account_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/movements/resources/transfer_account' +require 'fintoc/v1/resources/transfer_account' -RSpec.describe Fintoc::Movements::TransferAccount do +RSpec.describe Fintoc::V1::TransferAccount do let(:data) do { holder_id: '771806538', diff --git a/spec/lib/fintoc/transfers/account_number_spec.rb b/spec/lib/fintoc/v2/account_number_spec.rb similarity index 96% rename from spec/lib/fintoc/transfers/account_number_spec.rb rename to spec/lib/fintoc/v2/account_number_spec.rb index 8b164fb..4446f77 100644 --- a/spec/lib/fintoc/transfers/account_number_spec.rb +++ b/spec/lib/fintoc/v2/account_number_spec.rb @@ -1,10 +1,10 @@ -require 'fintoc/transfers/resources/account_number' +require 'fintoc/v2/resources/account_number' -RSpec.describe Fintoc::Transfers::AccountNumber do +RSpec.describe Fintoc::V2::AccountNumber do subject(:account_number) { described_class.new(**data) } let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } - let(:client) { Fintoc::Transfers::Client.new(api_key) } + let(:client) { Fintoc::V2::Client.new(api_key) } let(:data) do { @@ -187,7 +187,7 @@ end describe '#simulate_receive_transfer' do - let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } + let(:expected_transfer) { instance_double(Fintoc::V2::Transfer) } context 'when in test mode' do before do diff --git a/spec/lib/fintoc/transfers/account_spec.rb b/spec/lib/fintoc/v2/account_spec.rb similarity index 96% rename from spec/lib/fintoc/transfers/account_spec.rb rename to spec/lib/fintoc/v2/account_spec.rb index f71ac6f..956af9c 100644 --- a/spec/lib/fintoc/transfers/account_spec.rb +++ b/spec/lib/fintoc/v2/account_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/transfers/resources/account' +require 'fintoc/v2/resources/account' -RSpec.describe Fintoc::Transfers::Account do +RSpec.describe Fintoc::V2::Account do let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } - let(:client) { Fintoc::Transfers::Client.new(api_key) } + let(:client) { Fintoc::V2::Client.new(api_key) } let(:entity_data) do { @@ -178,7 +178,7 @@ end describe '#simulate_receive_transfer' do - let(:expected_transfer) { instance_double(Fintoc::Transfers::Transfer) } + let(:expected_transfer) { instance_double(Fintoc::V2::Transfer) } context 'when in test mode' do before do diff --git a/spec/lib/fintoc/transfers/account_verification_spec.rb b/spec/lib/fintoc/v2/account_verification_spec.rb similarity index 95% rename from spec/lib/fintoc/transfers/account_verification_spec.rb rename to spec/lib/fintoc/v2/account_verification_spec.rb index d865f66..50bf6e6 100644 --- a/spec/lib/fintoc/transfers/account_verification_spec.rb +++ b/spec/lib/fintoc/v2/account_verification_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/transfers/resources/account_verification' +require 'fintoc/v2/resources/account_verification' -RSpec.describe Fintoc::Transfers::AccountVerification do +RSpec.describe Fintoc::V2::AccountVerification do let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } - let(:client) { Fintoc::Transfers::Client.new(api_key) } + let(:client) { Fintoc::V2::Client.new(api_key) } let(:counterparty_data) do { diff --git a/spec/lib/fintoc/transfers/client_spec.rb b/spec/lib/fintoc/v2/client_spec.rb similarity index 85% rename from spec/lib/fintoc/transfers/client_spec.rb rename to spec/lib/fintoc/v2/client_spec.rb index 5002069..7c53eb1 100644 --- a/spec/lib/fintoc/transfers/client_spec.rb +++ b/spec/lib/fintoc/v2/client_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/client/client' +require 'fintoc/v2/client/client' -RSpec.describe Fintoc::Transfers::Client do +RSpec.describe Fintoc::V2::Client do let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } let(:jws_private_key) { nil } let(:client) { described_class.new(api_key, jws_private_key: jws_private_key) } @@ -14,7 +14,7 @@ describe '#to_s' do it 'returns a formatted string representation' do expect(client.to_s) - .to include('Fintoc::Transfers::Client') + .to include('Fintoc::V2::Client') .and include('πŸ”‘=') end end diff --git a/spec/lib/fintoc/transfers/entity_spec.rb b/spec/lib/fintoc/v2/entity_spec.rb similarity index 92% rename from spec/lib/fintoc/transfers/entity_spec.rb rename to spec/lib/fintoc/v2/entity_spec.rb index 59ddc6d..067d3de 100644 --- a/spec/lib/fintoc/transfers/entity_spec.rb +++ b/spec/lib/fintoc/v2/entity_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/transfers/resources/entity' +require 'fintoc/v2/resources/entity' -RSpec.describe Fintoc::Transfers::Entity do +RSpec.describe Fintoc::V2::Entity do let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } - let(:client) { Fintoc::Transfers::Client.new(api_key) } + let(:client) { Fintoc::V2::Client.new(api_key) } let(:data) do { diff --git a/spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb b/spec/lib/fintoc/v2/managers/account_numbers_manager_spec.rb similarity index 87% rename from spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb rename to spec/lib/fintoc/v2/managers/account_numbers_manager_spec.rb index 353dc8c..9a83494 100644 --- a/spec/lib/fintoc/transfers/managers/account_numbers_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/account_numbers_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/account_numbers_manager' +require 'fintoc/v2/managers/account_numbers_manager' -RSpec.describe Fintoc::Transfers::Managers::AccountNumbersManager do +RSpec.describe Fintoc::V2::Managers::AccountNumbersManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:get_proc) { instance_double(Proc) } let(:post_proc) { instance_double(Proc) } @@ -64,13 +64,13 @@ .with('account_numbers', account_id:, description: 'My account number', metadata: {}) .and_return(first_account_number_data) - allow(Fintoc::Transfers::AccountNumber).to receive(:new) + allow(Fintoc::V2::AccountNumber).to receive(:new) end describe '#create' do it 'calls build_account_number with the response' do manager.create(account_id: 'acc_123', description: 'My account number', metadata: {}) - expect(Fintoc::Transfers::AccountNumber) + expect(Fintoc::V2::AccountNumber) .to have_received(:new).with(**first_account_number_data, client:) end end @@ -78,7 +78,7 @@ describe '#get' do it 'calls build_account_number with the response' do manager.get('acno_123') - expect(Fintoc::Transfers::AccountNumber) + expect(Fintoc::V2::AccountNumber) .to have_received(:new).with(**first_account_number_data, client:) end end @@ -86,9 +86,9 @@ describe '#list' do it 'calls build_account_number for each response' do manager.list - expect(Fintoc::Transfers::AccountNumber) + expect(Fintoc::V2::AccountNumber) .to have_received(:new).with(**first_account_number_data, client:) - expect(Fintoc::Transfers::AccountNumber) + expect(Fintoc::V2::AccountNumber) .to have_received(:new).with(**second_account_number_data, client:) end end @@ -96,7 +96,7 @@ describe '#update' do it 'calls build_account_number with the response' do manager.update('acno_123', description: 'Updated description') - expect(Fintoc::Transfers::AccountNumber) + expect(Fintoc::V2::AccountNumber) .to have_received(:new).with(**updated_account_number_data, client:) end end diff --git a/spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb b/spec/lib/fintoc/v2/managers/account_verifications_manager_spec.rb similarity index 83% rename from spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb rename to spec/lib/fintoc/v2/managers/account_verifications_manager_spec.rb index 4befd11..5c524f4 100644 --- a/spec/lib/fintoc/transfers/managers/account_verifications_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/account_verifications_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/account_verifications_manager' +require 'fintoc/v2/managers/account_verifications_manager' -RSpec.describe Fintoc::Transfers::Managers::AccountVerificationsManager do +RSpec.describe Fintoc::V2::Managers::AccountVerificationsManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:get_proc) { instance_double(Proc) } let(:post_proc) { instance_double(Proc) } @@ -41,13 +41,13 @@ .with('account_verifications', account_number:) .and_return(first_account_verification_data) - allow(Fintoc::Transfers::AccountVerification).to receive(:new) + allow(Fintoc::V2::AccountVerification).to receive(:new) end describe '#create' do it 'calls build_account_verification with the response' do manager.create(account_number: '735969000000203226') - expect(Fintoc::Transfers::AccountVerification) + expect(Fintoc::V2::AccountVerification) .to have_received(:new).with(**first_account_verification_data, client:) end end @@ -55,7 +55,7 @@ describe '#get' do it 'calls build_account_verification with the response' do manager.get('accv_123') - expect(Fintoc::Transfers::AccountVerification) + expect(Fintoc::V2::AccountVerification) .to have_received(:new).with(**first_account_verification_data, client:) end end @@ -63,9 +63,9 @@ describe '#list' do it 'calls build_account_verification for each response item' do manager.list - expect(Fintoc::Transfers::AccountVerification) + expect(Fintoc::V2::AccountVerification) .to have_received(:new).with(**first_account_verification_data, client:) - expect(Fintoc::Transfers::AccountVerification) + expect(Fintoc::V2::AccountVerification) .to have_received(:new).with(**second_account_verification_data, client:) end end diff --git a/spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb b/spec/lib/fintoc/v2/managers/accounts_manager_spec.rb similarity index 88% rename from spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb rename to spec/lib/fintoc/v2/managers/accounts_manager_spec.rb index 205ade7..a13e1df 100644 --- a/spec/lib/fintoc/transfers/managers/accounts_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/accounts_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/accounts_manager' +require 'fintoc/v2/managers/accounts_manager' -RSpec.describe Fintoc::Transfers::Managers::AccountsManager do +RSpec.describe Fintoc::V2::Managers::AccountsManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:get_proc) { instance_double(Proc) } let(:post_proc) { instance_double(Proc) } @@ -72,13 +72,13 @@ .with('accounts', entity_id:, description: 'My account') .and_return(first_account_data) - allow(Fintoc::Transfers::Account).to receive(:new) + allow(Fintoc::V2::Account).to receive(:new) end describe '#create' do it 'calls build_account with the response' do manager.create(entity_id:, description: 'My account') - expect(Fintoc::Transfers::Account) + expect(Fintoc::V2::Account) .to have_received(:new).with(**first_account_data, client:) end end @@ -86,7 +86,7 @@ describe '#get' do it 'calls build_account with the response' do manager.get(account_id) - expect(Fintoc::Transfers::Account) + expect(Fintoc::V2::Account) .to have_received(:new).with(**first_account_data, client:) end end @@ -94,9 +94,9 @@ describe '#list' do it 'calls build_account for each response' do manager.list - expect(Fintoc::Transfers::Account) + expect(Fintoc::V2::Account) .to have_received(:new).with(**first_account_data, client:) - expect(Fintoc::Transfers::Account) + expect(Fintoc::V2::Account) .to have_received(:new).with(**second_account_data, client:) end end @@ -104,7 +104,7 @@ describe '#update' do it 'calls build_account with the response' do manager.update(account_id, name: 'Updated name') - expect(Fintoc::Transfers::Account) + expect(Fintoc::V2::Account) .to have_received(:new).with(**updated_account_data, client:) end end diff --git a/spec/lib/fintoc/transfers/managers/entities_manager_spec.rb b/spec/lib/fintoc/v2/managers/entities_manager_spec.rb similarity index 75% rename from spec/lib/fintoc/transfers/managers/entities_manager_spec.rb rename to spec/lib/fintoc/v2/managers/entities_manager_spec.rb index db46739..8a27b47 100644 --- a/spec/lib/fintoc/transfers/managers/entities_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/entities_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/entities_manager' +require 'fintoc/v2/managers/entities_manager' -RSpec.describe Fintoc::Transfers::Managers::EntitiesManager do +RSpec.describe Fintoc::V2::Managers::EntitiesManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:get_proc) { instance_double(Proc) } @@ -37,14 +37,14 @@ allow(get_proc).to receive(:call).with("entities/#{entity_id}").and_return(first_entity_data) allow(get_proc).to receive(:call).with('entities').and_return(entities_data) - allow(Fintoc::Transfers::Entity).to receive(:new) + allow(Fintoc::V2::Entity).to receive(:new) end describe '#get' do it 'fetches and builds an entity' do manager.get(entity_id) - expect(Fintoc::Transfers::Entity).to have_received(:new).with(**first_entity_data, client:) + expect(Fintoc::V2::Entity).to have_received(:new).with(**first_entity_data, client:) end end @@ -52,8 +52,8 @@ it 'fetches and builds a list of entities' do manager.list - expect(Fintoc::Transfers::Entity).to have_received(:new).with(**first_entity_data, client:) - expect(Fintoc::Transfers::Entity).to have_received(:new).with(**second_entity_data, client:) + expect(Fintoc::V2::Entity).to have_received(:new).with(**first_entity_data, client:) + expect(Fintoc::V2::Entity).to have_received(:new).with(**second_entity_data, client:) end it 'passes parameters to the API call' do diff --git a/spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb b/spec/lib/fintoc/v2/managers/simulate_manager_spec.rb similarity index 76% rename from spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb rename to spec/lib/fintoc/v2/managers/simulate_manager_spec.rb index 03b709e..7e289d7 100644 --- a/spec/lib/fintoc/transfers/managers/simulate_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/simulate_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/simulate_manager' +require 'fintoc/v2/managers/simulate_manager' -RSpec.describe Fintoc::Transfers::Managers::SimulateManager do +RSpec.describe Fintoc::V2::Managers::SimulateManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:post_proc) { instance_double(Proc) } let(:manager) { described_class.new(client) } @@ -26,14 +26,14 @@ .with('simulate/receive_transfer', account_number_id:, amount:, currency:) .and_return(transfer_data) - allow(Fintoc::Transfers::Transfer).to receive(:new) + allow(Fintoc::V2::Transfer).to receive(:new) end describe '#receive_transfer' do it 'calls build_transfer with the response' do manager.receive_transfer(account_number_id:, amount:, currency:) - expect(Fintoc::Transfers::Transfer).to have_received(:new).with(**transfer_data, client:) + expect(Fintoc::V2::Transfer).to have_received(:new).with(**transfer_data, client:) end end end diff --git a/spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb b/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb similarity index 87% rename from spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb rename to spec/lib/fintoc/v2/managers/transfers_manager_spec.rb index a059db4..70fd199 100644 --- a/spec/lib/fintoc/transfers/managers/transfers_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb @@ -1,6 +1,6 @@ -require 'fintoc/transfers/managers/account_verifications_manager' +require 'fintoc/v2/managers/account_verifications_manager' -RSpec.describe Fintoc::Transfers::Managers::TransfersManager do +RSpec.describe Fintoc::V2::Managers::TransfersManager do let(:client) { instance_double(Fintoc::BaseClient) } let(:get_proc) { instance_double(Proc) } let(:post_proc) { instance_double(Proc) } @@ -68,13 +68,13 @@ .with('transfers/return', transfer_id:) .and_return(first_transfer_data) - allow(Fintoc::Transfers::Transfer).to receive(:new) + allow(Fintoc::V2::Transfer).to receive(:new) end describe '#create' do it 'calls build_transfer with the response' do manager.create(amount: 10000, currency: 'MXN', account_id: 'acc_123', counterparty:) - expect(Fintoc::Transfers::Transfer) + expect(Fintoc::V2::Transfer) .to have_received(:new).with(**first_transfer_data, client:) end end @@ -82,7 +82,7 @@ describe '#get' do it 'calls build_transfer with the response' do manager.get('trf_123') - expect(Fintoc::Transfers::Transfer) + expect(Fintoc::V2::Transfer) .to have_received(:new).with(**first_transfer_data, client:) end end @@ -90,9 +90,9 @@ describe '#list' do it 'calls build_transfer for each response item' do manager.list - expect(Fintoc::Transfers::Transfer) + expect(Fintoc::V2::Transfer) .to have_received(:new).with(**first_transfer_data, client:) - expect(Fintoc::Transfers::Transfer) + expect(Fintoc::V2::Transfer) .to have_received(:new).with(**second_transfer_data, client:) end end @@ -100,7 +100,7 @@ describe '#return' do it 'calls build_transfer with the response' do manager.return('trf_123') - expect(Fintoc::Transfers::Transfer) + expect(Fintoc::V2::Transfer) .to have_received(:new).with(**first_transfer_data, client:) end end diff --git a/spec/lib/fintoc/transfers/transfer_spec.rb b/spec/lib/fintoc/v2/transfer_spec.rb similarity index 98% rename from spec/lib/fintoc/transfers/transfer_spec.rb rename to spec/lib/fintoc/v2/transfer_spec.rb index 7bdfe23..a3ca8e2 100644 --- a/spec/lib/fintoc/transfers/transfer_spec.rb +++ b/spec/lib/fintoc/v2/transfer_spec.rb @@ -1,8 +1,8 @@ -require 'fintoc/transfers/resources/transfer' +require 'fintoc/v2/resources/transfer' -RSpec.describe Fintoc::Transfers::Transfer do +RSpec.describe Fintoc::V2::Transfer do let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } - let(:client) { Fintoc::Transfers::Client.new(api_key) } + let(:client) { Fintoc::V2::Client.new(api_key) } let(:counterparty_data) do { diff --git a/spec/support/shared_examples/clients/account_numbers_client_examples.rb b/spec/support/shared_examples/clients/account_numbers_client_examples.rb index b68a8f7..8a96a9e 100644 --- a/spec/support/shared_examples/clients/account_numbers_client_examples.rb +++ b/spec/support/shared_examples/clients/account_numbers_client_examples.rb @@ -4,7 +4,7 @@ it 'responds to account number-specific methods' do expect(client).to respond_to(:account_numbers) - expect(client.account_numbers).to be_a(Fintoc::Transfers::Managers::AccountNumbersManager) + expect(client.account_numbers).to be_a(Fintoc::V2::Managers::AccountNumbersManager) expect(client.account_numbers) .to respond_to(:create) .and respond_to(:get) @@ -20,7 +20,7 @@ ) expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .to be_an_instance_of(Fintoc::V2::AccountNumber) .and have_attributes( account_id:, description: 'Test account number', @@ -34,7 +34,7 @@ account_number = client.account_numbers.get(account_number_id) expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .to be_an_instance_of(Fintoc::V2::AccountNumber) .and have_attributes( id: account_number_id, object: 'account_number' @@ -46,7 +46,7 @@ it 'returns an array of AccountNumber instances', :vcr do account_numbers = client.account_numbers.list - expect(account_numbers).to all(be_a(Fintoc::Transfers::AccountNumber)) + expect(account_numbers).to all(be_a(Fintoc::V2::AccountNumber)) expect(account_numbers.size).to be >= 1 expect(account_numbers.first.id).to eq(account_number_id) end @@ -60,7 +60,7 @@ ) expect(account_number) - .to be_an_instance_of(Fintoc::Transfers::AccountNumber) + .to be_an_instance_of(Fintoc::V2::AccountNumber) .and have_attributes( id: account_number_id, description: updated_description diff --git a/spec/support/shared_examples/clients/account_verifications_client_examples.rb b/spec/support/shared_examples/clients/account_verifications_client_examples.rb index 5ddbee4..9dd7082 100644 --- a/spec/support/shared_examples/clients/account_verifications_client_examples.rb +++ b/spec/support/shared_examples/clients/account_verifications_client_examples.rb @@ -38,7 +38,7 @@ it 'responds to account verification-specific methods' do expect(client).to respond_to(:account_verifications) expect(client.account_verifications) - .to be_a(Fintoc::Transfers::Managers::AccountVerificationsManager) + .to be_a(Fintoc::V2::Managers::AccountVerificationsManager) expect(client.account_verifications) .to respond_to(:create) .and respond_to(:get) @@ -51,7 +51,7 @@ account_verification = client.account_verifications.create(account_number:) expect(account_verification) - .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .to be_an_instance_of(Fintoc::V2::AccountVerification) .and have_attributes( object: 'account_verification', status: 'pending' @@ -64,7 +64,7 @@ account_verification = client.account_verifications.get(account_verification_id) expect(account_verification) - .to be_an_instance_of(Fintoc::Transfers::AccountVerification) + .to be_an_instance_of(Fintoc::V2::AccountVerification) .and have_attributes( id: account_verification_id, object: 'account_verification' @@ -76,7 +76,7 @@ it 'returns an array of AccountVerification instances', :vcr do account_verifications = client.account_verifications.list - expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + expect(account_verifications).to all(be_a(Fintoc::V2::AccountVerification)) expect(account_verifications.size).to be >= 1 end @@ -86,7 +86,7 @@ limit: 10 ) - expect(account_verifications).to all(be_a(Fintoc::Transfers::AccountVerification)) + expect(account_verifications).to all(be_a(Fintoc::V2::AccountVerification)) end end end diff --git a/spec/support/shared_examples/clients/accounts_client_examples.rb b/spec/support/shared_examples/clients/accounts_client_examples.rb index fb787f0..d8d5943 100644 --- a/spec/support/shared_examples/clients/accounts_client_examples.rb +++ b/spec/support/shared_examples/clients/accounts_client_examples.rb @@ -4,7 +4,7 @@ it 'responds to account-specific methods' do expect(client).to respond_to(:accounts) - expect(client.accounts).to be_a(Fintoc::Transfers::Managers::AccountsManager) + expect(client.accounts).to be_a(Fintoc::V2::Managers::AccountsManager) expect(client.accounts) .to respond_to(:create) .and respond_to(:get) @@ -18,7 +18,7 @@ account = client.accounts.create(entity_id:, description: 'Test account') expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) + .to be_an_instance_of(Fintoc::V2::Account) .and have_attributes( description: 'Test account', currency: 'MXN', @@ -32,7 +32,7 @@ account = client.accounts.get(account_id) expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) + .to be_an_instance_of(Fintoc::V2::Account) .and have_attributes( id: account_id, description: 'Test account' @@ -44,7 +44,7 @@ it 'returns an array of Account instances', :vcr do accounts = client.accounts.list - expect(accounts).to all(be_a(Fintoc::Transfers::Account)) + expect(accounts).to all(be_a(Fintoc::V2::Account)) expect(accounts.size).to be >= 1 expect(accounts.first.id).to eq(account_id) end @@ -56,7 +56,7 @@ account = client.accounts.update(account_id, description: updated_description) expect(account) - .to be_an_instance_of(Fintoc::Transfers::Account) + .to be_an_instance_of(Fintoc::V2::Account) .and have_attributes( id: account_id, description: updated_description diff --git a/spec/support/shared_examples/clients/entities_client_examples.rb b/spec/support/shared_examples/clients/entities_client_examples.rb index 7c3117c..d237fb3 100644 --- a/spec/support/shared_examples/clients/entities_client_examples.rb +++ b/spec/support/shared_examples/clients/entities_client_examples.rb @@ -3,7 +3,7 @@ it 'provides an entities manager' do expect(client).to respond_to(:entities) - expect(client.entities).to be_a(Fintoc::Transfers::Managers::EntitiesManager) + expect(client.entities).to be_a(Fintoc::V2::Managers::EntitiesManager) expect(client.entities) .to respond_to(:get) .and respond_to(:list) @@ -15,7 +15,7 @@ entity = client.entities.get(entity_id) expect(entity) - .to be_an_instance_of(Fintoc::Transfers::Entity) + .to be_an_instance_of(Fintoc::V2::Entity) .and have_attributes( id: entity_id, holder_name: 'Fintoc' @@ -27,7 +27,7 @@ it 'returns an array of Entity instances', :vcr do entities = client.entities.list - expect(entities).to all(be_a(Fintoc::Transfers::Entity)) + expect(entities).to all(be_a(Fintoc::V2::Entity)) expect(entities.size).to eq(1) expect(entities.first.id).to eq('ent_31t0VhhrAXASFQTVYfCfIBnljbT') end diff --git a/spec/support/shared_examples/clients/links_client_examples.rb b/spec/support/shared_examples/clients/links_client_examples.rb index 32ac82b..318350e 100644 --- a/spec/support/shared_examples/clients/links_client_examples.rb +++ b/spec/support/shared_examples/clients/links_client_examples.rb @@ -3,7 +3,7 @@ it 'responds to link-specific methods' do expect(client).to respond_to(:links) - expect(client.links).to be_a(Fintoc::Movements::Managers::LinksManager) + expect(client.links).to be_a(Fintoc::V1::Managers::LinksManager) expect(client.links) .to respond_to(:get) .and respond_to(:list) @@ -14,14 +14,14 @@ describe '#get' do it 'get the link from a given link token', :vcr do link = client.links.get(link_token) - expect(link).to be_an_instance_of(Fintoc::Movements::Link) + expect(link).to be_an_instance_of(Fintoc::V1::Link) end end describe '#list' do it 'get all the links from a given link token', :vcr do links = client.links.list - expect(links).to all(be_a(Fintoc::Movements::Link)) + expect(links).to all(be_a(Fintoc::V1::Link)) end end end diff --git a/spec/support/shared_examples/clients/simulate_client_examples.rb b/spec/support/shared_examples/clients/simulate_client_examples.rb index 0989c83..ff731be 100644 --- a/spec/support/shared_examples/clients/simulate_client_examples.rb +++ b/spec/support/shared_examples/clients/simulate_client_examples.rb @@ -1,7 +1,7 @@ RSpec.shared_examples 'a client with simulate manager' do it 'responds to simulate-specific methods' do expect(client).to respond_to(:simulate) - expect(client.simulate).to be_a(Fintoc::Transfers::Managers::SimulateManager) + expect(client.simulate).to be_a(Fintoc::V2::Managers::SimulateManager) expect(client.simulate) .to respond_to(:receive_transfer) end @@ -20,7 +20,7 @@ transfer = client.simulate.receive_transfer(**simulate_transfer_data) expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) + .to be_an_instance_of(Fintoc::V2::Transfer) .and have_attributes( amount: simulate_transfer_data[:amount], currency: simulate_transfer_data[:currency], diff --git a/spec/support/shared_examples/clients/transfers_client_examples.rb b/spec/support/shared_examples/clients/transfers_client_examples.rb index 6e033d4..03f5fff 100644 --- a/spec/support/shared_examples/clients/transfers_client_examples.rb +++ b/spec/support/shared_examples/clients/transfers_client_examples.rb @@ -58,7 +58,7 @@ it 'responds to transfer-specific methods' do expect(client).to respond_to(:transfers) - expect(client.transfers).to be_a(Fintoc::Transfers::Managers::TransfersManager) + expect(client.transfers).to be_a(Fintoc::V2::Managers::TransfersManager) expect(client.transfers) .to respond_to(:create) .and respond_to(:get) @@ -72,7 +72,7 @@ transfer = client.transfers.create(**transfer_data) expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) + .to be_an_instance_of(Fintoc::V2::Transfer) .and have_attributes( amount: 50000, currency: 'MXN', @@ -88,7 +88,7 @@ transfer = client.transfers.get(transfer_id) expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) + .to be_an_instance_of(Fintoc::V2::Transfer) .and have_attributes( id: transfer_id, object: 'transfer' @@ -100,14 +100,14 @@ it 'returns an array of Transfer instances', :vcr do transfers = client.transfers.list - expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers).to all(be_a(Fintoc::V2::Transfer)) expect(transfers.size).to be >= 1 end it 'accepts filtering parameters', :vcr do transfers = client.transfers.list(status: 'succeeded', direction: 'outbound') - expect(transfers).to all(be_a(Fintoc::Transfers::Transfer)) + expect(transfers).to all(be_a(Fintoc::V2::Transfer)) expect(transfers).to all(have_attributes(status: 'succeeded', direction: 'outbound')) end end @@ -119,7 +119,7 @@ transfer = client.transfers.return(transfer_id) expect(transfer) - .to be_an_instance_of(Fintoc::Transfers::Transfer) + .to be_an_instance_of(Fintoc::V2::Transfer) .and have_attributes( id: transfer_id, status: 'return_pending' diff --git a/spec/vcr/Fintoc_Movements_Account/_get_movements/get_the_last_30_account_s_movements.yml b/spec/vcr/Fintoc_V1_Account/_get_movements/get_the_last_30_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Account/_get_movements/get_the_last_30_account_s_movements.yml rename to spec/vcr/Fintoc_V1_Account/_get_movements/get_the_last_30_account_s_movements.yml diff --git a/spec/vcr/Fintoc_Movements_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml b/spec/vcr/Fintoc_V1_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml rename to spec/vcr/Fintoc_V1_Account/_movement_with_since_argument/get_account_s_movements_with_arguments.yml diff --git a/spec/vcr/Fintoc_Movements_Account/_movements/get_the_last_30_account_s_movements.yml b/spec/vcr/Fintoc_V1_Account/_movements/get_the_last_30_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Account/_movements/get_the_last_30_account_s_movements.yml rename to spec/vcr/Fintoc_V1_Account/_movements/get_the_last_30_account_s_movements.yml diff --git a/spec/vcr/Fintoc_Movements_Account/_update_balance/update_account_s_movements.yml b/spec/vcr/Fintoc_V1_Account/_update_balance/update_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Account/_update_balance/update_account_s_movements.yml rename to spec/vcr/Fintoc_V1_Account/_update_balance/update_account_s_movements.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml b/spec/vcr/Fintoc_V1_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml rename to spec/vcr/Fintoc_V1_Client/behaves_like_a_client_with_links_manager/_links/_get/get_the_link_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml b/spec/vcr/Fintoc_V1_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml similarity index 100% rename from spec/vcr/Fintoc_Movements_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml rename to spec/vcr/Fintoc_V1_Client/behaves_like_a_client_with_links_manager/_links/_list/get_all_the_links_from_a_given_link_token.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_create/returns_an_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_get/returns_an_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_list/returns_an_array_of_AccountNumber_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_numbers_manager/_account_numbers/_update/returns_an_updated_AccountNumber_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_create/returns_an_AccountVerification_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_get/returns_an_AccountVerification_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/accepts_filtering_parameters.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_account_verifications_manager/_account_verifications/_list/returns_an_array_of_AccountVerification_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_create/returns_an_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_get/returns_an_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_list/returns_an_array_of_Account_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_accounts_manager/_accounts/_update/returns_an_updated_Account_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_entities_manager/_entities/_get/returns_an_Entity_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_entities_manager/_entities/_list/returns_an_array_of_Entity_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_simulate_manager/_simulate/_receive_transfer/simulates_receiving_a_transfer_and_returns_Transfer_object.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_create/returns_a_Transfer_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_get/returns_a_Transfer_instance.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/accepts_filtering_parameters.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_list/returns_an_array_of_Transfer_instances.yml diff --git a/spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml b/spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml similarity index 100% rename from spec/vcr/Fintoc_Transfers_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml rename to spec/vcr/Fintoc_V2_Client/behaves_like_a_client_with_transfers_manager/_transfers/_return/returns_a_Transfer_instance_with_return_pending_status.yml From 4b440cb80580710435f1922c144493387165bcf7 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 12:02:57 -0400 Subject: [PATCH 56/72] fix(readme): Fix readme client --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d5f0cfc..d308eb3 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ Or install it yourself as: ```ruby require 'fintoc' -client = Fintoc::Client.new('sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx') -link = client_v1.links.get('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W') +client_v1 = Fintoc::V1::Client.new('api_key') +link = client_v1.links.get('link_token') account = link.find(type: 'checking_account') # Get the last 30 movements From 9b4a61677b39770977df72d90ab4df40a492ac3a Mon Sep 17 00:00:00 2001 From: Danny Fuentes Date: Fri, 5 Sep 2025 15:40:13 -0400 Subject: [PATCH 57/72] feat: add webhook signature module --- lib/fintoc/webhook_signature.rb | 73 ++++++++++++ spec/lib/fintoc/webhook_signature_spec.rb | 132 ++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 lib/fintoc/webhook_signature.rb create mode 100644 spec/lib/fintoc/webhook_signature_spec.rb diff --git a/lib/fintoc/webhook_signature.rb b/lib/fintoc/webhook_signature.rb new file mode 100644 index 0000000..f0bcd49 --- /dev/null +++ b/lib/fintoc/webhook_signature.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'openssl' +require 'time' +require 'fintoc/errors' + +module Fintoc + class WebhookSignature + EXPECTED_SCHEME = 'v1' + DEFAULT_TOLERANCE = 300 # 5 minutes + + class << self + def verify_header(payload, header, secret, tolerance = DEFAULT_TOLERANCE) # rubocop:disable Naming/PredicateMethod + timestamp, signatures = parse_header(header) + + verify_timestamp(timestamp, tolerance) if tolerance + + expected_signature = compute_signature(payload, timestamp, secret) + signature = signatures[EXPECTED_SCHEME] + + if signature.nil? || signature.empty? # rubocop:disable Rails/Blank + raise Fintoc::Errors::WebhookSignatureError.new("No #{EXPECTED_SCHEME} signature found") + end + + unless same_signatures?(signature, expected_signature) + raise Fintoc::Errors::WebhookSignatureError.new('Signature mismatch') + end + + true + end + + def compute_signature(payload, timestamp, secret) + signed_payload = "#{timestamp}.#{payload}" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, signed_payload) + end + + private + + def parse_header(header) + elements = header.split(',').map(&:strip) + pairs = elements.map { |element| element.split('=', 2).map(&:strip) } + pairs = pairs.to_h + + if pairs['t'].nil? || pairs['t'].empty? # rubocop:disable Rails/Blank + raise Fintoc::Errors::WebhookSignatureError.new('Missing timestamp in header') + end + + timestamp = pairs['t'].to_i + signatures = pairs.except('t') + + [timestamp, signatures] + rescue StandardError => e + raise Fintoc::Errors::WebhookSignatureError.new( + 'Unable to extract timestamp and signatures from header' + ), cause: e + end + + def verify_timestamp(timestamp, tolerance) + now = Time.now.to_i + + if timestamp < (now - tolerance) + raise Fintoc::Errors::WebhookSignatureError.new( + "Timestamp outside the tolerance zone (#{timestamp})" + ) + end + end + + def same_signatures?(signature, expected_signature) + OpenSSL.secure_compare(expected_signature, signature) + end + end + end +end diff --git a/spec/lib/fintoc/webhook_signature_spec.rb b/spec/lib/fintoc/webhook_signature_spec.rb new file mode 100644 index 0000000..7d2cb22 --- /dev/null +++ b/spec/lib/fintoc/webhook_signature_spec.rb @@ -0,0 +1,132 @@ +require 'fintoc/webhook_signature' + +RSpec.describe Fintoc::WebhookSignature do + let(:secret) { 'test_secret_key' } + let(:payload) { '{"test": "payload"}' } + let(:frozen_time) { Time.parse('2025-09-05 10:10:10 UTC') } + let(:timestamp) { frozen_time.to_i } + let(:signature) { OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, "#{timestamp}.#{payload}") } + let(:valid_header) { "t=#{timestamp},v1=#{signature}" } + + before do + allow(Time).to receive_messages(current: frozen_time, now: frozen_time) + end + + describe '.verify_header' do + context 'when signature is valid' do + it 'returns true' do + expect(described_class.verify_header(payload, valid_header, secret)).to be true + end + + it 'returns true with custom tolerance' do + expect(described_class.verify_header(payload, valid_header, secret, 600)).to be true + end + + it 'returns true when tolerance is nil (no timestamp verification)' do + old_timestamp = timestamp - 1000 + old_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, + "#{old_timestamp}.#{payload}") + old_header = "t=#{old_timestamp},v1=#{old_signature}" + + expect(described_class.verify_header(payload, old_header, secret, nil)).to be true + end + end + + context 'when header does not have a timestamp' do + let(:invalid_header) { "v1=#{signature}" } + + it 'raises WebhookSignatureError' do + expect do + described_class.verify_header(payload, invalid_header, secret) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nUnable to extract timestamp and signatures from header\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + end + + context 'when timestamp is too old' do + let(:old_timestamp) { timestamp - 400 } + let(:old_signature) do + OpenSSL::HMAC.hexdigest( + OpenSSL::Digest.new('sha256'), + secret, + "#{old_timestamp}.#{payload}" + ) + end + let(:old_header) { "t=#{old_timestamp},v1=#{old_signature}" } + + it 'raises WebhookSignatureError with default tolerance' do + expect do + described_class.verify_header(payload, old_header, secret) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nTimestamp outside the tolerance zone (#{old_timestamp})\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + + it 'raises WebhookSignatureError with custom tolerance' do + expect do + described_class.verify_header(payload, old_header, secret, 100) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nTimestamp outside the tolerance zone (#{old_timestamp})\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + end + + context 'when header does not contain signature scheme' do + let(:header_without_scheme) { "t=#{timestamp}" } + + it 'raises WebhookSignatureError' do + expect do + described_class.verify_header(payload, header_without_scheme, secret) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nNo v1 signature found\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + end + + context 'when signature and expected signature do not match' do + let(:wrong_signature) { 'wrong_signature_value' } + let(:invalid_header) { "t=#{timestamp},v1=#{wrong_signature}" } + + it 'raises WebhookSignatureError' do + expect do + described_class.verify_header(payload, invalid_header, secret) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nSignature mismatch\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + end + + context 'with different signature schemes' do + it 'ignores non-v1 signatures and uses v1' do + header_with_multiple = "t=#{timestamp},v0=wrong_signature,v1=#{signature},v2=another_wrong" + + expect(described_class.verify_header(payload, header_with_multiple, secret)).to be true + end + end + + context 'with malformed headers' do + it 'raises WebhookSignatureError' do + header_empty_timestamp = "t=,v1=#{signature}" + + expect do + described_class.verify_header(payload, header_empty_timestamp, secret) + end.to raise_error( + Fintoc::Errors::WebhookSignatureError, + "\nUnable to extract timestamp and signatures from header\n " \ + 'Please check the docs at: https://docs.fintoc.com/reference/errors' + ) + end + end + end +end From 940ac16f45620772d64143b24485a721fec56c16 Mon Sep 17 00:00:00 2001 From: Danny Fuentes Date: Fri, 5 Sep 2025 15:40:49 -0400 Subject: [PATCH 58/72] feat: add webhook signature error --- lib/fintoc/errors.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/fintoc/errors.rb b/lib/fintoc/errors.rb index 465bf45..ab65162 100644 --- a/lib/fintoc/errors.rb +++ b/lib/fintoc/errors.rb @@ -154,6 +154,9 @@ class InternalServerError < FintocError; end class UnrecognizedRequestError < FintocError; end class CoreResponseError < FintocError; end + # Webhook Errors + class WebhookSignatureError < FintocError; end + # Legacy Errors (keeping existing ones for backward compatibility and just in case) class LinkError < FintocError; end class InstitutionError < FintocError; end From bc67b4f111a7b196d1101720aa6dcb598dadf57f Mon Sep 17 00:00:00 2001 From: Danny Fuentes Date: Fri, 5 Sep 2025 15:41:06 -0400 Subject: [PATCH 59/72] feat: include webhook signature module to fintoc --- lib/fintoc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/fintoc.rb b/lib/fintoc.rb index 1224934..4743214 100644 --- a/lib/fintoc.rb +++ b/lib/fintoc.rb @@ -1,6 +1,7 @@ require 'fintoc/version' require 'fintoc/errors' require 'fintoc/client' +require 'fintoc/webhook_signature' require 'config/initializers/money' From e6c780e7922faf4a81b37480cc1e52b286e5cb19 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:49:50 -0400 Subject: [PATCH 60/72] refactor(errors): Remove redundant to_s at Errors Since it is implied by inheriting from StandardError --- lib/fintoc/errors.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/fintoc/errors.rb b/lib/fintoc/errors.rb index ab65162..9470109 100644 --- a/lib/fintoc/errors.rb +++ b/lib/fintoc/errors.rb @@ -13,10 +13,6 @@ def initialize(message, doc_url = Fintoc::Constants::GENERAL_DOC_URL) def message "\n#{@message}\n Please check the docs at: #{@doc_url}" end - - def to_s - message - end end # 400 Bad Request Errors From 4c7ce5a12dc1765fe52ab3787adc1c3333f86b54 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:50:31 -0400 Subject: [PATCH 61/72] refactor(v1/accounts): Move Headers to frozen constant --- lib/fintoc/v1/resources/account.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/fintoc/v1/resources/account.rb b/lib/fintoc/v1/resources/account.rb index eb214e0..ec9ddb1 100644 --- a/lib/fintoc/v1/resources/account.rb +++ b/lib/fintoc/v1/resources/account.rb @@ -11,6 +11,8 @@ class Account attr_reader :id, :name, :holder_name, :currency, :type, :refreshed_at, :official_name, :number, :holder_id, :balance, :movements + HEADERS = ['#', 'Amount', 'Currency', 'Description', 'Date'].freeze + def initialize( id:, name:, @@ -67,9 +69,9 @@ def show_movements(rows = 5) .map.with_index do |mov, index| [index + 1, mov.amount, mov.currency, mov.description, mov.locale_date] end - headers = ['#', 'Amount', 'Currency', 'Description', 'Date'] + puts - puts tabulate(headers, movements, indent: 4, style: 'fancy') + puts tabulate(HEADERS, movements, indent: 4, style: 'fancy') end def to_s From a42299b72db714803a6188f5fb836edfe358dcba Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:50:45 -0400 Subject: [PATCH 62/72] fix(v1/link): Fix call to delete --- lib/fintoc/v1/resources/link.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fintoc/v1/resources/link.rb b/lib/fintoc/v1/resources/link.rb index ec390ce..78f9966 100644 --- a/lib/fintoc/v1/resources/link.rb +++ b/lib/fintoc/v1/resources/link.rb @@ -74,7 +74,7 @@ def update_accounts end def delete - @client.delete_link(@id) + @client.links.delete(@id) end def to_s From b4d1349edd49f8ee3ab4f8a709c5e6f23958f1d0 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:51:21 -0400 Subject: [PATCH 63/72] refactor(fintoc/version): Move version spec to better folder --- spec/fintoc_spec.rb | 9 --------- spec/lib/fintoc/fintoc_spec.rb | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 spec/fintoc_spec.rb create mode 100644 spec/lib/fintoc/fintoc_spec.rb diff --git a/spec/fintoc_spec.rb b/spec/fintoc_spec.rb deleted file mode 100644 index 85e57ad..0000000 --- a/spec/fintoc_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -RSpec.describe Fintoc do - it 'has a version number' do - expect(Fintoc::VERSION).not_to be_nil - end - - # it "does something useful" do - # expect(false).to eq(true) - # end -end diff --git a/spec/lib/fintoc/fintoc_spec.rb b/spec/lib/fintoc/fintoc_spec.rb new file mode 100644 index 0000000..7e36d86 --- /dev/null +++ b/spec/lib/fintoc/fintoc_spec.rb @@ -0,0 +1,5 @@ +RSpec.describe Fintoc, :module do + it 'is a valid version number' do + expect(Fintoc::VERSION).to match(/\A\d+\.\d+\.\d+\z/) + end +end From 05957966d6be466ccff355fd50ced343cc0636c9 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:56:27 -0400 Subject: [PATCH 64/72] feature(spec): Increase spec coverage --- spec/lib/fintoc/client_spec.rb | 6 +++ spec/lib/fintoc/v1/account_spec.rb | 34 +++++++++++++++ spec/lib/fintoc/v1/balance_spec.rb | 23 ++++++++-- spec/lib/fintoc/v1/institution_spec.rb | 18 ++++++-- spec/lib/fintoc/v1/link_spec.rb | 47 +++++++++++++++++++-- spec/lib/fintoc/v1/movement_spec.rb | 6 +++ spec/lib/fintoc/v1/transfer_account_spec.rb | 18 +++++++- 7 files changed, 139 insertions(+), 13 deletions(-) diff --git a/spec/lib/fintoc/client_spec.rb b/spec/lib/fintoc/client_spec.rb index 39d7fb3..6111f2e 100644 --- a/spec/lib/fintoc/client_spec.rb +++ b/spec/lib/fintoc/client_spec.rb @@ -21,6 +21,12 @@ end end + describe '#to_s' do + it 'returns the client as a string' do + expect(client.to_s).to match(/Fintoc::Client\(v1: .*, v2: .*\)/) + end + end + describe 'client separation' do it 'allows direct access to movements client' do expect(client.v1.links) diff --git a/spec/lib/fintoc/v1/account_spec.rb b/spec/lib/fintoc/v1/account_spec.rb index 889a889..28d47d2 100644 --- a/spec/lib/fintoc/v1/account_spec.rb +++ b/spec/lib/fintoc/v1/account_spec.rb @@ -61,6 +61,40 @@ end end + describe '#show_movements' do + context 'when account has movements' do + let(:movement) do + Fintoc::V1::Movement.new( + id: '1', + amount: 1000, + currency: 'CLP', + description: 'Test movement', + post_date: '2023-01-01T10:00:00Z', + transaction_date: '2023-01-01T10:00:00Z', + type: 'normal_movement', + reference_id: 'ref123', + recipient_account: nil, + sender_account: nil, + comment: nil + ) + end + + let(:account) { described_class.new(**data, movements: [movement]) } + + it 'displays movements information with non-empty movements array' do + expect { account.show_movements }.to output(/This account has 1 movement/).to_stdout + end + end + + context 'when account has no movements' do + let(:account) { described_class.new(**data, movements: []) } + + it 'displays zero movements message' do + expect { account.show_movements }.to output(/This account has 0 movements/).to_stdout + end + end + end + describe '#update_balance' do it "update account's movements", :vcr do movements = linked_account.update_movements diff --git a/spec/lib/fintoc/v1/balance_spec.rb b/spec/lib/fintoc/v1/balance_spec.rb index 30675d7..069fecd 100644 --- a/spec/lib/fintoc/v1/balance_spec.rb +++ b/spec/lib/fintoc/v1/balance_spec.rb @@ -4,11 +4,26 @@ let(:data) { { available: 1000, current: 500, limit: 10 } } let(:balance) { described_class.new(**data) } - it 'create an instance of Balance' do - expect(balance).to be_an_instance_of(described_class) + describe '#new' do + it 'create an instance of Balance' do + expect(balance).to be_an_instance_of(described_class) + end + + it 'returns their object_id when id_ getter is called' do + expect(balance.id).to eq(balance.object_id) + end + end + + describe '#to_s' do + it 'returns the balance as a string' do + expect(balance.to_s).to eq('1000 (500)') + end end - it 'returns their object_id when id_ getter is called' do - expect(balance.id).to eq(balance.object_id) + describe '#inspect' do + it 'returns the balance as a string' do + expect(balance.inspect) + .to eq("") + end end end diff --git a/spec/lib/fintoc/v1/institution_spec.rb b/spec/lib/fintoc/v1/institution_spec.rb index d2aa9f2..d2be9d5 100644 --- a/spec/lib/fintoc/v1/institution_spec.rb +++ b/spec/lib/fintoc/v1/institution_spec.rb @@ -7,11 +7,21 @@ let(:institution) { described_class.new(**data) } - it 'create an instance of Institution' do - expect(institution).to be_an_instance_of(described_class) + describe '#new' do + it 'create an instance of Institution' do + expect(institution).to be_an_instance_of(described_class) + end end - it "print the institution's name when to_s is called" do - expect(institution.to_s).to eq("🏦 #{data[:name]}") + describe '#to_s' do + it "print the institution's name when to_s is called" do + expect(institution.to_s).to eq("🏦 #{data[:name]}") + end + end + + describe '#inspect' do + it 'returns the institution as a string' do + expect(institution.inspect).to eq("") + end end end diff --git a/spec/lib/fintoc/v1/link_spec.rb b/spec/lib/fintoc/v1/link_spec.rb index 3a04446..2698ae6 100644 --- a/spec/lib/fintoc/v1/link_spec.rb +++ b/spec/lib/fintoc/v1/link_spec.rb @@ -50,10 +50,20 @@ ] } end - let(:link) { described_class.new(**data) } + let(:link) { described_class.new(**data, client:) } + let(:client) { Fintoc::V1::Client.new(api_key) } + let(:api_key) { 'sk_test_SeCrEt_aPi_KeY' } - it 'create an instance of Link' do - expect(link).to be_an_instance_of(described_class) + describe '#new' do + it 'create an instance of Link' do + expect(link).to be_an_instance_of(described_class) + end + end + + describe '#to_s' do + it 'returns the link as a string' do + expect(link.to_s).to eq("<#{data[:username]}@#{data[:institution][:name]}> πŸ”— ") + end end describe '#find' do @@ -70,4 +80,35 @@ ) end end + + describe '#show_accounts' do + context 'when link has accounts' do + it 'displays accounts information' do + expect { link.show_accounts }.to output(/This links has 2 accounts/).to_stdout + end + end + + context 'when link has no accounts' do + it 'displays zero accounts message' do + empty_link = described_class.new(**data, accounts: []) + expect { empty_link.show_accounts }.to output(/This links has 0 accounts/).to_stdout + end + end + end + + describe '#delete' do + let(:delete_proc) { instance_double(Proc) } + + before do + allow(client).to receive(:delete).with(version: :v1).and_return(delete_proc) + allow(delete_proc) + .to receive(:call) + .with("links/#{link.id}") + .and_return(true) + end + + it 'deletes the link successfully' do + expect(link.delete).to be true + end + end end diff --git a/spec/lib/fintoc/v1/movement_spec.rb b/spec/lib/fintoc/v1/movement_spec.rb index 5b9872c..1339227 100644 --- a/spec/lib/fintoc/v1/movement_spec.rb +++ b/spec/lib/fintoc/v1/movement_spec.rb @@ -89,6 +89,12 @@ end end + describe '#to_s' do + it 'returns the movement as a string' do + expect(movement.to_s).to eq('59400 (Traspaso de:Fintoc SpA @ 04/17/20)') + end + end + it 'return uniq movements using the hash method implemented in Movement Class' do expect(dup_movements.uniq.length).to eq(1) end diff --git a/spec/lib/fintoc/v1/transfer_account_spec.rb b/spec/lib/fintoc/v1/transfer_account_spec.rb index 20ee680..86951da 100644 --- a/spec/lib/fintoc/v1/transfer_account_spec.rb +++ b/spec/lib/fintoc/v1/transfer_account_spec.rb @@ -11,7 +11,21 @@ end let(:transfer) { described_class.new(**data) } - it 'create an instance of TransferAccount' do - expect(transfer).to be_an_instance_of(described_class) + describe '#new' do + it 'create an instance of TransferAccount' do + expect(transfer).to be_an_instance_of(described_class) + end + end + + describe '#id' do + it 'returns the transfer account as a string' do + expect(transfer.id).to eq(transfer.object_id) + end + end + + describe '#to_s' do + it 'returns the transfer account as a string' do + expect(transfer.to_s).to eq(data[:holder_id].to_s) + end end end From 5fc4850841599f663855db96a219f464dc55ce91 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 16:58:06 -0400 Subject: [PATCH 65/72] fix(transfers-manager): Fix mock of transfers return in spec --- spec/lib/fintoc/v2/managers/transfers_manager_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb b/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb index 70fd199..ef1ebdf 100644 --- a/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb +++ b/spec/lib/fintoc/v2/managers/transfers_manager_spec.rb @@ -66,7 +66,7 @@ allow(post_proc) .to receive(:call) .with('transfers/return', transfer_id:) - .and_return(first_transfer_data) + .and_return(returned_transfer_data) allow(Fintoc::V2::Transfer).to receive(:new) end @@ -101,7 +101,7 @@ it 'calls build_transfer with the response' do manager.return('trf_123') expect(Fintoc::V2::Transfer) - .to have_received(:new).with(**first_transfer_data, client:) + .to have_received(:new).with(**returned_transfer_data, client:) end end end From 0779de4f7f7b908d9581dcb383c767fbb0841f4d Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 18:03:08 -0400 Subject: [PATCH 66/72] fix(base-client): Invert visible part and hidden part of key This also adds a spec for BaseClient --- lib/fintoc/base_client.rb | 6 +- spec/lib/fintoc/base_client_spec.rb | 123 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 spec/lib/fintoc/base_client_spec.rb diff --git a/lib/fintoc/base_client.rb b/lib/fintoc/base_client.rb index 2bf40d9..1e9640c 100644 --- a/lib/fintoc/base_client.rb +++ b/lib/fintoc/base_client.rb @@ -64,10 +64,10 @@ def fetch_next end def to_s - visible_chars = 4 - hidden_part = '*' * (@api_key.size - visible_chars) + visible_chars = 8 visible_key = @api_key.slice(0, visible_chars) - "#{self.class.name}(πŸ”‘=#{hidden_part + visible_key})" + hidden_part = '*' * (@api_key.size - visible_chars) + "#{self.class.name}(πŸ”‘=#{visible_key + hidden_part})" end private diff --git a/spec/lib/fintoc/base_client_spec.rb b/spec/lib/fintoc/base_client_spec.rb new file mode 100644 index 0000000..7735057 --- /dev/null +++ b/spec/lib/fintoc/base_client_spec.rb @@ -0,0 +1,123 @@ +require 'fintoc/base_client' + +RSpec.describe Fintoc::BaseClient do + let(:api_key) { 'sk_test_SeCreT-aPi_KeY' } + let(:client) { described_class.new(api_key) } + + describe '#initialize' do + it 'creates an instance with api_key' do + expect(client).to be_an_instance_of(described_class) + expect(client.instance_variable_get(:@api_key)).to eq(api_key) + end + + context 'with jws_private_key' do + let(:jws_private_key) { OpenSSL::PKey::RSA.new(2048) } + let(:client_with_jws) { described_class.new(api_key, jws_private_key: jws_private_key) } + + it 'creates an instance with jws support' do + expect(client_with_jws.instance_variable_get(:@jws)).not_to be_nil + end + end + end + + describe '#get' do + it 'returns a proc for GET requests' do + get_request = client.get(version: :v1) + expect(get_request).to be_a(Proc) + end + end + + describe '#delete' do + it 'returns a proc for DELETE requests' do + delete_request = client.delete(version: :v1) + expect(delete_request).to be_a(Proc) + end + end + + describe '#post' do + it 'returns a proc for POST requests' do + post_request = client.post(version: :v1) + expect(post_request).to be_a(Proc) + end + end + + describe '#patch' do + it 'returns a proc for PATCH requests' do + patch_request = client.patch(version: :v1) + expect(patch_request).to be_a(Proc) + end + end + + describe '#request' do + let(:mock_response) { instance_double(HTTP::Response) } + let(:mock_status) { instance_double(HTTP::Response::Status) } + let(:mock_headers) { instance_double(HTTP::Headers) } + + context 'when HTTP request is successful' do + let(:success_response_body) { { data: 'test_data' }.to_json } + + before do + allow(mock_response).to receive_messages( + body: success_response_body, + status: mock_status, + headers: mock_headers + ) + allow(mock_status).to receive_messages( + client_error?: false, + server_error?: false + ) + allow(mock_headers).to receive(:get).with('link').and_return(nil) + allow(client).to receive(:make_request).and_return(mock_response) + end + + it 'returns parsed JSON content' do + request_proc = client.request('get') + result = request_proc.call('/test/resource') + + expect(result).to eq({ data: 'test_data' }) + end + end + + context 'when HTTP request returns an error' do + let(:error_response_body) do + { + error: { + code: 'authentication_error', + message: 'Invalid API key', + doc_url: 'https://docs.fintoc.com/errors' + } + }.to_json + end + + before do + allow(mock_response).to receive_messages( + body: error_response_body, + status: mock_status, + headers: mock_headers + ) + allow(mock_status).to receive_messages( + client_error?: true, + server_error?: false + ) + allow(mock_headers).to receive(:get).with('link').and_return(nil) + allow(client).to receive(:make_request).and_return(mock_response) + end + + it 'raises custom error from response' do + request_proc = client.request('get') + expect { request_proc.call('/test/resource') } + .to raise_error(Fintoc::Errors::AuthenticationError, /Invalid API key/) + end + end + end + + describe '#to_s' do + it 'returns masked API key representation' do + result = client.to_s + expect(result).to include('sk_test_') + expect(result).to include('****') + expect(result).not_to include(api_key) + expect(result).not_to include('SeCreT-aPi_KeY') + end + end +end From d5f0809f189dd39680d9ea4719ca4d45e76d60f5 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 18:52:46 -0400 Subject: [PATCH 67/72] feature(account/spec): Add missing spec for update_balance method of V1::Account --- spec/lib/fintoc/v1/account_spec.rb | 11 +++ .../updates_the_account_s_balance.yml | 77 +++++++++++++++++++ .../update_account_s_movements.yml | 0 3 files changed, 88 insertions(+) create mode 100644 spec/vcr/Fintoc_V1_Account/_update_balance/updates_the_account_s_balance.yml rename spec/vcr/Fintoc_V1_Account/{_update_balance => _update_movements}/update_account_s_movements.yml (100%) diff --git a/spec/lib/fintoc/v1/account_spec.rb b/spec/lib/fintoc/v1/account_spec.rb index 28d47d2..b9eb14c 100644 --- a/spec/lib/fintoc/v1/account_spec.rb +++ b/spec/lib/fintoc/v1/account_spec.rb @@ -96,6 +96,17 @@ end describe '#update_balance' do + let(:client_for_update_balance) { Fintoc::V1::Client.new(api_key) } + + it "updates the account's balance", :vcr do + initial_balance = account.balance + account.update_balance + expect(account.balance).to be_a(Fintoc::V1::Balance) + expect(account.balance).not_to equal(initial_balance) + end + end + + describe '#update_movements' do it "update account's movements", :vcr do movements = linked_account.update_movements expect(movements).to all(be_a(Fintoc::V1::Movement)) diff --git a/spec/vcr/Fintoc_V1_Account/_update_balance/updates_the_account_s_balance.yml b/spec/vcr/Fintoc_V1_Account/_update_balance/updates_the_account_s_balance.yml new file mode 100644 index 0000000..da40abd --- /dev/null +++ b/spec/vcr/Fintoc_V1_Account/_update_balance/updates_the_account_s_balance.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v1/accounts/Z6AwnGn4idL7DPj4 + body: + encoding: UTF-8 + string: '' + headers: + Authorization: + - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx + User-Agent: + - fintoc-ruby/0.1.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 18 Sep 2020 19:32:33 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - close + Set-Cookie: + - __cfduid=dbfae5f92e0ae1b5fe843ee4eba3eb1ce1600457552; expires=Sun, 18-Oct-20 + 19:32:32 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"897ae694e5399ed740681eeb2602478d" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 5155665b-825d-4a62-84a4-910f4750e187 + X-Runtime: + - '0.336331' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + Vary: + - Origin + Via: + - 1.1 vegur + Cf-Cache-Status: + - DYNAMIC + Cf-Request-Id: + - 05444d4bea0000e51adc97e200000001 + Expect-Ct: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Cf-Ray: + - 5d4d7e597cb8e51a-ARI + body: + encoding: UTF-8 + string: '{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta + Corriente","official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. + Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}' + recorded_at: Fri, 18 Sep 2020 19:32:33 GMT + +recorded_with: VCR 6.0.0 diff --git a/spec/vcr/Fintoc_V1_Account/_update_balance/update_account_s_movements.yml b/spec/vcr/Fintoc_V1_Account/_update_movements/update_account_s_movements.yml similarity index 100% rename from spec/vcr/Fintoc_V1_Account/_update_balance/update_account_s_movements.yml rename to spec/vcr/Fintoc_V1_Account/_update_movements/update_account_s_movements.yml From 212d42a2a722a341fdef4b49f9d65edd1540af82 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 19:08:36 -0400 Subject: [PATCH 68/72] feature(spec/link): Add missing spec for update_accounts of V1::Link --- spec/lib/fintoc/v1/link_spec.rb | 52 ++++++ ...balance_and_movements_for_all_accounts.yml | 148 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 spec/vcr/Fintoc_V1_Link/_update_accounts/updates_balance_and_movements_for_all_accounts.yml diff --git a/spec/lib/fintoc/v1/link_spec.rb b/spec/lib/fintoc/v1/link_spec.rb index 2698ae6..d5e2461 100644 --- a/spec/lib/fintoc/v1/link_spec.rb +++ b/spec/lib/fintoc/v1/link_spec.rb @@ -96,6 +96,58 @@ end end + describe '#update_accounts' do + let(:api_key) { 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx' } + let(:client) { Fintoc::V1::Client.new(api_key) } + let(:link_token) { '6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W' } + let(:linked_link) { client.links.get(link_token) } + let(:account_original_balance) do + { + available: 7_010_510, + current: 7_010_510, + limit: 7_510_510 + } + end + let(:account_data_before_update) do + { + id: 'Z6AwnGn4idL7DPj4', + name: 'Cuenta Corriente', + official_name: 'Cuenta Corriente Moneda Local', + number: '9530516286', + holder_id: '134910798', + holder_name: 'Jon Snow', + type: 'checking_account', + currency: 'CLP', + refreshed_at: nil, + balance: account_original_balance + } + end + + let(:data) do + { + id: 'nMNejK7BT8oGbvO4', + username: '183917137', + link_token: 'nMNejK7BT8oGbvO4_token_GLtktZX5SKphRtJFe_yJTDWT', + holder_type: 'individual', + created_at: '2020-04-22T21:10:19.254Z', + institution: { + country: 'cl', + id: 'cl_banco_de_chile', + name: 'Banco de Chile' + }, + mode: 'test', + accounts: [account_data_before_update] + } + end + + it 'updates balance and movements for all accounts', :vcr do + expect(link.accounts[0].balance.available).to eq(account_original_balance[:available]) + link.update_accounts + expect(link.accounts[0].balance).to be_a(Fintoc::V1::Balance) + expect(link.accounts[0].balance.available).not_to eq(account_original_balance[:available]) + end + end + describe '#delete' do let(:delete_proc) { instance_double(Proc) } diff --git a/spec/vcr/Fintoc_V1_Link/_update_accounts/updates_balance_and_movements_for_all_accounts.yml b/spec/vcr/Fintoc_V1_Link/_update_accounts/updates_balance_and_movements_for_all_accounts.yml new file mode 100644 index 0000000..f05e3e0 --- /dev/null +++ b/spec/vcr/Fintoc_V1_Link/_update_accounts/updates_balance_and_movements_for_all_accounts.yml @@ -0,0 +1,148 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.fintoc.com/v1/accounts/Z6AwnGn4idL7DPj4 + body: + encoding: UTF-8 + string: '' + headers: + Authorization: + - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx + User-Agent: + - fintoc-ruby/0.1.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 18 Sep 2020 19:32:33 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - close + Set-Cookie: + - __cfduid=dbfae5f92e0ae1b5fe843ee4eba3eb1ce1600457552; expires=Sun, 18-Oct-20 + 19:32:32 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"897ae694e5399ed740681eeb2602478d" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 5155665b-825d-4a62-84a4-910f4750e187 + X-Runtime: + - '0.336331' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + Vary: + - Origin + Via: + - 1.1 vegur + Cf-Cache-Status: + - DYNAMIC + Cf-Request-Id: + - 05444d4bea0000e51adc97e200000001 + Expect-Ct: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Cf-Ray: + - 5d4d7e597cb8e51a-ARI + body: + encoding: UTF-8 + string: '{"id":"JjEQx2rPTGGKbrP5","type":"checking_account","number":"813990168","name":"Cuenta + Corriente","official_name":"Cuenta Corriente","balance":{"available":23457460,"current":23457460,"limit":23457460},"holder_id":"404276727","holder_name":"Sta. + Francisco OrdΓ³Γ±ez Esquivel","currency":"CLP"}' + recorded_at: Fri, 18 Sep 2020 19:32:33 GMT +- request: + method: get + uri: https://api.fintoc.com/v1/accounts/Z6AwnGn4idL7DPj4/movements + body: + encoding: UTF-8 + string: '' + headers: + Authorization: + - sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx + User-Agent: + - fintoc-ruby/0.1.0 + Connection: + - close + Host: + - api.fintoc.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 18 Sep 2020 19:32:33 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - close + Set-Cookie: + - __cfduid=dbfae5f92e0ae1b5fe843ee4eba3eb1ce1600457552; expires=Sun, 18-Oct-20 + 19:32:32 GMT; path=/; domain=.fintoc.com; HttpOnly; SameSite=Lax; Secure + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Etag: + - W/"897ae694e5399ed740681eeb2602478d" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 5155665b-825d-4a62-84a4-910f4750e187 + X-Runtime: + - '0.336331' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + Vary: + - Origin + Via: + - 1.1 vegur + Cf-Cache-Status: + - DYNAMIC + Cf-Request-Id: + - 05444d4bea0000e51adc97e200000001 + Expect-Ct: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Cf-Ray: + - 5d4d7e597cb8e51a-ARI + body: + encoding: UTF-8 + string: '[]' + recorded_at: Fri, 18 Sep 2020 19:32:33 GMT + +recorded_with: VCR 6.0.0 From c4838d80fc7f2e0e2957cf2fb45ef83c3ff579bc Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 19:08:53 -0400 Subject: [PATCH 69/72] feature(simplecov): Add simplecov gem --- Gemfile | 26 +++++++++++++++++--------- Gemfile.lock | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 1badaa3..3831ec5 100644 --- a/Gemfile +++ b/Gemfile @@ -4,12 +4,20 @@ source 'https://rubygems.org' gemspec # Development dependencies -gem 'rexml' # Required for webmock in Ruby 3.0+ -gem 'rspec', '~> 3.0' -gem 'rubocop', '~> 1.80' -gem 'rubocop-capybara', '~> 2.22', '>= 2.22.1' -gem 'rubocop-performance', '~> 1.25' -gem 'rubocop-rails', '~> 2.33', '>= 2.33.3' -gem 'rubocop-rspec', '~> 3.6' -gem 'vcr', '~> 6.3' -gem 'webmock' +group :development do + gem 'rubocop', '~> 1.80' + gem 'rubocop-capybara', '~> 2.22', '>= 2.22.1' + gem 'rubocop-performance', '~> 1.25' + gem 'rubocop-rails', '~> 2.33', '>= 2.33.3' + gem 'rubocop-rspec', '~> 3.6' +end + +group :test do + gem 'rexml' # Required for webmock in Ruby 3.0+ + gem 'rspec', '~> 3.0' + gem 'simplecov', '~> 0.21' + gem 'simplecov_linter_formatter' + gem 'simplecov_text_formatter' + gem 'vcr', '~> 6.3' + gem 'webmock' +end diff --git a/Gemfile.lock b/Gemfile.lock index a946bcc..e0d60c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - fintoc (0.2.0) + fintoc (1.0.0) http money-rails tabulate @@ -53,6 +53,7 @@ GEM crass (1.0.6) date (3.4.1) diff-lcs (1.6.2) + docile (1.4.1) domain_name (0.6.20240107) drb (2.2.3) erb (5.0.2) @@ -211,6 +212,15 @@ GEM rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) securerandom (0.4.1) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.2) + simplecov_json_formatter (0.1.4) + simplecov_linter_formatter (0.2.0) + rainbow + simplecov_text_formatter (0.1.0) stringio (3.1.7) tabulate (0.1.2) thor (1.4.0) @@ -249,6 +259,9 @@ DEPENDENCIES rubocop-performance (~> 1.25) rubocop-rails (~> 2.33, >= 2.33.3) rubocop-rspec (~> 3.6) + simplecov (~> 0.21) + simplecov_linter_formatter + simplecov_text_formatter vcr (~> 6.3) webmock From 77cae8d80bb69a095e2b54dff2d36f154b8d2edb Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 19:09:17 -0400 Subject: [PATCH 70/72] feature(simplecov): Add simplecov config --- lib/tasks/simplecov_config.rb | 19 +++++++++++++++++++ spec/spec_helper.rb | 1 + 2 files changed, 20 insertions(+) create mode 100644 lib/tasks/simplecov_config.rb diff --git a/lib/tasks/simplecov_config.rb b/lib/tasks/simplecov_config.rb new file mode 100644 index 0000000..0773caf --- /dev/null +++ b/lib/tasks/simplecov_config.rb @@ -0,0 +1,19 @@ +if ENV['COVERAGE'] == 'true' + require 'simplecov' + require 'simplecov_text_formatter' + require 'simplecov_linter_formatter' + + SimpleCov.start do + formatter SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::HTMLFormatter]) + + add_filter '/vendor/' + add_filter '/lib/fintoc.rb' + add_filter '/lib/fintoc/version.rb' + add_filter '/lib/tasks/simplecov_config.rb' + + track_files 'lib/**/*.rb' + + minimum_coverage 100 + minimum_coverage_by_file 100 + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2e9f9d7..1ff67c5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'bundler/setup' +require 'tasks/simplecov_config' require 'fintoc' require 'vcr' From 4227d6a500d900b7fb0c4d929ef348f0386c6783 Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 19:09:33 -0400 Subject: [PATCH 71/72] feature(ci/simplecov): Add coverage requirement to CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0046ce0..b0c6692 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,4 +43,4 @@ jobs: bundler-cache: true - name: Run tests - run: bundle exec rspec + run: COVERAGE=true bundle exec rspec From 020960e470be718955edf567eb0cf7faadca4b1a Mon Sep 17 00:00:00 2001 From: Pedro Bahamondes Date: Fri, 5 Sep 2025 19:33:24 -0400 Subject: [PATCH 72/72] feature(version): Bump version to 1.0.0 and update changelog --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++++++----- lib/fintoc/version.rb | 2 +- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 987d892..40f7f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,45 @@ # Changelog -## 0.2.0 - 2025-08-26 +## 1.0.0 - 2025-09-05 -* Update linting rules and fix linting issues -* Update CI flow -* Update gems and support only Ruby versions that haven't reached EOL -* Update README +### πŸš€ New Features + +- **New Client Architecture**: Restructured client to distinguish between API versions + - Movements API now accessible via `Fintoc::V1::Client` + - Transfers API accessible via `Fintoc::V2::Client` + - Unified client interface with `Fintoc::Client` providing access to both versions + - Backward compatibility maintained for existing method signatures + +- **V2 Client - Transfers API Implementation**: Partial implementation of Transfers API endpoints in `Fintoc::V2::Client` + - **Entities**: List and retrieve business entities + - **Transfer Accounts**: Create, read, update, and list transfer accounts + - **Account Numbers**: Manage account numbers/CLABEs + - **Transfers**: Create, retrieve, list, and return transfers + - **Simulation**: Simulate receiving transfers for testing + - **Account Verifications**: Verify account numbers + - **Movements**: TODO! Not yet implemented + +### πŸ§ͺ Testing & Quality + +- **100% Line Coverage**: Achieved full line coverage using SimpleCov gem, increasing the spec coverage and testing all new code. + - Configured with `minimum_coverage 100` and `minimum_coverage_by_file 100` + - Uses `simplecov_text_formatter` and `simplecov_linter_formatter` for reporting + +- **Robust CI Pipeline**: Enhanced GitHub Actions workflow for comprehensive testing + - **Linting**: Dedicated RuboCop job for code quality enforcement + - **Multi-version Testing**: Tests against all currently supported Ruby versions (3.2, 3.3, and 3.4) for version compatibility + - **Coverage Integration**: Automated coverage reporting in CI pipeline + +### Others + +- **Money-Rails Integration**: Added `money-rails` gem for proper currency handling +- **Comprehensive README Update**: Extensively updated documentation with usage examples and development instructions +- **Improved Error Handling**: Better error management across API versions +- **JWS Support**: JSON Web Signature support for secure V2 API operations +- **HMac Signature verification**: Added the `Fintoc::WebhookSignature` class for easing webhook signature verification ## 0.1.0 - 2021-01-18 +Initial version + * Up to date with the [2020-11-17](https://docs.fintoc.com/docs/api-changelog#2020-11-17) API version diff --git a/lib/fintoc/version.rb b/lib/fintoc/version.rb index 160de32..36e3c44 100644 --- a/lib/fintoc/version.rb +++ b/lib/fintoc/version.rb @@ -1,3 +1,3 @@ module Fintoc - VERSION = '0.2.0' + VERSION = '1.0.0' end