plaid 1.7.1 → 2.0.0.alpha
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +15 -0
- data/LICENSE +20 -0
- data/README.md +215 -63
- data/Rakefile +50 -4
- data/UPGRADING.md +45 -0
- data/bin/console +13 -0
- data/bin/setup +8 -0
- data/lib/plaid.rb +51 -88
- data/lib/plaid/account.rb +144 -0
- data/lib/plaid/category.rb +62 -0
- data/lib/plaid/client.rb +67 -0
- data/lib/plaid/connector.rb +168 -0
- data/lib/plaid/errors.rb +24 -14
- data/lib/plaid/income.rb +106 -0
- data/lib/plaid/info.rb +65 -0
- data/lib/plaid/institution.rb +240 -0
- data/lib/plaid/risk.rb +34 -0
- data/lib/plaid/transaction.rb +123 -0
- data/lib/plaid/user.rb +430 -0
- data/lib/plaid/version.rb +1 -1
- data/plaid.gemspec +20 -12
- metadata +58 -62
- data/.gitignore +0 -15
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/LICENSE.txt +0 -22
- data/PUBLISHING.md +0 -21
- data/lib/plaid/config.rb +0 -19
- data/lib/plaid/connection.rb +0 -109
- data/lib/plaid/models/account.rb +0 -24
- data/lib/plaid/models/category.rb +0 -17
- data/lib/plaid/models/exchange_token_response.rb +0 -11
- data/lib/plaid/models/info.rb +0 -12
- data/lib/plaid/models/institution.rb +0 -22
- data/lib/plaid/models/transaction.rb +0 -24
- data/lib/plaid/models/user.rb +0 -189
- data/spec/plaid/config_spec.rb +0 -67
- data/spec/plaid/connection_spec.rb +0 -191
- data/spec/plaid/error_spec.rb +0 -10
- data/spec/plaid/models/account_spec.rb +0 -37
- data/spec/plaid/models/category_spec.rb +0 -16
- data/spec/plaid/models/institution_spec.rb +0 -19
- data/spec/plaid/models/transaction_spec.rb +0 -28
- data/spec/plaid/models/user_spec.rb +0 -172
- data/spec/plaid_spec.rb +0 -263
- data/spec/spec_helper.rb +0 -14
data/UPGRADING.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
## Upgrading from 1.x to 2.0.0
|
2
|
+
|
3
|
+
Make sure you use Ruby 2.0 or higher.
|
4
|
+
|
5
|
+
Update the `Plaid.config` block:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
Plaid.config do |p|
|
9
|
+
p.client_id = '<<< Plaid provided client ID >>>' # WAS: customer_id
|
10
|
+
p.secret = '<<< Plaid provided secret key >>>' # No change
|
11
|
+
p.env = :tartan # or :api for production # WAS: environment_location
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
Use `Plaid::User.create` instead of `Plaid.add_user` (**NOTE**: parameter order has changed!)
|
16
|
+
|
17
|
+
Use `Plaid::User.load` instead of `Plaid.set_user` (**NOTE**: parameter order has changed!)
|
18
|
+
|
19
|
+
Use `Plaid::User.exchange_token` instead of `Plaid.exchange_token` (**NOTE**: parameter list has changed!)
|
20
|
+
|
21
|
+
Use `Plaid::User.create` or (`.load`) and `Plaid::User#transactions` instead of `Plaid.transactions`.
|
22
|
+
|
23
|
+
Use `Plaid::Institution.all` and `Plaid::Institution.get` instead of `Plaid.institution`.
|
24
|
+
|
25
|
+
Use `Plaid::Category.all` and `Plaid::Category.get` instead of `Plaid.category`.
|
26
|
+
|
27
|
+
`Plaid::Account#institution_type` was renamed to `Plaid::Account#institution`.
|
28
|
+
|
29
|
+
`Plaid::Transaction#account` was renamed to `Plaid::Transaction#account_id`.
|
30
|
+
|
31
|
+
`Plaid::Transaction#date` is a Date, not a String object now.
|
32
|
+
|
33
|
+
`Plaid::Transaction#cat` was removed. Use `Plaid::Transaction#category_hierarchy` and `Plaid::Transaction#category_id` directly.
|
34
|
+
|
35
|
+
`Plaid::Transaction#category` was renamed to `Plaid::Transaction#category_hierarchy`.
|
36
|
+
|
37
|
+
`Plaid::Transaction#pending_transaction` was renamed to `Plaid::Transaction#pending_transaction_id`.
|
38
|
+
|
39
|
+
Use `Plaid::User#mfa_step` instead of `Plaid::User#select_mfa_method` and `Plaid::User#mfa_authentication`.
|
40
|
+
|
41
|
+
`Plaid::User#permit?` was removed. You don't need this.
|
42
|
+
|
43
|
+
`Plaid::User.delete_user` was renamed to `Plaid::User.delete`.
|
44
|
+
|
45
|
+
**NOTE** that Symbols are now consistently used instead of Strings as product names, keys in hashes, etc. Look at the docs, they have all the examples.
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/lib/plaid.rb
CHANGED
@@ -1,100 +1,63 @@
|
|
1
1
|
require 'plaid/version'
|
2
|
-
require 'plaid/config'
|
3
2
|
require 'plaid/errors'
|
4
|
-
|
5
|
-
require 'plaid/
|
6
|
-
require 'plaid/
|
7
|
-
require 'plaid/
|
8
|
-
require 'plaid/
|
9
|
-
|
10
|
-
require '
|
11
|
-
|
3
|
+
require 'plaid/connector'
|
4
|
+
require 'plaid/category'
|
5
|
+
require 'plaid/institution'
|
6
|
+
require 'plaid/user'
|
7
|
+
require 'plaid/transaction'
|
8
|
+
require 'plaid/info'
|
9
|
+
require 'plaid/income'
|
10
|
+
require 'plaid/client'
|
11
|
+
|
12
|
+
require 'uri'
|
13
|
+
|
14
|
+
# Public: The Plaid namespace.
|
12
15
|
module Plaid
|
13
|
-
|
16
|
+
# Public: Available Plaid products.
|
17
|
+
PRODUCTS = %i(connect auth info income risk).freeze
|
14
18
|
|
15
|
-
class <<
|
16
|
-
#
|
17
|
-
|
19
|
+
class <<self
|
20
|
+
# Public: The default Client.
|
21
|
+
attr_accessor :client
|
18
22
|
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
# api_level, username, password, type
|
23
|
-
# TODO: Rename this to something more descriptive for 2.0, such as 'create_user`
|
24
|
-
def add_user(api_level, username, password, type, pin = nil, options = nil)
|
25
|
-
payload = { username: username, password: password, type: type }
|
26
|
-
|
27
|
-
payload[:pin] = pin if pin
|
28
|
-
payload[:options] = options.is_a?(Hash) ? JSON.generate(options) : options if options
|
29
|
-
|
30
|
-
res = Connection.post(api_level, payload)
|
31
|
-
User.build(res, api_level)
|
32
|
-
end
|
33
|
-
|
34
|
-
# API: public
|
35
|
-
# Exchange a Plaid Link public_token for a Plaid access_token
|
36
|
-
# Required parameters:
|
37
|
-
# public_token
|
38
|
-
# account_id (optional)
|
39
|
-
def exchange_token(public_token, account_id = nil)
|
40
|
-
payload = { public_token: public_token }
|
41
|
-
# include the account id, if set
|
42
|
-
payload[:account_id] = account_id if account_id
|
43
|
-
|
44
|
-
res = Connection.post('exchange_token', payload)
|
45
|
-
ExchangeTokenResponse.new(res)
|
46
|
-
end
|
47
|
-
|
48
|
-
# API: public
|
49
|
-
# Use this to restore a user from Plaid based upon the access token
|
50
|
-
# TODO: Rename this to something more descriptive for 2.0, such as 'get_user'
|
51
|
-
def set_user(token, api_levels=[], institution_type=nil)
|
52
|
-
_user = User.new
|
53
|
-
_user.access_token = fully_qualified_token(token, institution_type)
|
54
|
-
_user.permissions = api_levels
|
55
|
-
api_levels.each { |l| _user.get(l) }
|
56
|
-
_user
|
57
|
-
end
|
23
|
+
# Public: The Integer read timeout for requests to Plaid HTTP API.
|
24
|
+
# Should be specified in seconds. Default value is 120 (2 minutes).
|
25
|
+
attr_accessor :read_timeout
|
58
26
|
|
59
|
-
#
|
60
|
-
# Given an access code and query options, use this to get a dataset of
|
61
|
-
# transactions and accounts for # a given user. See /connect/get endpoint
|
27
|
+
# Public: A helper function to ease configuration.
|
62
28
|
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# Examples
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# API: public
|
80
|
-
# Returns the fully-qualified token based upon type
|
81
|
-
# Note: Don't see this documented in the Plaid API docs, need to investigate this
|
82
|
-
def fully_qualified_token(token, institution_type)
|
83
|
-
institution_type.nil? ? token : token + '_' + institution_type
|
84
|
-
end
|
85
|
-
|
86
|
-
# API: public
|
87
|
-
# Builds an institution object and returns when the institution details exist
|
88
|
-
def institution(id = nil)
|
89
|
-
res = Connection.get('institutions', id)
|
90
|
-
id.nil? ? Institution.all(res) : Institution.new(res)
|
29
|
+
# Yields self.
|
30
|
+
#
|
31
|
+
# Examples
|
32
|
+
#
|
33
|
+
# Plaid.configure do |p|
|
34
|
+
# p.client_id = 'Plaid provided client ID here'
|
35
|
+
# p.secret = 'Plaid provided secret key here'
|
36
|
+
# p.env = :tartan
|
37
|
+
# p.read_timeout = 300 # it's 5 minutes, yay!
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Returns nothing.
|
41
|
+
def config
|
42
|
+
client = Client.new
|
43
|
+
yield client
|
44
|
+
self.client = client
|
91
45
|
end
|
92
46
|
|
93
|
-
#
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
47
|
+
# Internal: Symbolize keys (and values) for a hash.
|
48
|
+
#
|
49
|
+
# hash - The Hash with string keys (or nil).
|
50
|
+
# values - The Boolean flag telling the function to symbolize values
|
51
|
+
# as well.
|
52
|
+
#
|
53
|
+
# Returns a Hash with keys.to_sym (or nil if hash is nil).
|
54
|
+
def symbolize_hash(hash, values: false)
|
55
|
+
return unless hash
|
56
|
+
return hash.map { |h| symbolize_hash(h) } if hash.is_a?(Array)
|
57
|
+
|
58
|
+
hash.each_with_object({}) do |(k, v), memo|
|
59
|
+
memo[k.to_sym] = values ? v.to_sym : v
|
60
|
+
end
|
98
61
|
end
|
99
62
|
end
|
100
63
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'plaid/risk'
|
2
|
+
|
3
|
+
module Plaid
|
4
|
+
# Public: Representation of user account data.
|
5
|
+
class Account
|
6
|
+
# Public: The String unique ID of the account. E.g.
|
7
|
+
# "QPO8Jo8vdDHMepg41PBwckXm4KdK1yUdmXOwK".
|
8
|
+
attr_reader :id
|
9
|
+
|
10
|
+
# Public: The String account ID unique to the accounts of a particular
|
11
|
+
# access token. E.g. "KdDjmojBERUKx3JkDd9RuxA5EvejA4SENO4AA".
|
12
|
+
attr_reader :item_id
|
13
|
+
|
14
|
+
# Public: The Float value of the current balance for the account.
|
15
|
+
attr_reader :current_balance
|
16
|
+
|
17
|
+
# Public: The Float value of the available balance for the account.
|
18
|
+
#
|
19
|
+
# The Available Balance is the Current Balance less any outstanding holds
|
20
|
+
# or debits that have not yet posted to the account. Note that not all
|
21
|
+
# institutions calculate the Available Balance. In the case that Available
|
22
|
+
# Balance is unavailable from the institution, Plaid will either return an
|
23
|
+
# Available Balance value of null or only return a Current Balance.
|
24
|
+
attr_reader :available_balance
|
25
|
+
|
26
|
+
# Public: The Symbol institution type, e.g. :wells.
|
27
|
+
attr_reader :institution
|
28
|
+
|
29
|
+
# Public: The Hash with additional information pertaining to the account
|
30
|
+
# such as the limit, name, or last few digits of the account number. E.g.
|
31
|
+
# {"name": "Plaid Savings", "number": "9606" }.
|
32
|
+
attr_reader :meta
|
33
|
+
|
34
|
+
# Public: The Symbol account type. One of :depository, :credit, :loan,
|
35
|
+
# :mortgage, :brokerage, and :other.
|
36
|
+
attr_reader :type
|
37
|
+
|
38
|
+
# Public: The String account subtype. E.g. "savings".
|
39
|
+
#
|
40
|
+
# Read more about subtypes in the Plaid API docs.
|
41
|
+
attr_reader :subtype
|
42
|
+
|
43
|
+
# Public: The Hash with account and routing numbers for the account.
|
44
|
+
#
|
45
|
+
# This attribute would be nil unless you used Auth product for the user.
|
46
|
+
#
|
47
|
+
# The Hash contains Symbol keys and String values. E.g.
|
48
|
+
# {routing: "021000021", account: "9900009606", wireRouting: "021000021"}.
|
49
|
+
attr_reader :numbers
|
50
|
+
|
51
|
+
# Public: The Risk information associated with the account.
|
52
|
+
#
|
53
|
+
# This attribute would be nil unless you used Risk product for the user.
|
54
|
+
attr_reader :risk
|
55
|
+
|
56
|
+
def initialize(hash)
|
57
|
+
@id = hash['_id']
|
58
|
+
@item_id = hash['_item']
|
59
|
+
@meta = hash['meta']
|
60
|
+
@type = hash['type'].to_sym
|
61
|
+
@subtype = hash['subtype']
|
62
|
+
@institution = hash['institution_type'].to_sym
|
63
|
+
|
64
|
+
unless (bal = hash['balance']).nil?
|
65
|
+
@available_balance = bal['available']
|
66
|
+
@current_balance = bal['current']
|
67
|
+
end
|
68
|
+
|
69
|
+
if (risk = hash['risk'])
|
70
|
+
@risk = Plaid::Risk.new(risk)
|
71
|
+
end
|
72
|
+
|
73
|
+
@numbers = Plaid.symbolize_hash(hash['numbers'])
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: Get a String representation of the account.
|
77
|
+
#
|
78
|
+
# Returns a String.
|
79
|
+
def inspect
|
80
|
+
"#<Plaid::Account id=#{id.inspect}, type=#{type.inspect}, " \
|
81
|
+
"name=#{name.inspect}, institution=#{institution.inspect}>"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Public: Get a String representation of the account.
|
85
|
+
#
|
86
|
+
# Returns a String.
|
87
|
+
alias to_s inspect
|
88
|
+
|
89
|
+
# Public: Get the account name.
|
90
|
+
#
|
91
|
+
# The name is obtained from #meta Hash.
|
92
|
+
#
|
93
|
+
# Returns the String name.
|
94
|
+
def name
|
95
|
+
meta && meta['name']
|
96
|
+
end
|
97
|
+
|
98
|
+
# Internal: Merge account information.
|
99
|
+
#
|
100
|
+
# accounts - The Array of Account instances.
|
101
|
+
# new_accounts - The Array of Account instances.
|
102
|
+
#
|
103
|
+
# Returns accounts.
|
104
|
+
def self.merge(accounts, new_accounts)
|
105
|
+
# Index accounts by their ID.
|
106
|
+
#
|
107
|
+
# Same as index = accounts.index_by(&:id) in ActiveSupport.
|
108
|
+
index = Hash[accounts.map { |a| [a.id, a] }]
|
109
|
+
|
110
|
+
new_accounts.each do |acc|
|
111
|
+
if (old_acc = index[acc.id])
|
112
|
+
old_acc.update_from(acc)
|
113
|
+
else
|
114
|
+
accounts << acc
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
accounts
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: Update account information.
|
122
|
+
#
|
123
|
+
# All fields which are not nil in another are copied to self.
|
124
|
+
#
|
125
|
+
# another - The Account instance with new information.
|
126
|
+
#
|
127
|
+
# Returns self.
|
128
|
+
def update_from(another)
|
129
|
+
# A sanity check. Nobody would want to update information from totally
|
130
|
+
# different account!
|
131
|
+
if id != another.id
|
132
|
+
raise ArgumentError, 'Plaid::Account#update_from: id != another.id!'
|
133
|
+
end
|
134
|
+
|
135
|
+
%i(item_id meta name type subtype institution available_balance
|
136
|
+
current_balance numbers risk).each do |field|
|
137
|
+
value = another.send(field)
|
138
|
+
instance_variable_set("@#{field}", value) unless value.nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
self
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Plaid
|
2
|
+
# Public: A class which encapsulates information about a Plaid category.
|
3
|
+
class Category
|
4
|
+
# Public: The String category ID, e.g. "21010006".
|
5
|
+
attr_reader :id
|
6
|
+
|
7
|
+
# Public: The Symbol category type. One of :special, :place, :digital.
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
# Public: The Array of String hierarchy. E.g.
|
11
|
+
# ["Food and Drink", "Nightlife"].
|
12
|
+
attr_reader :hierarchy
|
13
|
+
|
14
|
+
# Internal: Initialize a Category with given fields.
|
15
|
+
def initialize(fields)
|
16
|
+
@type = fields['type'].to_sym
|
17
|
+
@hierarchy = fields['hierarchy']
|
18
|
+
@id = fields['id']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Get a String representation of Category.
|
22
|
+
#
|
23
|
+
# Returns a String.
|
24
|
+
def inspect
|
25
|
+
"#<Plaid::Category id=#{id.inspect}, type=#{type.inspect}, " \
|
26
|
+
"hierarchy=#{hierarchy.inspect}>"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Get a String representation of Category.
|
30
|
+
#
|
31
|
+
# Returns a String.
|
32
|
+
alias to_s inspect
|
33
|
+
|
34
|
+
# Public: Get information about all available categories.
|
35
|
+
#
|
36
|
+
# Does a GET /categories call.
|
37
|
+
#
|
38
|
+
# client - The Plaid::Client instance used to connect
|
39
|
+
# (default: Plaid.client).
|
40
|
+
#
|
41
|
+
# Returns an Array of Category instances.
|
42
|
+
def self.all(client: nil)
|
43
|
+
Connector.new(:categories, client: client).get.map do |category_data|
|
44
|
+
new(category_data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Get information about a given category.
|
49
|
+
#
|
50
|
+
# Does a GET /categories/:id call.
|
51
|
+
#
|
52
|
+
# id - the String category ID (e.g. "17001013").
|
53
|
+
# client - The Plaid::Client instance used to connect
|
54
|
+
# (default: Plaid.client).
|
55
|
+
#
|
56
|
+
# Returns a Category instance or raises Plaid::NotFoundError if category
|
57
|
+
# with given id is not found.
|
58
|
+
def self.get(id, client: nil)
|
59
|
+
new Connector.new(:categories, id, client: client).get
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/plaid/client.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Plaid
|
2
|
+
# Public: A class encapsulating client_id, secret, and Plaid API URL.
|
3
|
+
class Client
|
4
|
+
# Public: The String Plaid account client ID to authenticate requests.
|
5
|
+
attr_accessor :client_id
|
6
|
+
|
7
|
+
# Public: The String Plaid account secret to authenticate requests.
|
8
|
+
attr_accessor :secret
|
9
|
+
|
10
|
+
# Public: Plaid environment, i.e. String base URL of the API site.
|
11
|
+
#
|
12
|
+
# E.g. 'https://tartan.plaid.com'.
|
13
|
+
attr_reader :env
|
14
|
+
|
15
|
+
# Public: Set Plaid environment to use.
|
16
|
+
#
|
17
|
+
# env - The Symbol (:tartan, :api), or a full String URL like
|
18
|
+
# 'https://tartan.plaid.com'.
|
19
|
+
def env=(env)
|
20
|
+
case env
|
21
|
+
when :tartan, :api
|
22
|
+
@env = "https://#{env}.plaid.com/"
|
23
|
+
when String
|
24
|
+
begin
|
25
|
+
URI.parse(env)
|
26
|
+
@env = env
|
27
|
+
rescue
|
28
|
+
raise ArgumentError, 'Invalid URL in Plaid::Client.env' \
|
29
|
+
" (#{env.inspect}). " \
|
30
|
+
'Specify either Symbol (:tartan, :api), or a ' \
|
31
|
+
"full URL, like 'https://tartan.plaid.com'"
|
32
|
+
end
|
33
|
+
else
|
34
|
+
raise ArgumentError, 'Invalid value for Plaid::Client.env' \
|
35
|
+
" (#{env.inspect}): " \
|
36
|
+
'must be :tartan, :api, or a full URL, ' \
|
37
|
+
"e.g. 'https://tartan.plaid.com'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: Construct a Client instance.
|
42
|
+
#
|
43
|
+
# env - The Symbol (:tartan, :api), or a full String URL like
|
44
|
+
# 'https://tartan.plaid.com'.
|
45
|
+
# client_id - The String Plaid account client ID to authenticate requests.
|
46
|
+
# secret - The String Plaid account secret to authenticate requests.
|
47
|
+
def initialize(env: nil, client_id: nil, secret: nil)
|
48
|
+
env && self.env = env
|
49
|
+
self.client_id = client_id
|
50
|
+
self.secret = secret
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Check if client_id is configured.
|
54
|
+
#
|
55
|
+
# Returns true if it is.
|
56
|
+
def client_id_configured?
|
57
|
+
@client_id.is_a?(String) && !@client_id.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Check if client_id is configured.
|
61
|
+
#
|
62
|
+
# Returns true if it is.
|
63
|
+
def secret_configured?
|
64
|
+
@secret.is_a?(String) && !@secret.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|