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,507 +0,0 @@
1
- require_relative 'account'
2
-
3
- module Plaid
4
- # Public: A class which encapsulates the authenticated user for all Plaid
5
- # products.
6
- class User
7
- # Public: The access token for authenticated user.
8
- attr_reader :access_token
9
-
10
- # Public: The processor token for a given account and authenticated user.
11
- attr_reader :processor_token
12
-
13
- # Public: The current product. Provides a context for #update and #delete
14
- # calls. See Plaid::PRODUCTS.
15
- attr_reader :product
16
-
17
- # Public: The Array of Account instances providing accounts information
18
- # for the user.
19
- attr_reader :accounts
20
-
21
- # Public: The Array of Transactions provided by initial call to User.create.
22
- #
23
- # If the :login_only option of User.create is set to false, the initial
24
- # 30-day transactional data are returned during the API call. This attribute
25
- # contains them.
26
- attr_reader :initial_transactions
27
-
28
- # Public: The Symbol MFA type to be used (or nil, if no MFA required).
29
- #
30
- # E.g. :questions, :list, or :device.
31
- attr_reader :mfa_type
32
-
33
- # Public: The MFA data (Hash or Array of Hash) or nil, if no MFA required.
34
- #
35
- # E.g. [{ question: "What was the name of your first pet?" }]
36
- # or
37
- # [{ mask: 't..t@plaid.com', type: 'email' },
38
- # { mask: 'xxx-xxx-5309', type: 'phone' }]
39
- # or
40
- # { message: 'Code sent to xxx-xxx-5309' }
41
- attr_reader :mfa
42
-
43
- # Public: The String stripe bank account token.
44
- #
45
- # This field is set when you use User.exchange_token to convert Link
46
- # public_token into an access token suitable for Plaid API.
47
- attr_reader :stripe_bank_account_token
48
-
49
- # Internal: The Plaid::Client instance used to make queries.
50
- attr_reader :client
51
-
52
- # Public: Create (add) a user.
53
- #
54
- # product - The Symbol product name you are adding the user to, one of
55
- # Plaid::PRODUCTS (e.g. :info, :connect, etc.).
56
- # institution - The String/Symbol financial institution type that you
57
- # want to access (e.g. :wells).
58
- # username - The String username associated with the financial
59
- # institution.
60
- # password - The String password associated with the financial
61
- # institution.
62
- # pin - The String PIN number associated with the financial
63
- # institution (default: nil).
64
- # options - the Hash options (default: {}):
65
- # :list - The Boolean flag which would request the
66
- # available send methods if the institution
67
- # requires code-based MFA credential (default:
68
- # false).
69
- # :webhook - The String webhook URL. Used with :connect,
70
- # :income, and :risk products (default: nil).
71
- # :pending - The Boolean flag requesting to return
72
- # pending transactions. Used with :connect
73
- # product (default: false).
74
- # :login_only - The Boolean option valid for initial
75
- # authentication only. If set to false, the
76
- # initial request will return transaction data
77
- # based on the start_date and end_date.
78
- # :start_date - The start Date from which to return
79
- # transactions (default: 30 days ago).
80
- # :end_date - The end Date to which transactions
81
- # will be collected (default: today).
82
- # client - The Plaid::Client instance used to connect to the API
83
- # (default is to use global Plaid client - Plaid.client).
84
- #
85
- # Returns a Plaid::User instance.
86
- def self.create(product, institution, username, password,
87
- pin: nil, options: nil, client: nil)
88
- check_product product
89
-
90
- payload = { username: username, password: password,
91
- type: institution.to_s }
92
- payload[:pin] = pin if pin
93
- payload[:options] = MultiJson.dump(options) if options
94
-
95
- conn = Connector.new(product, auth: true, client: client)
96
- resp = conn.post(payload)
97
-
98
- new product, response: resp, mfa: conn.mfa?, client: client
99
- end
100
-
101
- # Public: Get User instance in case user access token is known.
102
- #
103
- # No requests are made, but the returned User instance is ready to be
104
- # used.
105
- #
106
- # product - The Symbol product name you want to use, one of
107
- # Plaid::PRODUCTS (e.g. :info, :connect, etc.).
108
- # token - The String access token for the user.
109
- # client - The Plaid::Client instance used to connect to the API
110
- # (default is to use global Plaid client - Plaid.client).
111
- #
112
- # Returns a Plaid::User instance.
113
- def self.load(product, token, client: nil)
114
- new check_product(product), access_token: token, client: client
115
- end
116
-
117
- # Public: Exchange a Link public_token for an API access_token.
118
- #
119
- # The account_id parameter is required if you wish to receive a Stripe bank
120
- # account token.
121
- #
122
- # public_token - The String Link public_token.
123
- # account_id - The String account ID.
124
- # product - The Symbol product name (default: :connect).
125
- # client - The Plaid::Client instance used to connect to the API
126
- # (default is to use global Plaid client - Plaid.client).
127
- #
128
- # Returns a new User with access token obtained from Plaid and default
129
- # product set to product. User#stripe_bank_account_token for this user
130
- # instance will contain the Stripe token.
131
- def self.exchange_token(public_token, account_id = nil,
132
- product: :connect, client: nil)
133
- check_product product
134
-
135
- payload = { public_token: public_token }
136
- payload[:account_id] = account_id if account_id
137
-
138
- response = Connector.new(:exchange_token, auth: true, client: client)
139
- .post(payload)
140
-
141
- stripe_token = account_id && response['stripe_bank_account_token']
142
- new product, response: response, client: client,
143
- stripe_token: stripe_token
144
- end
145
-
146
- # Internal: Initialize a User instance.
147
- #
148
- # product - The Symbol product name.
149
- # access_token - The String access token obtained from Plaid.
150
- # response - The Hash response body to parse.
151
- # mfa - The Boolean flag indicating that response body
152
- # contains an MFA response.
153
- # stripe_token - The String stripe bank account token.
154
- # client - The Plaid::Client instance used to connect to the API
155
- # (default is to use global Plaid client - Plaid.client).
156
- def initialize(product, access_token: nil, response: nil, mfa: nil,
157
- stripe_token: nil, client: nil)
158
- @product = product
159
- @client = client
160
- @access_token = access_token if access_token
161
- @mfa_required = mfa
162
- @stripe_bank_account_token = stripe_token
163
- @accounts = @initial_transactions = @info = @risk = @income = nil
164
-
165
- parse_response(response) if response
166
- end
167
-
168
- # Public: Find out if MFA is required based on last request.
169
- #
170
- # After calling e.g. User.create you might need to make an additional
171
- # authorization step if MFA is required by the financial institution.
172
- #
173
- # Returns true if this step is needed, a falsey value otherwise.
174
- def mfa?
175
- @mfa_required
176
- end
177
-
178
- # Public: Submit MFA information.
179
- #
180
- # info - The String with MFA information (default: nil).
181
- # send_method - The Hash with code send method information.
182
- # E.g. { type: 'phone' } or { mask: '123-...-4321' }.
183
- # Default is first available email.
184
- # options - the Hash options (default: {}):
185
- # :list - The Boolean flag which would request the
186
- # available send methods if the institution
187
- # requires code-based MFA credential (default:
188
- # false).
189
- # :webhook - The String webhook URL. Used with :connect,
190
- # :income, and :risk products (default: nil).
191
- # :pending - The Boolean flag requesting to return
192
- # pending transactions. Used with :connect
193
- # product (default: false).
194
- # :login_only - The Boolean option valid for initial
195
- # authentication only. If set to false, the
196
- # initial request will return transaction data
197
- # based on the start_date and end_date.
198
- # :start_date - The start Date from which to return
199
- # transactions (default: 30 days ago).
200
- # :end_date - The end Date to which transactions
201
- # will be collected (default: today).
202
- #
203
- # Returns true if whole MFA process is completed, false otherwise.
204
- def mfa_step(info = nil, send_method: nil, options: nil)
205
- payload = { access_token: access_token }
206
- payload[:mfa] = info if info
207
- if options || send_method
208
- options = {} unless options
209
- options[:send_method] = send_method if send_method
210
- payload[:options] = MultiJson.dump(options)
211
- end
212
- conn = Connector.new(product, :step, auth: true)
213
-
214
- # Use PATCH if we are in context of User#update.
215
- response = if @mfa_patch
216
- conn.patch(payload)
217
- else
218
- conn.post(payload)
219
- end
220
-
221
- @mfa_required = conn.mfa?
222
- parse_response(response)
223
- end
224
-
225
- # Public: Get transactions.
226
- #
227
- # Does a /connect/get call. Updates self.accounts with latest information.
228
- #
229
- # pending - the Boolean flag requesting to return pending transactions.
230
- # account_id - the String Account ID (default: nil). If this argument is
231
- # present, only transactions for given account will be
232
- # requested.
233
- # start_date - The start Date (inclusive).
234
- # end_date - The end Date (inclusive).
235
- #
236
- # Returns an Array of Transaction records.
237
- def transactions(pending: false, account_id: nil,
238
- start_date: nil, end_date: nil)
239
- options = { pending: pending }
240
- options[:account] = account_id if account_id
241
- options[:gte] = start_date.to_s if start_date
242
- options[:lte] = end_date.to_s if end_date
243
-
244
- response = Connector.new(:connect, :get, auth: true, client: client)
245
- .post(access_token: access_token,
246
- options: MultiJson.dump(options))
247
- update_accounts(response)
248
- build_objects(response['transactions'], Transaction)
249
- end
250
-
251
- # Public: Update user credentials.
252
- #
253
- # Updates the user credentials for the current product. See
254
- # User#for_product.
255
- #
256
- # username - The String username associated with the financial
257
- # institution.
258
- # password - The String password associated with the financial
259
- # institution.
260
- # pin - The String PIN number associated with the financial
261
- # institution (default: nil).
262
- #
263
- # Returns self.
264
- def update(username, password, pin = nil)
265
- payload = {
266
- access_token: access_token,
267
- username: username,
268
- password: password
269
- }
270
-
271
- payload[:pin] = pin if pin
272
-
273
- conn = Connector.new(product, auth: true, client: client)
274
- resp = conn.patch(payload)
275
-
276
- if conn.mfa?
277
- @mfa_required = true
278
- end
279
-
280
- parse_response(resp)
281
-
282
- # A note for User#mfa_step to send PATCH request too
283
- @mfa_patch = true
284
-
285
- self
286
- end
287
-
288
- # Public: Create or update the webhook for Connect.
289
- #
290
- # Does a PATCH /connect request.
291
- #
292
- # webhook - The String with webhook URL.
293
- #
294
- # Returns self.
295
- def update_webhook(webhook)
296
- raise ArgumentError, 'User#update_webhook only supported by Connect!' \
297
- unless product == :connect
298
-
299
- payload = {
300
- access_token: access_token,
301
- options: MultiJson.dump(webhook: webhook)
302
- }
303
-
304
- parse_response(Connector.new(:connect, auth: true, client: client)
305
- .patch(payload))
306
- self
307
- end
308
-
309
- # Public: Delete the user.
310
- #
311
- # Makes a delete request and freezes self to prevent further modifications
312
- # to the object.
313
- #
314
- # Returns self.
315
- def delete
316
- Connector.new(product, auth: true, client: client)
317
- .delete(access_token: access_token)
318
-
319
- freeze
320
- end
321
-
322
- # Public: Upgrade the user.
323
- #
324
- # For an existing user that has been added via any of products (:connect,
325
- # :auth, :income, :info, or :risk), you can upgrade that user to have
326
- # functionality with other products.
327
- #
328
- # Does a POST /upgrade request.
329
- #
330
- # See also User#for_product.
331
- #
332
- # product - The Symbol product name you are upgrading the user to, one of
333
- # Plaid::PRODUCTS.
334
- #
335
- # Returns another User record with the same access token, but tied to the
336
- # new product.
337
- def upgrade(product)
338
- payload = { access_token: access_token, upgrade_to: product.to_s }
339
- response = Connector.new(:upgrade, auth: true, client: client)
340
- .post(payload)
341
-
342
- User.new product, response: response, client: client
343
- end
344
-
345
- # Public: Get the current user tied to another product.
346
- #
347
- # No API request is made, just the current product is changed.
348
- #
349
- # product - The Symbol product you are selecting, one of Plaid::PRODUCTS.
350
- #
351
- # See also User#upgrade.
352
- #
353
- # Returns a new User instance.
354
- def for_product(product)
355
- User.load product, access_token, client: client
356
- end
357
-
358
- # Public: Get auth information for the user (routing numbers for accounts).
359
- #
360
- # Not only this method returns the new data, but it updates self.accounts as
361
- # well.
362
- #
363
- # The method does a POST /auth/get request.
364
- #
365
- # sync - The Boolean flag which, if true, causes auth information to be
366
- # rerequested from the server. Otherwise cached version is returned,
367
- # if it exists.
368
- #
369
- # Returns an Array of Account with numbers baked in.
370
- def auth(sync: false)
371
- if sync || !@accounts || !@accounts[0] || !@accounts[0].numbers
372
- response = Connector.new(:auth, :get, auth: true, client: client)
373
- .post(access_token: access_token)
374
-
375
- update_accounts(response)
376
- end
377
-
378
- accounts
379
- end
380
-
381
- # Public: Get info for the user.
382
- #
383
- # Does a POST /info/get request.
384
- #
385
- # sync - The Boolean flag which, if true, causes information to be
386
- # rerequested from the server. Otherwise cached version is returned,
387
- # if it exists.
388
- #
389
- # Returns a Plaid::Info instance.
390
- def info(sync: false)
391
- if sync || !@info
392
- parse_response(Connector.new(:info, :get, auth: true, client: client)
393
- .post(access_token: access_token))
394
- end
395
-
396
- @info
397
- end
398
-
399
- # Public: Get income information for the user.
400
- #
401
- # Does a POST /income/get request.
402
- #
403
- # sync - The Boolean flag which, if true, causes income information to be
404
- # rerequested from the server. Otherwise cached version is returned,
405
- # if it exists.
406
- #
407
- # Returns a Plaid::Income instance.
408
- def income(sync: false)
409
- if sync || !@income
410
- parse_response(Connector.new(:income, :get, auth: true, client: client)
411
- .post(access_token: access_token))
412
- end
413
-
414
- @income
415
- end
416
-
417
- # Public: Get risk data for the user's accounts.
418
- #
419
- # Does a POST /risk/get request.
420
- #
421
- # sync - The Boolean flag which, if true, causes risk information to be
422
- # rerequested from the server. Otherwise cached version is returned,
423
- # if it exists.
424
- #
425
- # Returns an Array of accounts with risk attribute set.
426
- def risk(sync: false)
427
- if sync || !@accounts || !@accounts[0] || !@accounts[0].risk
428
- parse_response(Connector.new(:risk, :get, auth: true, client: client)
429
- .post(access_token: access_token))
430
- end
431
-
432
- @accounts
433
- end
434
-
435
- # Public: Get current account balance.
436
- #
437
- # Does a POST /balance request.
438
- #
439
- # Returns an Array of Plaid::Account.
440
- def balance
441
- response = Connector.new(:balance, auth: true, client: client)
442
- .post(access_token: access_token)
443
-
444
- update_accounts(response)
445
- end
446
-
447
- private
448
-
449
- # Internal: Validate the product name.
450
- def self.check_product(product)
451
- if Plaid::PRODUCTS.include?(product)
452
- product
453
- else
454
- raise ArgumentError, "product (#{product.inspect}) must be one of " \
455
- "Plaid products (#{Plaid::PRODUCTS.inspect})"
456
- end
457
- end
458
-
459
- private_class_method :check_product
460
-
461
- # Internal: Set up attributes from Add User response.
462
- def parse_response(response)
463
- @access_token = response['access_token']
464
- @processor_token = response['processor_token']
465
-
466
- return parse_mfa_response(response) if mfa?
467
-
468
- @mfa_type = @mfa = nil
469
-
470
- update_accounts(response) if response['accounts']
471
-
472
- if (trans = response['transactions'])
473
- @initial_transactions = build_objects(trans, Transaction)
474
- end
475
-
476
- if (income = response['income'])
477
- @income = Plaid::Income.new(income)
478
- end
479
-
480
- return unless (i = response['info'])
481
- @info = Plaid::Info.new(i)
482
- end
483
-
484
- # Internal: Parse an MFA response
485
- def parse_mfa_response(response)
486
- @mfa_type = response['type'].to_sym
487
- @mfa = Plaid.symbolize_hash(response['mfa'])
488
- end
489
-
490
- # Internal: Convert an array of data into an array of objects, encapsulating
491
- # that data.
492
- def build_objects(data, klass)
493
- data ? data.map { |element| klass.new(element) } : []
494
- end
495
-
496
- # Internal: Update account data from the response.
497
- def update_accounts(response)
498
- new_accounts = build_objects(response['accounts'], Account)
499
-
500
- if @accounts
501
- Account.merge @accounts, new_accounts
502
- else
503
- @accounts = new_accounts
504
- end
505
- end
506
- end
507
- end