finicity 0.1.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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +69 -0
  7. data/Rakefile +10 -0
  8. data/finicity.gemspec +31 -0
  9. data/lib/finicity.rb +42 -0
  10. data/lib/finicity/client.rb +350 -0
  11. data/lib/finicity/configuration.rb +13 -0
  12. data/lib/finicity/errors.rb +62 -0
  13. data/lib/finicity/logger.rb +19 -0
  14. data/lib/finicity/railtie.rb +22 -0
  15. data/lib/finicity/v1.rb +29 -0
  16. data/lib/finicity/v1/request/activate_accounts.rb +73 -0
  17. data/lib/finicity/v1/request/activate_accounts_with_mfa.rb +88 -0
  18. data/lib/finicity/v1/request/add_customer.rb +54 -0
  19. data/lib/finicity/v1/request/delete_customer.rb +45 -0
  20. data/lib/finicity/v1/request/discover_accounts.rb +71 -0
  21. data/lib/finicity/v1/request/discover_accounts_with_mfa.rb +87 -0
  22. data/lib/finicity/v1/request/get_accounts.rb +50 -0
  23. data/lib/finicity/v1/request/get_customers.rb +43 -0
  24. data/lib/finicity/v1/request/get_customers_by_username.rb +52 -0
  25. data/lib/finicity/v1/request/get_institution.rb +45 -0
  26. data/lib/finicity/v1/request/get_institutions.rb +45 -0
  27. data/lib/finicity/v1/request/get_login_form.rb +47 -0
  28. data/lib/finicity/v1/request/get_transactions.rb +60 -0
  29. data/lib/finicity/v1/request/interactive_refresh_account.rb +51 -0
  30. data/lib/finicity/v1/request/interactive_refresh_account_with_mfa.rb +74 -0
  31. data/lib/finicity/v1/request/refresh_accounts.rb +47 -0
  32. data/lib/finicity/v1/response/accounts.rb +75 -0
  33. data/lib/finicity/v1/response/customers.rb +36 -0
  34. data/lib/finicity/v1/response/error.rb +13 -0
  35. data/lib/finicity/v1/response/institutions.rb +38 -0
  36. data/lib/finicity/v1/response/login_form.rb +29 -0
  37. data/lib/finicity/v1/response/mfa.rb +22 -0
  38. data/lib/finicity/v1/response/transactions.rb +28 -0
  39. data/lib/finicity/v2.rb +7 -0
  40. data/lib/finicity/v2/request/partner_authentication.rb +39 -0
  41. data/lib/finicity/v2/response/partner_authentication.rb +12 -0
  42. data/lib/finicity/version.rb +3 -0
  43. data/spec/finicity/client_spec.rb +527 -0
  44. data/spec/finicity/v1/request/activate_accounts_spec.rb +49 -0
  45. data/spec/finicity/v1/request/activate_accounts_with_mfa_spec.rb +64 -0
  46. data/spec/finicity/v1/request/add_customer_spec.rb +37 -0
  47. data/spec/finicity/v1/request/delete_customer_spec.rb +18 -0
  48. data/spec/finicity/v1/request/discover_accounts_spec.rb +42 -0
  49. data/spec/finicity/v1/request/discover_accounts_with_mfa_spec.rb +59 -0
  50. data/spec/finicity/v1/request/get_accounts_spec.rb +18 -0
  51. data/spec/finicity/v1/request/get_customers_by_username_spec.rb +18 -0
  52. data/spec/finicity/v1/request/get_customers_spec.rb +18 -0
  53. data/spec/finicity/v1/request/get_institution_spec.rb +18 -0
  54. data/spec/finicity/v1/request/get_institutions_spec.rb +18 -0
  55. data/spec/finicity/v1/request/get_login_form_spec.rb +18 -0
  56. data/spec/finicity/v1/request/get_transactions_spec.rb +19 -0
  57. data/spec/finicity/v1/request/interactive_refresh_account_spec.rb +19 -0
  58. data/spec/finicity/v1/request/interactive_refresh_account_with_mfa_spec.rb +38 -0
  59. data/spec/finicity/v1/request/refresh_accounts_spec.rb +18 -0
  60. data/spec/finicity/v1/response/accounts_spec.rb +39 -0
  61. data/spec/finicity/v1/response/customers_spec.rb +19 -0
  62. data/spec/finicity/v1/response/error_spec.rb +19 -0
  63. data/spec/finicity/v1/response/institutions_spec.rb +19 -0
  64. data/spec/finicity/v1/response/login_form_spec.rb +31 -0
  65. data/spec/finicity/v1/response/mfa_spec.rb +23 -0
  66. data/spec/finicity/v1/response/transactions_spec.rb +47 -0
  67. data/spec/finicity/v2/request/partner_authentication_spec.rb +21 -0
  68. data/spec/finicity/v2/response/partner_authentication_spec.rb +15 -0
  69. data/spec/spec_helper.rb +36 -0
  70. metadata +265 -0
@@ -0,0 +1,13 @@
1
+ module Finicity
2
+ class Configuration
3
+ attr_accessor :base_url,
4
+ :logger,
5
+ :partner_id,
6
+ :partner_secret,
7
+ :app_key
8
+
9
+ def initialize
10
+ @logger = ::Logger.new(STDOUT)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,62 @@
1
+ module Finicity
2
+ class GenericError < ::StandardError
3
+ attr_reader :error_message, :http_status, :finicity_code
4
+
5
+ def initialize(error_message = nil, http_status = nil, finicity_code = nil)
6
+ @error_message = error_message
7
+ @http_status = http_status
8
+ @finicity_code = finicity_code
9
+ end
10
+
11
+ def to_s
12
+ status = http_status.nil? ? "" : "[Status #{http_status}] "
13
+ code = finicity_code.nil? ? "" : "[Finicity Code #{finicity_code}] "
14
+ "#{status}#{code}#{error_message}"
15
+ end
16
+ end
17
+
18
+ class AuthenticationError < ::Finicity::GenericError
19
+ end
20
+
21
+ class DuplicateCustomerError < ::StandardError
22
+ attr_reader :username
23
+
24
+ def initialize(username)
25
+ @username = username
26
+ end
27
+
28
+ def to_s
29
+ "Multiple customers found with username: #{username}"
30
+ end
31
+ end
32
+
33
+ class FinicityAggregationError < ::StandardError
34
+ ERROR_CODE_MAP = {
35
+ '0' => 'Success.',
36
+ '102' => 'Retry error. Website is down or there is a connectivity issue.',
37
+ '103' => 'Invalid Credentials. Credentials must be updated.',
38
+ '106' => 'Account Name/Number/Type mismatch.',
39
+ '108' => 'End user action required at the third party site.',
40
+ '109' => 'Password change required at the third party site.',
41
+ '185' => 'MFA answer(s) missing.',
42
+ '187' => 'Incorrect answer to MFA challenge question.'
43
+ }
44
+
45
+ attr_reader :aggregation_status_code
46
+
47
+ def initialize(aggregation_status_code)
48
+ @aggregation_status_code = aggregation_status_code
49
+ end
50
+
51
+ def error_message
52
+ ERROR_CODE_MAP[aggregation_status_code.to_s] || 'Unknown Error'
53
+ end
54
+
55
+ def to_s
56
+ "[Aggregation Status Code #{aggregation_status_code}] #{error_message}"
57
+ end
58
+ end
59
+
60
+ class InvalidCredentialsError < ::Finicity::FinicityAggregationError
61
+ end
62
+ end
@@ -0,0 +1,19 @@
1
+ module Finicity
2
+ module Logger
3
+ def log_request
4
+ ::Finicity.logger.debug do
5
+ log = "REQUEST: #{self.class.name}"
6
+ log << "\n URL: #{url}" if self.respond_to?(:url)
7
+ log << "\n QUERY: #{query}" if self.respond_to?(:query)
8
+ log << "\n BODY: #{mask_body(body)}" if self.respond_to?(:body)
9
+ log
10
+ end
11
+ end
12
+
13
+ def mask_body(body)
14
+ body = body.gsub(/<value>.*<\/value>/, "<value>[FILTERED]</value>")
15
+ body = body.gsub(/<answer>.*<\/answer>/, "<answer>[FILTERED]</answer>")
16
+ body
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Finicity
2
+ class Railtie < ::Rails::Railtie
3
+
4
+ config.finicity = ::Finicity.config
5
+
6
+ initializer "finicity_configuration" do |app|
7
+ if File.exists?(::Rails.root.join('config', 'finicity.yml'))
8
+
9
+ yaml_file = ::YAML.load_file(::Rails.root.join('config', 'finicity.yml'))
10
+
11
+ ::Finicity.configure do |config|
12
+ config.base_url = yaml_file['base_url']
13
+ config.partner_id = yaml_file['partner_id']
14
+ config.partner_secret = yaml_file['partner_secret']
15
+ config.app_key = yaml_file['app_key']
16
+ end
17
+ else
18
+ ::Rails.logger.warn("Failed to load finicity.yml")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ require 'finicity/v1/request/activate_accounts'
2
+ require 'finicity/v1/request/activate_accounts_with_mfa'
3
+ require 'finicity/v1/request/add_customer'
4
+ require 'finicity/v1/request/delete_customer'
5
+ require 'finicity/v1/request/discover_accounts'
6
+ require 'finicity/v1/request/discover_accounts_with_mfa'
7
+ require 'finicity/v1/request/get_accounts'
8
+ require 'finicity/v1/request/get_customers_by_username'
9
+ require 'finicity/v1/request/get_customers'
10
+ require 'finicity/v1/request/get_institution'
11
+ require 'finicity/v1/request/get_institutions'
12
+ require 'finicity/v1/request/get_login_form'
13
+ require 'finicity/v1/request/get_transactions'
14
+ require 'finicity/v1/request/interactive_refresh_account'
15
+ require 'finicity/v1/request/interactive_refresh_account_with_mfa'
16
+ require 'finicity/v1/request/refresh_accounts'
17
+
18
+ require 'finicity/v1/response/accounts'
19
+ require 'finicity/v1/response/customers'
20
+ require 'finicity/v1/response/error'
21
+ require 'finicity/v1/response/institutions'
22
+ require 'finicity/v1/response/login_form'
23
+ require 'finicity/v1/response/mfa'
24
+ require 'finicity/v1/response/transactions'
25
+
26
+ module Finicity
27
+ module V1
28
+ end
29
+ end
@@ -0,0 +1,73 @@
1
+ module Finicity::V1
2
+ module Request
3
+ class ActivateAccounts
4
+ include ::Finicity::Logger
5
+ extend ::HTTPClient::IncludeClient
6
+ include_http_client do |client|
7
+ client.cookie_manager = nil
8
+ end
9
+
10
+ ##
11
+ # Attributes
12
+ #
13
+ attr_accessor :accounts,
14
+ :customer_id,
15
+ :institution_id,
16
+ :token
17
+
18
+ ##
19
+ # Instance Methods
20
+ #
21
+ def initialize(token, customer_id, institution_id, accounts)
22
+ @accounts = accounts
23
+ @customer_id = customer_id
24
+ @institution_id = institution_id
25
+ @token = token
26
+ end
27
+
28
+ # The accounts parameter is the finicity representation of accounts
29
+ def activate_accounts
30
+ http_client.put(url, body, headers)
31
+ end
32
+
33
+ # The accounts parameter is the finicity representation of accounts
34
+ def body
35
+ builder = ::Nokogiri::XML::Builder.new do |xml|
36
+ xml.accounts {
37
+ accounts.each do |account|
38
+ xml.account {
39
+ xml.id(account.id)
40
+ xml.number(account.number)
41
+ xml.name(account.name)
42
+ xml.type(account.type)
43
+ }
44
+ end
45
+ }
46
+ end
47
+
48
+ builder.to_xml
49
+ end
50
+
51
+ def headers
52
+ {
53
+ 'Finicity-App-Key' => ::Finicity.config.app_key,
54
+ 'Finicity-App-Token' => token,
55
+ 'Content-Type' => 'application/xml'
56
+ }
57
+ end
58
+
59
+ def url
60
+ ::URI.join(
61
+ ::Finicity.config.base_url,
62
+ 'v1/',
63
+ 'customers/',
64
+ "#{customer_id}/",
65
+ 'institutions/',
66
+ "#{institution_id}/",
67
+ 'accounts'
68
+ )
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,88 @@
1
+ module Finicity::V1
2
+ module Request
3
+ class ActivateAccountsWithMfa
4
+ include ::Finicity::Logger
5
+ extend ::HTTPClient::IncludeClient
6
+ include_http_client do |client|
7
+ client.cookie_manager = nil
8
+ end
9
+
10
+ ##
11
+ # Attributes
12
+ #
13
+ attr_accessor :accounts,
14
+ :customer_id,
15
+ :institution_id,
16
+ :mfa_credentials,
17
+ :mfa_session,
18
+ :token
19
+
20
+ ##
21
+ # Instance Methods
22
+ #
23
+ def initialize(token, mfa_session, customer_id, institution_id, accounts, mfa_credentials)
24
+ @accounts = accounts
25
+ @customer_id = customer_id
26
+ @institution_id = institution_id
27
+ @mfa_credentials = mfa_credentials
28
+ @mfa_session = mfa_session
29
+ @token = token
30
+ end
31
+
32
+ def activate_accounts_with_mfa
33
+ http_client.put(url, body, headers)
34
+ end
35
+
36
+ # The accounts parameter is the finicity representation of accounts
37
+ def body
38
+ builder = ::Nokogiri::XML::Builder.new do |xml|
39
+ xml.accounts {
40
+ accounts.each do |account|
41
+ xml.account {
42
+ xml.id(account.id)
43
+ xml.number(account.number)
44
+ xml.name(account.name)
45
+ xml.type(account.type)
46
+ }
47
+ end
48
+ xml.mfaChallenges {
49
+ xml.questions {
50
+ mfa_credentials.each do |mfa_credential|
51
+ xml.question {
52
+ xml.text_(mfa_credential[:text])
53
+ xml.answer(mfa_credential[:answer])
54
+ }
55
+ end
56
+ }
57
+ }
58
+ }
59
+ end
60
+
61
+ builder.to_xml
62
+ end
63
+
64
+ def headers
65
+ {
66
+ 'Finicity-App-Key' => ::Finicity.config.app_key,
67
+ 'Finicity-App-Token' => token,
68
+ 'Content-Type' => 'application/xml',
69
+ 'MFA-Session' => mfa_session,
70
+ }
71
+ end
72
+
73
+ def url
74
+ ::URI.join(
75
+ ::Finicity.config.base_url,
76
+ 'v1/',
77
+ 'customers/',
78
+ "#{customer_id}/",
79
+ 'institutions/',
80
+ "#{institution_id}/",
81
+ 'accounts/',
82
+ 'mfa'
83
+ )
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,54 @@
1
+ module Finicity::V1
2
+ module Request
3
+ class AddCustomer
4
+ include ::Finicity::Logger
5
+ extend ::HTTPClient::IncludeClient
6
+ include_http_client do |client|
7
+ client.cookie_manager = nil
8
+ end
9
+
10
+ ##
11
+ # Attributes
12
+ #
13
+ attr_accessor :token,
14
+ :user_guid
15
+
16
+ ##
17
+ # Instance Methods
18
+ #
19
+ def initialize(token, user_guid)
20
+ @token = token
21
+ @user_guid = user_guid
22
+ end
23
+
24
+ def add_customer
25
+ http_client.post(url, body, headers)
26
+ end
27
+
28
+ def body
29
+ {
30
+ 'username' => user_guid,
31
+ 'email' => "#{user_guid}@mx.com",
32
+ 'firstName' => user_guid,
33
+ 'lastName' => user_guid
34
+ }.to_xml(:root => 'customer')
35
+ end
36
+
37
+ def headers
38
+ {
39
+ 'Finicity-App-Key' => ::Finicity.config.app_key,
40
+ 'Finicity-App-Token' => token,
41
+ 'Content-Type' => 'application/xml'
42
+ }
43
+ end
44
+
45
+ def url
46
+ ::URI.join(
47
+ ::Finicity.config.base_url,
48
+ 'v1/',
49
+ 'customers'
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ module Finicity::V1
2
+ module Request
3
+ class DeleteCustomer
4
+ include ::Finicity::Logger
5
+ extend ::HTTPClient::IncludeClient
6
+ include_http_client do |client|
7
+ client.cookie_manager = nil
8
+ end
9
+
10
+ ##
11
+ # Attributes
12
+ #
13
+ attr_accessor :token,
14
+ :customer_id
15
+
16
+ ##
17
+ # Instance Methods
18
+ #
19
+ def initialize(token, customer_id)
20
+ @customer_id = customer_id
21
+ @token = token
22
+ end
23
+
24
+ def delete_customer
25
+ http_client.delete(url, nil, headers)
26
+ end
27
+
28
+ def headers
29
+ {
30
+ 'Finicity-App-Key' => ::Finicity.config.app_key,
31
+ 'Finicity-App-Token' => token
32
+ }
33
+ end
34
+
35
+ def url
36
+ ::URI.join(
37
+ ::Finicity.config.base_url,
38
+ 'v1/',
39
+ 'customers/',
40
+ customer_id.to_s
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,71 @@
1
+ module Finicity::V1
2
+ module Request
3
+ class DiscoverAccounts
4
+ include ::Finicity::Logger
5
+ extend ::HTTPClient::IncludeClient
6
+ include_http_client do |client|
7
+ client.cookie_manager = nil
8
+ end
9
+
10
+ ##
11
+ # Attributes
12
+ #
13
+ attr_accessor :customer_id,
14
+ :institution_id,
15
+ :login_credentials,
16
+ :token
17
+
18
+ ##
19
+ # Instance Methods
20
+ #
21
+ def initialize(token, customer_id, institution_id, login_credentials)
22
+ @customer_id = customer_id
23
+ @institution_id = institution_id
24
+ @login_credentials = login_credentials
25
+ @token = token
26
+ end
27
+
28
+ def discover_accounts
29
+ http_client.post(url, body, headers)
30
+ end
31
+
32
+ def body
33
+ builder = ::Nokogiri::XML::Builder.new do |xml|
34
+ xml.accounts {
35
+ xml.credentials {
36
+ login_credentials.each do |login_credential|
37
+ xml.loginField {
38
+ xml.id(login_credential[:id])
39
+ xml.name(login_credential[:name])
40
+ xml.value(login_credential[:value])
41
+ }
42
+ end
43
+ }
44
+ }
45
+ end
46
+
47
+ builder.to_xml
48
+ end
49
+
50
+ def headers
51
+ {
52
+ 'Finicity-App-Key' => ::Finicity.config.app_key,
53
+ 'Finicity-App-Token' => token,
54
+ 'Content-Type' => 'application/xml'
55
+ }
56
+ end
57
+
58
+ def url
59
+ ::URI.join(
60
+ ::Finicity.config.base_url,
61
+ 'v1/',
62
+ 'customers/',
63
+ "#{customer_id}/",
64
+ 'institutions/',
65
+ "#{institution_id}/",
66
+ 'accounts'
67
+ )
68
+ end
69
+ end
70
+ end
71
+ end