plaid 3.0.0 → 4.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.
@@ -1,144 +0,0 @@
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
@@ -1,62 +0,0 @@
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
@@ -1,172 +0,0 @@
1
- require 'net/http'
2
- require 'uri'
3
- require 'multi_json'
4
-
5
- module Plaid
6
- # Internal: A class encapsulating HTTP requests to the Plaid API servers
7
- class Connector
8
- attr_reader :uri, :http, :request, :response, :body
9
-
10
- # Internal: Default read timeout for HTTP calls.
11
- DEFAULT_TIMEOUT = 120
12
-
13
- # Internal: Prepare to run request.
14
- #
15
- # path - The String path without leading slash. E.g. 'connect'
16
- # subpath - The String subpath. E.g. 'get'
17
- # auth - The Boolean flag indicating that client_id and secret should be
18
- # included into the request payload.
19
- # client - The Plaid::Client instance used to connect
20
- # (default: Plaid.client).
21
- def initialize(path, subpath = nil, auth: false, client: nil)
22
- @auth = auth
23
- @client = client || Plaid.client
24
- verify_configuration
25
-
26
- path = File.join(@client.env, path.to_s)
27
- path = File.join(path, subpath.to_s) if subpath
28
-
29
- @uri = URI.parse(path)
30
-
31
- @http = Net::HTTP.new(@uri.host, @uri.port)
32
- @http.use_ssl = true
33
-
34
- @http.read_timeout = Plaid.read_timeout || DEFAULT_TIMEOUT
35
- end
36
-
37
- # Internal: Run GET request.
38
- #
39
- # Returns the parsed JSON response body.
40
- def get(payload = {})
41
- payload = with_credentials(payload)
42
-
43
- @uri.query = URI.encode_www_form(payload) unless payload.empty?
44
-
45
- run Net::HTTP::Get.new(@uri)
46
- end
47
-
48
- # Internal: Run POST request.
49
- #
50
- # Adds client_id and secret to the payload if @auth is true.
51
- #
52
- # payload - The Hash with data.
53
- #
54
- # Returns the parsed JSON response body.
55
- def post(payload)
56
- post_like payload, Net::HTTP::Post.new(@uri.path)
57
- end
58
-
59
- # Internal: Run PATCH request.
60
- #
61
- # Adds client_id and secret to the payload if @auth is true.
62
- #
63
- # payload - The Hash with data.
64
- #
65
- # Returns the parsed JSON response body.
66
- def patch(payload)
67
- post_like payload, Net::HTTP::Patch.new(@uri.path)
68
- end
69
-
70
- # Internal: Run DELETE request.
71
- #
72
- # Adds client_id and secret to the payload if @auth is true.
73
- #
74
- # payload - The Hash with data.
75
- #
76
- # Returns the parsed JSON response body.
77
- def delete(payload)
78
- post_like payload, Net::HTTP::Delete.new(@uri.path)
79
- end
80
-
81
- # Internal: Check if MFA response received.
82
- #
83
- # Returns true if response has code 201.
84
- def mfa?
85
- @response.is_a?(Net::HTTPCreated)
86
- end
87
-
88
- private
89
-
90
- # Internal: Run the request and process the response.
91
- #
92
- # Returns the parsed JSON body or raises an appropriate exception (a
93
- # descendant of Plaid::PlaidError).
94
- def run(request)
95
- @request = request
96
- @response = http.request(@request)
97
-
98
- if @response.body.nil? || @response.body.empty?
99
- raise Plaid::ServerError.new(0, 'Server error', 'Try to connect later')
100
- end
101
-
102
- # All responses are expected to have a JSON body, so we always parse,
103
- # not looking at the status code.
104
- @body = MultiJson.load(@response.body)
105
-
106
- case @response
107
- when Net::HTTPSuccess, Net::HTTPCreated
108
- @body
109
- else
110
- raise_error
111
- end
112
- end
113
-
114
- # Internal: Run POST-like request.
115
- #
116
- # payload - The Hash with posted data.
117
- # request - The Net::HTTPGenericRequest descendant instance.
118
- def post_like(payload, request)
119
- request.set_form_data(with_credentials(payload))
120
-
121
- run request
122
- end
123
-
124
- # Internal: Raise an error with the class depending on @response.
125
- #
126
- # Returns an Array with arguments.
127
- def raise_error
128
- klass = case @response
129
- when Net::HTTPBadRequest then Plaid::BadRequestError
130
- when Net::HTTPUnauthorized then Plaid::UnauthorizedError
131
- when Net::HTTPPaymentRequired then Plaid::RequestFailedError
132
- when Net::HTTPNotFound then Plaid::NotFoundError
133
- else
134
- Plaid::ServerError
135
- end
136
-
137
- raise klass.new(body['code'], body['message'], body['resolve'])
138
- end
139
-
140
- # Internal: Verify that Plaid environment is properly configured.
141
- #
142
- # Raises NotConfiguredError if anything is wrong.
143
- def verify_configuration
144
- raise_not_configured(:env, auth: false) unless @client.env
145
- return unless @auth
146
-
147
- !@client.client_id_configured? && raise_not_configured(:client_id)
148
- !@client.secret_configured? && raise_not_configured(:secret)
149
- end
150
-
151
- # Internal: Raise a NotConfiguredError exception with proper message.
152
- def raise_not_configured(field, auth: true)
153
- message = "You must set Plaid::Client.#{field} before using any methods"
154
- message << ' which require authentication' if auth
155
- message << "! It's current value is #{@client.send(field).inspect}. " \
156
- 'E.g. add a Plaid.config do .. end block somewhere in the ' \
157
- 'initialization code of your program.'
158
-
159
- raise NotConfiguredError, message
160
- end
161
-
162
- # Internal: Merge credentials to the payload if needed.
163
- def with_credentials(payload)
164
- if @auth
165
- payload.merge(client_id: @client.client_id,
166
- secret: @client.secret)
167
- else
168
- payload
169
- end
170
- end
171
- end
172
- end
@@ -1,106 +0,0 @@
1
- module Plaid
2
- # Public: Representation of Income data.
3
- class Income
4
- # Public: The class encapsulating an income stream.
5
- class Stream
6
- # Public: The Float monthly income associated with the income stream.
7
- # E.g. 5700.
8
- attr_reader :monthly_income
9
-
10
- # Public: The Float representation of Plaid's confidence in the income
11
- # data associated with this particular income stream, with 0 being
12
- # the lowest confidence and 1 being the highest. E.g. 0.9.
13
- attr_reader :confidence
14
-
15
- # Public: The Integer extent of data found for this income stream.
16
- # E.g. 314.
17
- attr_reader :days
18
-
19
- # Public: The String name of the entity associated with this income
20
- # stream. E.g. "APPLE INC".
21
- attr_reader :name
22
-
23
- # Internal: Initialize a Stream.
24
- #
25
- # fields - The Hash with fields (keys are Strings).
26
- def initialize(fields)
27
- @monthly_income = fields['monthly_income']
28
- @confidence = fields['confidence']
29
- @days = fields['days']
30
- @name = fields['name']
31
- end
32
-
33
- # Public: Get a String representation of the Stream.
34
- #
35
- # Returns a String.
36
- def inspect
37
- "#<Plaid::Income::Stream name=#{name.inspect}, " \
38
- "monthly_income=#{monthly_income.inspect}, " \
39
- "confidence=#{confidence.inspect}, " \
40
- "days=#{days.inspect}>"
41
- end
42
-
43
- # Public: Get a String representation of the Stream.
44
- #
45
- # Returns a String.
46
- alias to_s inspect
47
- end
48
-
49
- # Public: The Array of Stream.
50
- attr_reader :income_streams
51
-
52
- # Public: The Numeric sum of user's income over the past 365 days. If Plaid
53
- # has less than 365 days of data this will be less than a full year income.
54
- # E.g. 67000.
55
- attr_reader :last_year_income
56
-
57
- # Public: The last_year_income interpolated to value before taxes. This is
58
- # the minimum pre-tax salary that assumes a filing status of single with
59
- # zero dependents. E.g. 73700.
60
- attr_reader :last_year_income_before_tax
61
-
62
- # Public: The Numeric user's income extrapolated over a year based on
63
- # current, active income streams. Income streams become inactive if they
64
- # have not recurred for more than two cycles. For example, if a weekly
65
- # paycheck hasn't been seen for the past two weeks, it is no longer active.
66
- # E.g. 69800.
67
- attr_reader :projected_yearly_income
68
-
69
- # Public: The projected_yearly_income interpolated to value before taxes.
70
- # This is the minimum pre-tax salary that assumes a filing status of single
71
- # with zero dependents. E.g. 75600.
72
- attr_reader :projected_yearly_income_before_tax
73
-
74
- # Public: The Integer max number of income streams present at the same time
75
- # over the past 365 days. E.g. 314.
76
- attr_reader :max_number_of_overlapping_income_streams
77
-
78
- # Public: The Integer total number of distinct income streams received over
79
- # the past 365 days. E.g. 2.
80
- attr_reader :number_of_income_streams
81
-
82
- def initialize(fields)
83
- @income_streams = fields['income_streams'].map { |is| Stream.new(is) }
84
-
85
- %w(last_year_income last_year_income_before_tax projected_yearly_income
86
- projected_yearly_income_before_tax number_of_income_streams
87
- max_number_of_overlapping_income_streams).each do |field|
88
- instance_variable_set "@#{field}", fields[field]
89
- end
90
- end
91
-
92
- # Public: Get a String representation of Income.
93
- #
94
- # Returns a String.
95
- def inspect
96
- "#<Plaid::Income last_year_income=#{last_year_income.inspect}, " \
97
- "projected_yearly_income=#{projected_yearly_income.inspect}, " \
98
- "number_of_income_streams=#{number_of_income_streams.inspect}, ...>"
99
- end
100
-
101
- # Public: Get a String representation of Income.
102
- #
103
- # Returns a String.
104
- alias to_s inspect
105
- end
106
- end