fintoc 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +47 -0
  3. data/.github/workflows/ci.yml +46 -0
  4. data/.rubocop.yml +14 -7
  5. data/CHANGELOG.md +40 -0
  6. data/Gemfile +20 -1
  7. data/Gemfile.lock +237 -61
  8. data/README.md +286 -42
  9. data/Rakefile +3 -3
  10. data/fintoc.gemspec +3 -7
  11. data/lib/config/initializers/money.rb +5 -0
  12. data/lib/fintoc/base_client.rb +150 -0
  13. data/lib/fintoc/client.rb +14 -123
  14. data/lib/fintoc/constants.rb +4 -3
  15. data/lib/fintoc/errors.rb +139 -15
  16. data/lib/fintoc/jws.rb +83 -0
  17. data/lib/fintoc/utils.rb +2 -2
  18. data/lib/fintoc/v1/client/client.rb +12 -0
  19. data/lib/fintoc/v1/managers/links_manager.rb +46 -0
  20. data/lib/fintoc/v1/resources/account.rb +95 -0
  21. data/lib/fintoc/v1/resources/balance.rb +27 -0
  22. data/lib/fintoc/v1/resources/institution.rb +21 -0
  23. data/lib/fintoc/v1/resources/link.rb +85 -0
  24. data/lib/fintoc/v1/resources/movement.rb +62 -0
  25. data/lib/fintoc/v1/resources/transfer_account.rb +24 -0
  26. data/lib/fintoc/v2/client/client.rb +37 -0
  27. data/lib/fintoc/v2/managers/account_numbers_manager.rb +59 -0
  28. data/lib/fintoc/v2/managers/account_verifications_manager.rb +45 -0
  29. data/lib/fintoc/v2/managers/accounts_manager.rb +54 -0
  30. data/lib/fintoc/v2/managers/entities_manager.rb +36 -0
  31. data/lib/fintoc/v2/managers/simulate_manager.rb +30 -0
  32. data/lib/fintoc/v2/managers/transfers_manager.rb +56 -0
  33. data/lib/fintoc/v2/resources/account.rb +105 -0
  34. data/lib/fintoc/v2/resources/account_number.rb +105 -0
  35. data/lib/fintoc/v2/resources/account_verification.rb +73 -0
  36. data/lib/fintoc/v2/resources/entity.rb +51 -0
  37. data/lib/fintoc/v2/resources/transfer.rb +131 -0
  38. data/lib/fintoc/version.rb +1 -1
  39. data/lib/fintoc/webhook_signature.rb +73 -0
  40. data/lib/fintoc.rb +3 -0
  41. data/lib/tasks/simplecov_config.rb +19 -0
  42. metadata +35 -83
  43. data/lib/fintoc/resources/account.rb +0 -84
  44. data/lib/fintoc/resources/balance.rb +0 -24
  45. data/lib/fintoc/resources/institution.rb +0 -18
  46. data/lib/fintoc/resources/link.rb +0 -83
  47. data/lib/fintoc/resources/movement.rb +0 -55
  48. data/lib/fintoc/resources/transfer_account.rb +0 -22
@@ -0,0 +1,85 @@
1
+ require 'date'
2
+ require 'tabulate'
3
+ require 'fintoc/utils'
4
+ require 'fintoc/v1/resources/account'
5
+ require 'fintoc/v1/resources/institution'
6
+
7
+ module Fintoc
8
+ module V1
9
+ class Link
10
+ attr_reader :id, :username, :holder_type, :institution, :created_at, :mode,
11
+ :accounts, :link_token
12
+
13
+ include Utils
14
+
15
+ def initialize(
16
+ id:,
17
+ username:,
18
+ holder_type:,
19
+ institution:,
20
+ created_at:,
21
+ mode:,
22
+ accounts: nil,
23
+ link_token: nil,
24
+ client: nil,
25
+ **
26
+ )
27
+ @id = id
28
+ @username = username
29
+ @holder_type = holder_type
30
+ @institution = Fintoc::V1::Institution.new(**institution)
31
+ @created_at = Date.iso8601(created_at)
32
+ @mode = mode
33
+ @accounts = if accounts.nil?
34
+ []
35
+ else
36
+ accounts.map { |data| Fintoc::V1::Account.new(**data, client:) }
37
+ end
38
+ @token = link_token
39
+ @client = client
40
+ end
41
+
42
+ def find_all(**kwargs)
43
+ raise 'You must provide *exactly one* account field.' if kwargs.size != 1
44
+
45
+ field, value = kwargs.to_a.first
46
+ @accounts.select do |account|
47
+ account.send(field.to_sym) == value
48
+ end
49
+ end
50
+
51
+ def find(**)
52
+ results = find_all(**)
53
+ results.any? ? results.first : nil
54
+ end
55
+
56
+ def show_accounts(rows = 5)
57
+ puts "This links has #{Utils.pluralize(@accounts.size, 'account')}"
58
+ return unless @accounts.any?
59
+
60
+ accounts = @accounts.to_a.slice(0, rows)
61
+ .map.with_index do |acc, index|
62
+ [index + 1, acc.name, acc.holder_name, acc.currency]
63
+ end
64
+ headers = ['#', 'Name', 'Holder', 'Currency']
65
+ puts
66
+ puts tabulate(headers, accounts, indent: 4, style: 'fancy')
67
+ end
68
+
69
+ def update_accounts
70
+ @accounts.each do |account|
71
+ account.update_balance
72
+ account.update_movements
73
+ end
74
+ end
75
+
76
+ def delete
77
+ @client.links.delete(@id)
78
+ end
79
+
80
+ def to_s
81
+ "<#{@username}@#{@institution.name}> 🔗 <Fintoc>"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,62 @@
1
+ require 'date'
2
+ require 'fintoc/v1/resources/transfer_account'
3
+
4
+ module Fintoc
5
+ module V1
6
+ class Movement
7
+ attr_reader :id, :amount, :currency, :description, :reference_id,
8
+ :post_date, :transaction_date, :type, :recipient_account,
9
+ :sender_account, :account, :comment
10
+
11
+ def initialize(
12
+ id:,
13
+ amount:,
14
+ currency:,
15
+ description:,
16
+ post_date:,
17
+ transaction_date:,
18
+ type:,
19
+ reference_id:,
20
+ recipient_account:,
21
+ sender_account:,
22
+ comment:,
23
+ client: nil,
24
+ **
25
+ )
26
+ @id = id
27
+ @amount = amount
28
+ @currency = currency
29
+ @description = description
30
+ @post_date = DateTime.iso8601(post_date)
31
+ @transaction_date = DateTime.iso8601(transaction_date) if transaction_date
32
+ @type = type
33
+ @reference_id = reference_id
34
+ @recipient_account =
35
+ if recipient_account
36
+ Fintoc::V1::TransferAccount.new(**recipient_account)
37
+ end
38
+ @sender_account = Fintoc::V1::TransferAccount.new(**sender_account) if sender_account
39
+ @comment = comment
40
+ @client = client
41
+ end
42
+
43
+ def ==(other)
44
+ @id == other.id
45
+ end
46
+
47
+ alias eql? ==
48
+
49
+ def hash
50
+ @id.hash
51
+ end
52
+
53
+ def locale_date
54
+ @post_date.strftime('%x')
55
+ end
56
+
57
+ def to_s
58
+ "#{@amount} (#{@description} @ #{locale_date})"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,24 @@
1
+ require 'fintoc/v1/resources/institution'
2
+
3
+ module Fintoc
4
+ module V1
5
+ class TransferAccount
6
+ attr_reader :holder_id, :holder_name, :number, :institution
7
+
8
+ def initialize(holder_id:, holder_name:, number:, institution:, **)
9
+ @holder_id = holder_id
10
+ @holder_name = holder_name
11
+ @number = number
12
+ @institution = institution && Fintoc::V1::Institution.new(**institution)
13
+ end
14
+
15
+ def id
16
+ object_id
17
+ end
18
+
19
+ def to_s
20
+ @holder_id.to_s
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'fintoc/base_client'
2
+ require 'fintoc/v2/managers/entities_manager'
3
+ require 'fintoc/v2/managers/accounts_manager'
4
+ require 'fintoc/v2/managers/account_numbers_manager'
5
+ require 'fintoc/v2/managers/transfers_manager'
6
+ require 'fintoc/v2/managers/simulate_manager'
7
+ require 'fintoc/v2/managers/account_verifications_manager'
8
+
9
+ module Fintoc
10
+ module V2
11
+ class Client < BaseClient
12
+ def entities
13
+ @entities ||= Managers::EntitiesManager.new(self)
14
+ end
15
+
16
+ def accounts
17
+ @accounts ||= Managers::AccountsManager.new(self)
18
+ end
19
+
20
+ def account_numbers
21
+ @account_numbers ||= Managers::AccountNumbersManager.new(self)
22
+ end
23
+
24
+ def transfers
25
+ @transfers ||= Managers::TransfersManager.new(self)
26
+ end
27
+
28
+ def simulate
29
+ @simulate ||= Managers::SimulateManager.new(self)
30
+ end
31
+
32
+ def account_verifications
33
+ @account_verifications ||= Managers::AccountVerificationsManager.new(self)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,59 @@
1
+ require 'fintoc/v2/resources/account_number'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class AccountNumbersManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(account_id:, description: nil, metadata: nil, **params)
12
+ data = _create_account_number(account_id:, description:, metadata:, **params)
13
+ build_account_number(data)
14
+ end
15
+
16
+ def get(account_number_id)
17
+ data = _get_account_number(account_number_id)
18
+ build_account_number(data)
19
+ end
20
+
21
+ def list(**params)
22
+ _list_account_numbers(**params).map { |data| build_account_number(data) }
23
+ end
24
+
25
+ def update(account_number_id, **params)
26
+ data = _update_account_number(account_number_id, **params)
27
+ build_account_number(data)
28
+ end
29
+
30
+ private
31
+
32
+ def _create_account_number(account_id:, description: nil, metadata: nil, **params)
33
+ request_params = { account_id: }
34
+ request_params[:description] = description if description
35
+ request_params[:metadata] = metadata if metadata
36
+ request_params.merge!(params)
37
+
38
+ @client.post(version: :v2).call('account_numbers', **request_params)
39
+ end
40
+
41
+ def _get_account_number(account_number_id)
42
+ @client.get(version: :v2).call("account_numbers/#{account_number_id}")
43
+ end
44
+
45
+ def _list_account_numbers(**params)
46
+ @client.get(version: :v2).call('account_numbers', **params)
47
+ end
48
+
49
+ def _update_account_number(account_number_id, **params)
50
+ @client.patch(version: :v2).call("account_numbers/#{account_number_id}", **params)
51
+ end
52
+
53
+ def build_account_number(data)
54
+ Fintoc::V2::AccountNumber.new(**data, client: @client)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ require 'fintoc/v2/resources/account_verification'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class AccountVerificationsManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(account_number:)
12
+ data = _create_account_verification(account_number:)
13
+ build_account_verification(data)
14
+ end
15
+
16
+ def get(account_verification_id)
17
+ data = _get_account_verification(account_verification_id)
18
+ build_account_verification(data)
19
+ end
20
+
21
+ def list(**params)
22
+ _list_account_verifications(**params).map { |data| build_account_verification(data) }
23
+ end
24
+
25
+ private
26
+
27
+ def _create_account_verification(account_number:)
28
+ @client.post(version: :v2, use_jws: true).call('account_verifications', account_number:)
29
+ end
30
+
31
+ def _get_account_verification(account_verification_id)
32
+ @client.get(version: :v2).call("account_verifications/#{account_verification_id}")
33
+ end
34
+
35
+ def _list_account_verifications(**params)
36
+ @client.get(version: :v2).call('account_verifications', **params)
37
+ end
38
+
39
+ def build_account_verification(data)
40
+ Fintoc::V2::AccountVerification.new(**data, client: @client)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,54 @@
1
+ require 'fintoc/v2/resources/account'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class AccountsManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(entity_id:, description:, **params)
12
+ data = _create_account(entity_id:, description:, **params)
13
+ build_account(data)
14
+ end
15
+
16
+ def get(account_id)
17
+ data = _get_account(account_id)
18
+ build_account(data)
19
+ end
20
+
21
+ def list(**params)
22
+ _list_accounts(**params).map { |data| build_account(data) }
23
+ end
24
+
25
+ def update(account_id, **params)
26
+ data = _update_account(account_id, **params)
27
+ build_account(data)
28
+ end
29
+
30
+ private
31
+
32
+ def _create_account(entity_id:, description:, **params)
33
+ @client.post(version: :v2).call('accounts', entity_id:, description:, **params)
34
+ end
35
+
36
+ def _get_account(account_id)
37
+ @client.get(version: :v2).call("accounts/#{account_id}")
38
+ end
39
+
40
+ def _list_accounts(**params)
41
+ @client.get(version: :v2).call('accounts', **params)
42
+ end
43
+
44
+ def _update_account(account_id, **params)
45
+ @client.patch(version: :v2).call("accounts/#{account_id}", **params)
46
+ end
47
+
48
+ def build_account(data)
49
+ Fintoc::V2::Account.new(**data, client: @client)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ require 'fintoc/v2/resources/entity'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class EntitiesManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get(entity_id)
12
+ data = _get_entity(entity_id)
13
+ build_entity(data)
14
+ end
15
+
16
+ def list(**params)
17
+ _list_entities(**params).map { |data| build_entity(data) }
18
+ end
19
+
20
+ private
21
+
22
+ def _get_entity(entity_id)
23
+ @client.get(version: :v2).call("entities/#{entity_id}")
24
+ end
25
+
26
+ def _list_entities(**params)
27
+ @client.get(version: :v2).call('entities', **params)
28
+ end
29
+
30
+ def build_entity(data)
31
+ Fintoc::V2::Entity.new(**data, client: @client)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'fintoc/v2/resources/transfer'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class SimulateManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def receive_transfer(account_number_id:, amount:, currency:)
12
+ data = _simulate_receive_transfer(account_number_id:, amount:, currency:)
13
+ build_transfer(data)
14
+ end
15
+
16
+ private
17
+
18
+ def _simulate_receive_transfer(account_number_id:, amount:, currency:)
19
+ @client
20
+ .post(version: :v2)
21
+ .call('simulate/receive_transfer', account_number_id:, amount:, currency:)
22
+ end
23
+
24
+ def build_transfer(data)
25
+ Fintoc::V2::Transfer.new(**data, client: @client)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ require 'fintoc/v2/resources/transfer'
2
+
3
+ module Fintoc
4
+ module V2
5
+ module Managers
6
+ class TransfersManager
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(amount:, currency:, account_id:, counterparty:, **params)
12
+ data = _create_transfer(amount:, currency:, account_id:, counterparty:, **params)
13
+ build_transfer(data)
14
+ end
15
+
16
+ def get(transfer_id)
17
+ data = _get_transfer(transfer_id)
18
+ build_transfer(data)
19
+ end
20
+
21
+ def list(**params)
22
+ _list_transfers(**params).map { |data| build_transfer(data) }
23
+ end
24
+
25
+ def return(transfer_id)
26
+ data = _return_transfer(transfer_id)
27
+ build_transfer(data)
28
+ end
29
+
30
+ private
31
+
32
+ def _create_transfer(amount:, currency:, account_id:, counterparty:, **params)
33
+ @client
34
+ .post(version: :v2, use_jws: true)
35
+ .call('transfers', amount:, currency:, account_id:, counterparty:, **params)
36
+ end
37
+
38
+ def _get_transfer(transfer_id)
39
+ @client.get(version: :v2).call("transfers/#{transfer_id}")
40
+ end
41
+
42
+ def _list_transfers(**params)
43
+ @client.get(version: :v2).call('transfers', **params)
44
+ end
45
+
46
+ def _return_transfer(transfer_id)
47
+ @client.post(version: :v2, use_jws: true).call('transfers/return', transfer_id:)
48
+ end
49
+
50
+ def build_transfer(data)
51
+ Fintoc::V2::Transfer.new(**data, client: @client)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,105 @@
1
+ require 'money'
2
+
3
+ module Fintoc
4
+ module V2
5
+ class Account
6
+ attr_reader :id, :object, :mode, :description, :available_balance, :currency,
7
+ :is_root, :root_account_number_id, :root_account_number, :status, :entity
8
+
9
+ def initialize(
10
+ id:,
11
+ object:,
12
+ mode:,
13
+ description:,
14
+ available_balance:,
15
+ currency:,
16
+ is_root:,
17
+ root_account_number_id:,
18
+ root_account_number:,
19
+ status:,
20
+ entity:,
21
+ client: nil,
22
+ **
23
+ )
24
+ @id = id
25
+ @object = object
26
+ @mode = mode
27
+ @description = description
28
+ @available_balance = available_balance
29
+ @currency = currency
30
+ @is_root = is_root
31
+ @root_account_number_id = root_account_number_id
32
+ @root_account_number = root_account_number
33
+ @status = status
34
+ @entity = entity
35
+ @client = client
36
+ end
37
+
38
+ def to_s
39
+ "💰 #{@description} (#{@id}) - #{Money.from_cents(@available_balance, @currency).format}"
40
+ end
41
+
42
+ def refresh
43
+ fresh_account = @client.accounts.get(@id)
44
+ refresh_from_account(fresh_account)
45
+ end
46
+
47
+ def update(description: nil)
48
+ params = {}
49
+ params[:description] = description if description
50
+
51
+ updated_account = @client.accounts.update(@id, **params)
52
+ refresh_from_account(updated_account)
53
+ end
54
+
55
+ def active?
56
+ @status == 'active'
57
+ end
58
+
59
+ def blocked?
60
+ @status == 'blocked'
61
+ end
62
+
63
+ def closed?
64
+ @status == 'closed'
65
+ end
66
+
67
+ def test_mode?
68
+ @mode == 'test'
69
+ end
70
+
71
+ def simulate_receive_transfer(amount:)
72
+ unless test_mode?
73
+ raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode'
74
+ end
75
+
76
+ @client.simulate.receive_transfer(
77
+ account_number_id: @root_account_number_id,
78
+ amount:,
79
+ currency: @currency
80
+ )
81
+ end
82
+
83
+ private
84
+
85
+ def refresh_from_account(account)
86
+ unless account.id == @id
87
+ raise ArgumentError, 'Account must be the same instance'
88
+ end
89
+
90
+ @object = account.object
91
+ @mode = account.mode
92
+ @description = account.description
93
+ @available_balance = account.available_balance
94
+ @currency = account.currency
95
+ @is_root = account.is_root
96
+ @root_account_number_id = account.root_account_number_id
97
+ @root_account_number = account.root_account_number
98
+ @status = account.status
99
+ @entity = account.entity
100
+
101
+ self
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,105 @@
1
+ module Fintoc
2
+ module V2
3
+ class AccountNumber
4
+ attr_reader :id, :object, :description, :number, :created_at, :updated_at,
5
+ :mode, :status, :is_root, :account_id, :metadata
6
+
7
+ def initialize(
8
+ id:,
9
+ object:,
10
+ description:,
11
+ number:,
12
+ created_at:,
13
+ updated_at:,
14
+ mode:,
15
+ status:,
16
+ is_root:,
17
+ account_id:,
18
+ metadata:,
19
+ client: nil,
20
+ **
21
+ )
22
+ @id = id
23
+ @object = object
24
+ @description = description
25
+ @number = number
26
+ @created_at = created_at
27
+ @updated_at = updated_at
28
+ @mode = mode
29
+ @status = status
30
+ @is_root = is_root
31
+ @account_id = account_id
32
+ @metadata = metadata
33
+ @client = client
34
+ end
35
+
36
+ def to_s
37
+ "🔢 #{@number} (#{@id}) - #{@description}"
38
+ end
39
+
40
+ def refresh
41
+ fresh_account_number = @client.account_numbers.get(@id)
42
+ refresh_from_account_number(fresh_account_number)
43
+ end
44
+
45
+ def update(description: nil, status: nil, metadata: nil)
46
+ params = {}
47
+ params[:description] = description if description
48
+ params[:status] = status if status
49
+ params[:metadata] = metadata if metadata
50
+
51
+ updated_account_number = @client.account_numbers.update(@id, **params)
52
+ refresh_from_account_number(updated_account_number)
53
+ end
54
+
55
+ def enabled?
56
+ @status == 'enabled'
57
+ end
58
+
59
+ def disabled?
60
+ @status == 'disabled'
61
+ end
62
+
63
+ def root?
64
+ @is_root
65
+ end
66
+
67
+ def test_mode?
68
+ @mode == 'test'
69
+ end
70
+
71
+ def simulate_receive_transfer(amount:, currency: 'MXN')
72
+ unless test_mode?
73
+ raise Fintoc::Errors::InvalidRequestError, 'Simulation is only available in test mode'
74
+ end
75
+
76
+ @client.simulate.receive_transfer(
77
+ account_number_id: @id,
78
+ amount:,
79
+ currency:
80
+ )
81
+ end
82
+
83
+ private
84
+
85
+ def refresh_from_account_number(account_number)
86
+ unless account_number.id == @id
87
+ raise ArgumentError, 'AccountNumber must be the same instance'
88
+ end
89
+
90
+ @object = account_number.object
91
+ @description = account_number.description
92
+ @number = account_number.number
93
+ @created_at = account_number.created_at
94
+ @updated_at = account_number.updated_at
95
+ @mode = account_number.mode
96
+ @status = account_number.status
97
+ @is_root = account_number.is_root
98
+ @account_id = account_number.account_id
99
+ @metadata = account_number.metadata
100
+
101
+ self
102
+ end
103
+ end
104
+ end
105
+ end