finicity-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ff94ad68c52843464a17802b5f6322d226a0d5e9
4
+ data.tar.gz: 98254830bb29dcab441aecd9d0365fdcd05ac7c3
5
+ SHA512:
6
+ metadata.gz: 4adbb085e03e3479435deab13a49986590cfdd609fac99f99e7e41ce18642db812d14cc240618c90b24ccdd9c595e3367990261c68fc1111298307440ff49cd8
7
+ data.tar.gz: 5537f9ebbec5479ed4de430b76e33e01fc83bcdbdd4eca29dffe1481f34225e8d4d7a137b1f005c1d40b2605608ec37716b7f195a10af5f5530a6135d13ba1e1
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - "bin/**/*"
5
+ - "db/**/*"
6
+ - "*.gemspec"
7
+ DisplayCopNames: true
8
+
9
+ Lint/EndAlignment:
10
+ AlignWith: variable
11
+
12
+ Metrics/LineLength:
13
+ Max: 120
14
+
15
+ Style/StringLiterals:
16
+ EnforcedStyle: double_quotes
17
+
18
+ Documentation:
19
+ Enabled: false
20
+
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Style/FileName:
25
+ Enabled: false
26
+
27
+ Documentation:
28
+ Enabled: false
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ finicity-ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.0
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.13.6
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at just.azzurri@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "codecov", require: false, group: :test
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Azzurrio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # Finicity::Ruby
2
+ [![Build Status](https://travis-ci.org/Fundthrough/finicity-ruby.svg?branch=master)](https://travis-ci.org/Fundthrough/finicity-ruby)
3
+ [![Code Climate](https://codeclimate.com/github/Fundthrough/finicity-ruby/badges/gpa.svg)](https://codeclimate.com/github/Fundthrough/finicity-ruby)
4
+ [![codecov](https://codecov.io/gh/Fundthrough/finicity-ruby/branch/master/graph/badge.svg)](https://codecov.io/gh/Fundthrough/finicity-ruby)
5
+ [![License](http://img.shields.io/:license-MIT-blue.svg?style=flat)](LICENSE)
6
+
7
+
8
+ Welcome to `finicity-ruby` gem. This gem is built to communicate easily with Finicity's Aggregation API. It only uses the `JSON` representation of the API. Pull requests are welcome to help improving the gem.
9
+
10
+ - [Installation](#installation)
11
+ - [Setup](#setup)
12
+ - [Usage](#usage)
13
+ - [Institutions](#1-institutions)
14
+ - [List](#list)
15
+ - [Get specific Institution (with login Credentials)](#get-specific-institution-with-login-credentials)
16
+ - [Customers](#2-customers)
17
+ - [Add new customer](#add-new-customer)
18
+ - [List all customers](#list-all-customers)
19
+ - [Delete customer](#delete-customer)
20
+ - [Accounts](#3-accounts)
21
+ - [Discover and add all accounts](#discover-and-add-all-accounts)
22
+ - [Discover and add all accounts (with MFA)](#discover-and-add-all-accounts-with-mfa)
23
+ - [List accounts](#list-accounts)
24
+ - [Get specific account](#get-specific-account)
25
+ - [Delete specific account](#delete-specific-account)
26
+ - [Refresh accounts](#refresh-accounts)
27
+ - [Refresh accounts (with MFA)](#refresh-accounts-with-mfa)
28
+ - [Activate accounts](#activate-accounts)
29
+ - [Update account credentials](#update-account-credentials)
30
+ - [Transactions](#4-transactions)
31
+ - [List all transactions](#list-all-transactions)
32
+ - [List all transactions for specific account](#list-all-transactions-for-specific-account)
33
+ - [Load historic transactions](#load-historic-transactions)
34
+ - [Contributing](#contributing)
35
+ - [License](#license)
36
+
37
+ ## Installation
38
+
39
+ The gem is available through `Rubygems` and can be installed via:
40
+
41
+ $ gem install finicity-ruby
42
+
43
+ or add it to your Gemfile like this:
44
+
45
+ ```ruby
46
+ gem "finicity-ruby"
47
+ ```
48
+
49
+ ## Setup
50
+ Then, in your `config/initializers/` add `finicity.rb` file with corresponding values. Right now we are using redis to store the temp app token which used with each request. In the future, we are going to support multiple options to store token like memory, file & redis. Meanwhile just set the `redis_url` config into your redis host url.
51
+
52
+ Finicity.configure do |config|
53
+ config.redis_url = "redis://127.0.0.1:6379"
54
+ config.app_key = "xxxxxxxxxxxxxxxx"
55
+ config.partner_id = "xxxxxxxxxxxxxxxx"
56
+ config.partner_secret = "xxxxxxxxxxxxxxxx"
57
+ config.app_type = "testing" # or "active" for production apps.
58
+ config.verbose = false
59
+ config.max_retries = 1 # How many times do you want to retry the request in case timeout failures.
60
+ end
61
+
62
+ ## Usage
63
+ ### 1. Institutions
64
+ #### List
65
+
66
+ To get all institutions
67
+
68
+ response = Finicity::Client.institution.list
69
+ response.status_code # 200
70
+ response.success? # true
71
+ response.body.institutions # returns all institutions [{name: "Royal Bank of Canada", id: "1411", ...]
72
+
73
+ To search for specific institution
74
+
75
+ Finicity::Client.institution.list(search: "Royal Bank of Canada")
76
+
77
+ #### Get specific Institution (with login Credentials)
78
+ Using the `id` you just got from the list above, you can find any specific institution
79
+
80
+ response = Finicity::Client.institution.get("107132")
81
+ response.body.institution.login_credentials # [{id: 1231, name: "username", description: "Please enter your username", ...]
82
+
83
+ ### 2. Customers
84
+ #### Add new customer
85
+ Finicity requires `username` to add a new customer. The response will contain the customer ID to be used for subsequent calls.
86
+
87
+ response = Finicity::Client.customer.add("YetAnotherBatman")
88
+ customer_id = response.body.customer.id
89
+
90
+ #### List all customers
91
+ To list all created customers from Finicity.
92
+
93
+ Finicity::Client.customer.list
94
+
95
+ #### Delete customer
96
+ Using the `customer_id` you just got after you add the customer.
97
+
98
+ Finicity::Client.scope(customer_id).delete
99
+
100
+ ### 3. Accounts
101
+ #### Discover and add all accounts
102
+ To discover & add the accounts into your customer, you've to authenticate it using the `institution#login_credentials`.
103
+
104
+ institution_id = 101732
105
+ credentials = [{id: 101732001, name: "Banking Userid", value: "Azzurrio"}, { id: "101732002", name: "Banking Password", value: "LetMePass"]
106
+ Finicity::Client.scope(customer_id).account.add_all(institution_id, credentials)
107
+
108
+ #### Discover and add all accounts (with MFA)
109
+ In case you get a MFA required. You can submit the answer into `add_all_mfa`. The `mfa_session` will be provided in `add_all` response headers.
110
+
111
+ response = Finicity::Client.scope(customer_id).account.add_all(institution_id, credentials)
112
+ response.status_code # 203
113
+ mfa_session = response.headers["MFA-session"] # e86jnv923nsas4
114
+ response.body.questions # [{ text: "What's your super hero?" }]
115
+ answers = [{ text: "What's your super hero?", answer: "Batman" }]
116
+ Finicity::Client.scope(customer_id).account.add_all_mfa(institution_id, mfa_sessiom, answers)
117
+
118
+ #### List accounts
119
+
120
+ Finicity::Client.scope(customer_id).account.list
121
+
122
+ #### Get specific account
123
+
124
+ Finicity::Client.scope(customer_id).account.get("236534") # using account id
125
+
126
+ #### Delete specific account
127
+
128
+ Finicity::Client.scope(customer_id).account.delete(account_id)
129
+
130
+ #### Refresh accounts
131
+ After adding the accounts you have to refresh them so you can have access into their transactions.
132
+
133
+ Finicity::Client.scope(customer_id).account.refresh(institution_login_id)
134
+
135
+ #### Refresh accounts (with MFA)
136
+ In case you get MFA, just like `as add_all_mfa`
137
+
138
+ Finicity::Client.scope(customer_id).account.refresh(institution_login_id, mfa_session, answers)
139
+
140
+ #### Activate accounts
141
+ Sometimes you get accounts with type `unknown`, so you need to activate those accounts with the correct types.
142
+
143
+ accounts = [{id: 12412, type: "savings"}, {id: 15434, type: "creditCard"}]
144
+ Finicity::Client.scope(customer_id).account.activate(institution_id, accounts)
145
+
146
+ #### Update account credentials
147
+ In case account credentials have been changed, you can you get credentials and update them after populating the values correct credentials.
148
+
149
+ Finicity::Client.scope(customer_id).account.credentials(account_id) # To be used for updating credentials
150
+ Finicity::Client.scope(customer_id).account.update_credentials(account_id, credentials)
151
+
152
+ ### 4. Transactions
153
+ #### List all transactions
154
+ This one will get all transactions for the given customer. You have to specify the date range using `from:` and `to:` named parameters.
155
+
156
+ Finicity::Client.scope(customer_id).transaction.list(from: 6.months.ago, to: Date.today)
157
+
158
+ #### List all transactions for specific account
159
+ In case you only need transactions for specific account. You have to provide the `account_id` as extra parameter.
160
+
161
+ Finicity::Client.scope(customer_id).transaction.list_for_account(account_id, from: 6.months.ago, to: Date.today)
162
+
163
+ #### Load historic transactions
164
+ This's a feature in Finicity API which gives you the ability to access +6 months transactions. This could be only used per account and it's an interactive request, which means MFA challenge could be appeared.
165
+
166
+ Finicity::Client.scope(customer_id).transaction.load_historic(account_id)
167
+ Finicity::Client.scope(customer_id).transaction.load_historic_mfa(account_id, mfa_session, answers)
168
+
169
+ ## Contributing
170
+
171
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Fundthrough/finicity-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
172
+
173
+
174
+ ## License
175
+
176
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
177
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "finicity/ruby"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "finicity/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "finicity-ruby"
8
+ spec.version = Finicity::VERSION
9
+ spec.authors = ["Azzurrio"]
10
+ spec.email = ["just.azzurri@gmail.com"]
11
+
12
+ spec.summary = "Ruby Client for Finicity Aggregation API"
13
+ spec.description = "Ruby Client for Finicity Aggregation API"
14
+ spec.homepage = "https://github.com/Fundthrough/finicity-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.required_ruby_version = ">= 2.3.0"
23
+ spec.required_rubygems_version = ">= 2.0.0"
24
+
25
+ spec.add_runtime_dependency "hashie", "~> 3.4.4"
26
+ spec.add_runtime_dependency "httparty", "~> 0.14.0"
27
+ spec.add_runtime_dependency "redis", "~> 3.3.1"
28
+ spec.add_runtime_dependency "activesupport"
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.11"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency "rubocop"
34
+ end
@@ -0,0 +1,41 @@
1
+ require "finicity/resources/base"
2
+ require "finicity/resources/institution"
3
+ require "finicity/resources/customer"
4
+ require "finicity/resources/account"
5
+ require "finicity/resources/transaction"
6
+
7
+ module Finicity
8
+ class Client
9
+ attr_reader :customer_id
10
+
11
+ def self.scope(customer_id)
12
+ new(customer_id)
13
+ end
14
+
15
+ def self.customer
16
+ Finicity::Resources::Customer
17
+ end
18
+
19
+ def self.institution
20
+ Finicity::Resources::Institution
21
+ end
22
+
23
+ def customer
24
+ @customer ||= Finicity::Resources::Customer.new(customer_id)
25
+ end
26
+
27
+ def account
28
+ @account ||= Finicity::Resources::Account.new(customer_id)
29
+ end
30
+
31
+ def transaction
32
+ @transaction ||= Finicity::Resources::Transaction.new(customer_id)
33
+ end
34
+
35
+ protected
36
+
37
+ def initialize(customer_id)
38
+ @customer_id = customer_id
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ require "hashie"
2
+
3
+ module Finicity
4
+ module Configurable
5
+ KEYS = [:redis_url, :app_key, :partner_id, :partner_secret, :max_retries, :app_type, :verbose].freeze
6
+
7
+ attr_writer(*KEYS)
8
+
9
+ def configure
10
+ yield self
11
+ self
12
+ end
13
+
14
+ def configs
15
+ @configs ||= begin
16
+ hash = {}
17
+ KEYS.each { |key| hash[key] = instance_variable_get(:"@#{key}") }
18
+ Hashie::Mash.new(hash)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require "finicity/fetchers/token"
2
+
3
+ module Finicity
4
+ module Fetchers
5
+ class API < Base
6
+ class << self
7
+ def request(*args)
8
+ response = super(*args)
9
+
10
+ return response unless invalid_app_token?(response)
11
+
12
+ app_token.refresh
13
+ request(*args)
14
+ end
15
+
16
+ protected
17
+
18
+ def invalid_app_token?(response)
19
+ response.status_code == 401 && INVALID_APP_TOKEN_CODES.include?(response.body&.code)
20
+ end
21
+
22
+ def default_headers
23
+ { "Finicity-App-Token" => app_token.get }
24
+ end
25
+
26
+ def app_token
27
+ ::Finicity::Fetchers::Token
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ require "httparty"
2
+
3
+ module Finicity
4
+ module Fetchers
5
+ class Base
6
+ include HTTParty
7
+
8
+ base_uri "https://api.finicity.com/aggregation"
9
+ headers "Content-Type" => "application/json"
10
+ headers "Accept" => "application/json"
11
+ headers "Finicity-App-Key" => Finicity.configs.app_key
12
+ debug_output $stdout if Finicity.configs.verbose
13
+
14
+ class << self
15
+ def request(method, endpoint, opts = {})
16
+ tries = 0
17
+ loop do
18
+ begin
19
+ break fetch(method, endpoint, opts)
20
+ rescue Net::ReadTimeout, Errno::ECONNREFUSED, Net::OpenTimeout => e
21
+ raise e if (tries += 1) > Finicity.configs.max_retries.to_i
22
+ end
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def fetch(method, endpoint, opts)
29
+ request_opts = normalize_request_options(opts)
30
+
31
+ response = send(method, endpoint, request_opts)
32
+
33
+ raise Finicity::ApiServerError, response.body if server_error?(response)
34
+
35
+ Hashie::Mash.new(
36
+ success?: response.success?,
37
+ status_code: response.code,
38
+ body: parse_json(response.body),
39
+ headers: response.headers
40
+ )
41
+ end
42
+
43
+ def normalize_request_options(opts)
44
+ opts.clone.tap do |o|
45
+ o[:headers] = default_headers.merge(o[:headers].to_h)
46
+ o[:body] = jsonify(o[:body]) if o[:body].present?
47
+ o[:query] = camelcase_keys(o[:query]) if o[:query].present?
48
+ end
49
+ end
50
+
51
+ def parse_json(body)
52
+ result = JSON.parse(body.to_s).deep_transform_keys!(&:underscore)
53
+ Hashie::Mash.new(result)
54
+ rescue JSON::ParserError
55
+ body
56
+ end
57
+
58
+ def jsonify(body)
59
+ camelcase_keys(body).to_json
60
+ end
61
+
62
+ def camelcase_keys(hash)
63
+ hash.deep_transform_keys! { |k| k.to_s.camelcase(:lower) }
64
+ end
65
+
66
+ def server_error?(response)
67
+ other_content_type?(response)
68
+ end
69
+
70
+ def other_content_type?(response)
71
+ content_type = response.headers["Content-Type"]&.downcase
72
+ content_type.present? && content_type != "application/json"
73
+ end
74
+
75
+ def default_headers
76
+ {}
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,50 @@
1
+ require "redis"
2
+
3
+ module Finicity
4
+ module Fetchers
5
+ class Token < Base
6
+ class << self
7
+ def get
8
+ refresh if token_expired?
9
+ token
10
+ end
11
+
12
+ def refresh
13
+ response = fetch_new_one
14
+
15
+ raise Finicity::TokenRefreshError, response.body unless response.success?
16
+
17
+ redis["finicity-token-expires-at"] = 90.minutes.from_now.to_s
18
+ redis["finicity-token"] = response.body.token
19
+ end
20
+
21
+ protected
22
+
23
+ def fetch_new_one
24
+ endpoint = "/v2/partners/authentication"
25
+ body = {
26
+ partner_id: Finicity.configs.partner_id,
27
+ partner_secret: Finicity.configs.partner_secret
28
+ }
29
+ request(:post, endpoint, body: body)
30
+ end
31
+
32
+ def token_expired?
33
+ !(token_expired_at.present? && Time.parse(token_expired_at).future?)
34
+ end
35
+
36
+ def token
37
+ redis["finicity-token"]
38
+ end
39
+
40
+ def token_expired_at
41
+ redis["finicity-token-expires-at"]
42
+ end
43
+
44
+ def redis
45
+ Redis.new(url: Finicity.configs.redis_url)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ module Finicity
2
+ module Fetchers
3
+ autoload :Base, "finicity/fetchers/base"
4
+ autoload :Token, "finicity/fetchers/token"
5
+ autoload :API, "finicity/fetchers/api"
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ module Finicity
2
+ module Resources
3
+ class Account < Base
4
+ def add_all(institution_id, credentials)
5
+ endpoint = "/v1/customers/#{customer_id}/institutions/#{institution_id}/accounts/addall"
6
+ body = { credentials: credentials }
7
+
8
+ request(:post, endpoint, body: body)
9
+ end
10
+
11
+ def add_all_mfa(institution_id, mfa_session, questions)
12
+ endpoint = "/v1/customers/#{customer_id}/institutions/#{institution_id}/accounts/addall/mfa"
13
+ body = { mfa_challenges: { questions: questions } }
14
+ headers = { "MFA-Session" => mfa_session }
15
+
16
+ request(:post, endpoint, body: body, headers: headers)
17
+ end
18
+
19
+ def list
20
+ endpoint = "/v1/customers/#{customer_id}/accounts"
21
+
22
+ request(:get, endpoint)
23
+ end
24
+
25
+ def activate(institution_id, accounts)
26
+ endpoint = "/v2/customers/#{customer_id}/institutions/#{institution_id}/accounts"
27
+
28
+ request(:put, endpoint, body: { accounts: accounts })
29
+ end
30
+
31
+ def refresh(institution_login_id)
32
+ endpoint = "/v1/customers/#{customer_id}/institutionLogins/#{institution_login_id}/accounts"
33
+
34
+ request(:post, endpoint)
35
+ end
36
+
37
+ def refresh_mfa(institution_login_id, mfa_session, questions)
38
+ endpoint = "/v1/customers/#{customer_id}/institutionLogins/#{institution_login_id}/accounts/mfa"
39
+
40
+ body = { questions: questions }
41
+ headers = { "MFA-Session" => mfa_session }
42
+
43
+ request(:post, endpoint, body: body, headers: headers)
44
+ end
45
+
46
+ def get(account_id)
47
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}"
48
+
49
+ request(:get, endpoint)
50
+ end
51
+
52
+ def delete(account_id)
53
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}"
54
+
55
+ request(:delete, endpoint)
56
+ end
57
+
58
+ def credentials(account_id)
59
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}/loginForm"
60
+
61
+ request(:get, endpoint)
62
+ end
63
+
64
+ def update_credentials(account_id, credentials)
65
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}/loginForm"
66
+ body = { login_form: credentials }
67
+
68
+ request(:put, endpoint, body: body)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ require "finicity/fetchers"
2
+
3
+ module Finicity
4
+ module Resources
5
+ class Base
6
+ attr_reader :customer_id
7
+
8
+ def initialize(customer_id)
9
+ @customer_id = customer_id
10
+ end
11
+
12
+ def self.request(*args)
13
+ ::Finicity::Fetchers::API.request(*args)
14
+ end
15
+
16
+ protected
17
+
18
+ def request(*args)
19
+ self.class.request(*args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Finicity
2
+ module Resources
3
+ class Customer < Base
4
+ def self.add(username)
5
+ endpoint = "/v1/customers/#{Finicity.configs.app_type}"
6
+ body = { username: username }
7
+
8
+ request(:post, endpoint, body: body)
9
+ end
10
+
11
+ def self.list(query = {})
12
+ endpoint = "/v1/customers"
13
+ query = { query: query } if query.present?
14
+
15
+ request(:get, endpoint, query)
16
+ end
17
+
18
+ def delete
19
+ endpoint = "/v1/customers/#{customer_id}"
20
+
21
+ request(:delete, endpoint)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Finicity
2
+ module Resources
3
+ class Institution < Base
4
+ def self.list(query = {})
5
+ endpoint = "/v1/institutions"
6
+ query = { query: query } if query.present?
7
+
8
+ request(:get, endpoint, query)
9
+ end
10
+
11
+ def self.get(institution_id)
12
+ endpoint = "/v1/institutions/#{institution_id}/details"
13
+
14
+ request(:get, endpoint)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ module Finicity
2
+ module Resources
3
+ class Transaction < Base
4
+ def list(from:, to:, params: {})
5
+ endpoint = "/v2/customers/#{customer_id}/transactions"
6
+ query = { from_date: from.to_time.to_i, to_date: to.to_time.to_i }.merge(params)
7
+
8
+ request(:get, endpoint, query: query)
9
+ end
10
+
11
+ def list_for_account(account_id, from:, to:, params: {})
12
+ endpoint = "/v2/customers/#{customer_id}/accounts/#{account_id}/transactions"
13
+ query = { from_date: from.to_time.to_i, to_date: to.to_time.to_i }.merge(params)
14
+
15
+ request(:get, endpoint, query: query)
16
+ end
17
+
18
+ def load_historic(account_id)
19
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}/transactions/historic"
20
+
21
+ request(:post, endpoint)
22
+ end
23
+
24
+ def load_historic_mfa(account_id, mfa_session, questions)
25
+ endpoint = "/v1/customers/#{customer_id}/accounts/#{account_id}/transactions/historic/mfa"
26
+ headers = { "MFA-Session" => mfa_session }
27
+ body = { questions: questions }
28
+
29
+ request(:post, endpoint, body: body, headers: headers)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Finicity
2
+ VERSION = "1.0.0".freeze
3
+ end
@@ -0,0 +1,41 @@
1
+ require "finicity/configurable"
2
+ require "finicity/version"
3
+ require "finicity/client"
4
+ require "active_support/all"
5
+
6
+ module Finicity
7
+ extend Finicity::Configurable
8
+
9
+ class ApiServerError < StandardError; end
10
+ class TokenRefreshError < StandardError; end
11
+ class LoadHistoricTxnError < StandardError; end
12
+
13
+ # Description: Expired Finicity app token
14
+ # Required Action: Renew app token
15
+ INVALID_APP_TOKEN_CODES = [10_022, 10_023].freeze
16
+
17
+ # Description: Retry Error or Problem Connecting to the Institution
18
+ # Required Action: Nothing. Just try again later.
19
+ CONNECTION_PROBLEM_CODES = [102, 320, 580].freeze
20
+
21
+ # Description: Invalid Credentials
22
+ # Required Action: Prompt the customer for the correct credentials.
23
+ INVALID_CREDENTIALS_CODES = [103].freeze
24
+
25
+ # Description: User Action Required
26
+ # Required Action: Prompt the user to login to their account directly at the institution's website
27
+ # and follow the instructions there
28
+ USER_ACTION_REQUIRED_CODES = [108, 109].freeze
29
+
30
+ # Description: Missing or Incorrect MFA Answer
31
+ # Required Action: Refresh Customer Account and follow the MFA sequence to prompt the user for the missing information
32
+ INCORRECT_MFA_ANSWERS_CODES = [185, 187].freeze
33
+
34
+ # Description: The account is currently being aggregated.
35
+ # Required Action: Nothing. This is only an informational message. Try again later.
36
+ ACCOUNT_BEING_AGG_CODES = [325, 5006].freeze
37
+
38
+ # Description: Missing Parameter
39
+ # Required Action: A required field was left blank, Submit the request again, with valid text
40
+ MISSING_PARAMS_CODES = [10_005].freeze
41
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: finicity-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Azzurrio
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.4.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.4.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.14.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.14.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.3.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.3.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Ruby Client for Finicity Aggregation API
126
+ email:
127
+ - just.azzurri@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".rubocop.yml"
135
+ - ".ruby-gemset"
136
+ - ".ruby-version"
137
+ - ".travis.yml"
138
+ - CODE_OF_CONDUCT.md
139
+ - Gemfile
140
+ - LICENSE.txt
141
+ - README.md
142
+ - Rakefile
143
+ - bin/console
144
+ - bin/setup
145
+ - finicity-ruby.gemspec
146
+ - lib/finicity-ruby.rb
147
+ - lib/finicity/client.rb
148
+ - lib/finicity/configurable.rb
149
+ - lib/finicity/fetchers.rb
150
+ - lib/finicity/fetchers/api.rb
151
+ - lib/finicity/fetchers/base.rb
152
+ - lib/finicity/fetchers/token.rb
153
+ - lib/finicity/resources/account.rb
154
+ - lib/finicity/resources/base.rb
155
+ - lib/finicity/resources/customer.rb
156
+ - lib/finicity/resources/institution.rb
157
+ - lib/finicity/resources/transaction.rb
158
+ - lib/finicity/version.rb
159
+ homepage: https://github.com/Fundthrough/finicity-ruby
160
+ licenses:
161
+ - MIT
162
+ metadata: {}
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 2.3.0
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 2.0.0
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.5.1
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Ruby Client for Finicity Aggregation API
183
+ test_files: []