plaid 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b5ab6d107eb42bdc74441ce2e47c068b43b66c0
4
- data.tar.gz: fd69213bbb1001c8b283edb6742da94647ae9a53
3
+ metadata.gz: 1f1feea43dc48ce5bad450da5642293cf9a26637
4
+ data.tar.gz: 8e355726ca0db3969979088c505889b4f29d147f
5
5
  SHA512:
6
- metadata.gz: 8fe17b5426f28e1657f38541b2434c7462f60205d9073b778761a516587e8e809ef582720e5ccad3c08e5cb3f7f2f67a5aad08a0f402c99da0536d8d3129c2b8
7
- data.tar.gz: 46bc2fe7a2f1da6895e15b4a9c265613f8da13c8966b75c9e069bbe55185a95dbfed3a15f8445a7929aab6d9e5867afe0aeef11ee42a3d1fd9ddd692499c3e09
6
+ metadata.gz: 6bcaccd07ed115c5629bd65ba34529aa3cb6264afc575bc9a8adf1859f9e217ac2544c74770283ccd8714edc26e3a167c957422b9d264486a99e3f80b986e2b5
7
+ data.tar.gz: 7a895e92f13d9ea624f0990834ecedab18613ffb946799d03c7c1972d3fb44ad948ec286d90d0fecb6cdf14627d3414fec1147039838d5c921f6c2168dc644b5
@@ -0,0 +1,3 @@
1
+ PLAID_RUBY_CLIENT_ID=the_real_client_id
2
+ PLAID_RUBY_SECRET=the_real_secret
3
+ PLAID_RUBY_PUBLIC_KEY=the_real_public_key
@@ -0,0 +1,44 @@
1
+ # 4.1.0 04-Jan-2018
2
+
3
+ * Make `/item/remove` the primary Item removal endpoint
4
+ * Add #options parameter to `/institutions/get`
5
+ * Handle network errors with `PlaidServerError`
6
+
7
+ # 4.0.0 09-Mar-2017
8
+
9
+ * Refactored the entire library to support [Plaid's new API](https://blog.plaid.com/improving-our-api/). Use the [transition guide](https://plaid.com/docs/link/transition-guide) to update your integration. Version 3.x.x of this gem is mirrored at [plaid-legacy](https://github.com/plaid/plaid-ruby-legacy).
10
+
11
+ # 3.0.0. 17-Jan-2017
12
+
13
+ * Add `/institutions/all` and `/institutions/all/search` endpoints, see [UPGRADING.md](UPGRADING.md#upgrading-from-2xx-to-300)
14
+
15
+ # 2.2.0. 03-Nov-2016
16
+
17
+ * Add `Transaction#reference_number` (@ericbirdsall).
18
+ * Fix webhook codes and add risk and income webhooks.
19
+
20
+ # 2.1.0. 19-Oct-2016
21
+
22
+ * Documentation fixes (@ishmael).
23
+ * Fix `Transaction#to_s` behavior (@michel-tricot).
24
+ * PATCH `/:product/step` flow.
25
+ * Use the same client in `User#upgrade` (@betesh).
26
+ * Webhook object (@zshannon).
27
+ * `processor_token` access in `User.exchange_token` (@gylaz).
28
+ * Raise `ServerError` in case server returned an empty response body.
29
+
30
+ # 2.0.0. 24-May-2016
31
+
32
+ * Use `~> 1.0` spec for multi_json dependency.
33
+ * Support `stripe_bank_account_token` in `User.exchange_token`.
34
+
35
+ # 2.0.0.alpha.2. 14-May-2016
36
+
37
+ * Use `:production` instead of `:api` to signify production environment
38
+ in `Plaid::Client#env=`.
39
+ * `User#mfa_step` allows to specify options now (thanks @gcweeks).
40
+ * Implemented `User#update_webhook`.
41
+
42
+ # 2.0.0.alpha. 06-May-2016
43
+
44
+ * Rewrite everything.
@@ -4,6 +4,46 @@ After checking out the repo, run `bin/setup` to install dependencies. You can al
4
4
 
5
5
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
6
6
 
7
+ ## Running tests
8
+
9
+ The gem test suite can be run in two modes. By default, it runs against the
10
+ live sandbox environment, creating items on the fly and calling various API
11
+ endpoints for them. For this to work you'll need real `client_id`, `secret`,
12
+ and `public_key` from your Plaid dashboard. Create a file named `.env`
13
+ based on `.env.sample` which is provided:
14
+
15
+ ```text
16
+ PLAID_RUBY_CLIENT_ID=the_real_client_id
17
+ PLAID_RUBY_SECRET=the_real_secret
18
+ PLAID_RUBY_PUBLIC_KEY=the_real_public_key
19
+ ```
20
+ This file will be loaded during the tests.
21
+
22
+ Another mode employs pre-recorded API responses using the
23
+ [vcr](https://github.com/vcr/vcr) gem. It runs much faster. Just use
24
+ `rake test_stubbed` and you're good to go even without `.env`!
25
+
26
+ ## Updating VCR "cassettes"
27
+
28
+ In case you're adding new API endpoints or when there were any substantial
29
+ changes in API you'll need to update the pre-recorded responses. Here's how:
30
+
31
+ 1. Make sure that `STUB_API=1 rake test` fails. It will fail saying something
32
+ like "... An HTTP request has been made that VCR does not know how to
33
+ handle".
34
+ 2. Run `RECORD_MODE=all STUB_API=1 rake test`. This will run the whole suite
35
+ and re-record everything. If you only need to update data for one test class,
36
+ use this:
37
+
38
+ ```
39
+ RECORD_MODE=all STUB_API=1 ruby -w -I"lib:test" -rminitest/pride test/test_which_fails.rb
40
+ ```
41
+ 3. Run `rake vcr_hide_credentials`. This step is essential, because
42
+ newly recorded files will contain your real `client_id` and friends. This
43
+ Rake task will go over all recorded files and replace real values with
44
+ stubbed ones used by `STUB_API=1 rake test`.
45
+ 4. Run `STUB_API=1 rake test` and verify that everything works.
46
+
7
47
  ## Contributing
8
48
 
9
49
  1. Make one or more atomic commits, and ensure that each commit has a
@@ -14,8 +54,8 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
14
54
 
15
55
  3. Make sure that you've documented all public methods using [TomDoc](http://tomdoc.org/).
16
56
 
17
- 4. Run `rake test`, and address any errors. Preferably, fix commits
18
- in place using `git rebase` or `git commit --amend` to make the
57
+ 4. Run tests (in both modes, see above) and address any errors. Preferably,
58
+ fix commits in place using `git rebase` or `git commit --amend` to make the
19
59
  changes easier to review.
20
60
 
21
61
  5. Open a pull request.
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 TODO: Write your name
3
+ Copyright (c) 2017 Plaid Technologies, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,14 @@
1
+ # Publishing
2
+
3
+ The module is published to [RubyGems][1] under the gem name [plaid][2].
4
+
5
+ To publish:
6
+
7
+ 1. `rake test` (runs the test suite locally).
8
+ 2. Update, commit, and merge (with review) the `lib/plaid/version.rb` and `CHANGELOG.md` files.
9
+ 5. `git pull` (makes sure your `HEAD` is up-to-date).
10
+ 6. `rake release` (builds the gem, creates a tag, pushes the gem to RubyGems and tag to GitHub).
11
+ 7. `rake update_github_docs` (generates RDoc files, updates `gh-pages` branch and pushes it to GitHub).
12
+
13
+ [1]: https://rubygems.org/
14
+ [2]: https://rubygems.org/gems/plaid
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # plaid-ruby [![Build Status](https://travis-ci.org/plaid/plaid-ruby.svg)](https://travis-ci.org/plaid/plaid-ruby) [![Gem Version](https://badge.fury.io/rb/plaid.svg)](http://badge.fury.io/rb/plaid)
1
+ # plaid-ruby [![Circle CI](https://circleci.com/gh/plaid/plaid-ruby.svg?style=svg&circle-token=30ee002ac2021da5b5b5a701d45fe2888af124a5)](https://circleci.com/gh/plaid/plaid-ruby) [![Gem Version](https://badge.fury.io/rb/plaid.svg)](http://badge.fury.io/rb/plaid)
2
2
 
3
3
  The official Ruby bindings for the [Plaid API](https://plaid.com/docs).
4
4
 
5
- This module was recently released as version `4.0.x` for Plaid's updated API. Use the gem `plaid-legacy` for version `3.0.x`.
5
+ **Note:** This module was recently refactored and released as version `4.0.x` to support [Plaid's updated API][1]. The previous module version, `3.0.x`, and API legacy documentation, is still available via RubyGems and mirrored as [`plaid-legacy`][2].
6
6
 
7
7
  ## Installation
8
8
 
@@ -26,7 +26,7 @@ The gem supports Ruby 2.1+ only.
26
26
 
27
27
  This gem wraps the Plaid API, which is fully described in the [documentation](https://plaid.com/docs/api).
28
28
 
29
- The RubyDoc for the gem is available [here](LINKPLACEHOLDER).
29
+ The RubyDoc for the gem is available [here](http://plaid.github.io/plaid-ruby/).
30
30
 
31
31
  ### Creating a Plaid client
32
32
 
@@ -43,87 +43,13 @@ $client = Plaid::Client.new(env: :sandbox,
43
43
  ```
44
44
 
45
45
  The `env` field is the environment which the client will be running in. Your choices for the `env` field include:
46
- - `:sandbox` allows you to do your initial integrations tests against preloaded data without being billed or making expensive API calls. More information about using the API sandbox can be found on the [API Sandbox documentation](https://plaid.com/docs/api/blob/master/SANDBOX.md).
46
+ - `:sandbox` allows you to do your initial integrations tests against preloaded data without being billed or making expensive API calls. More information about using the API sandbox can be found on the [API Sandbox documentation](https://plaid.com/docs/api#sandbox).
47
47
  - `:development` allows you to test against both real and test accounts without being billed. More information about Plaid test accounts can be found in our [API documentation](https://plaid.com/docs/api/#sandbox).
48
48
  - `:production` is the production environment where you can launch your production ready application and be charged for your Plaid usage.
49
49
 
50
- ### Creating a new item
51
-
52
- A new item can be created by providing a set of credentials, an institution code, and a list of products to create the item with.
53
-
54
- ```ruby
55
- item = client.item.create(credentials: { username: 'user_good',
56
- password: 'pass_good',
57
- pin: '1234' },
58
- institution_id: 'ins_109509',
59
- initial_products: %i(auth identity transactions))
60
- ```
61
- The first argument for `client.item.create` is always the credentials in the form of a hash.
62
- ```ruby
63
- credentials = { username: 'user_good',
64
- password: 'pass_good',
65
- pin: '1234' }
66
- ```
67
- The `pin` field in the credentials hash is not required and does not need to be entered if your institution does not require it.
68
-
69
- A successful, non-MFA (multi-factor authentication) response (HTTP 200) to an item creation will come in the form of a hash where each of these attributes can be accessed by the corresponding string key:
70
- ```ruby
71
- { "access_token" => String,
72
- "item" => { "available_products" => [String],
73
- "billed_products" => [String],
74
- "error" => Object,
75
- "institution_id" => String,
76
- "item_id" => String,
77
- "webhook" => nullable String },
78
- "request_id" => String }
79
- ```
80
- The response provides three primary pieces of information:
81
- - `access_token` is your token to access this item and the item's products in the future such as `auth` or `transactions`
82
- - `item` provides information about the item such as the `item_id` and `available_products`
83
- - `request_id` is the identifier for your actual request, this is often used to file tickets if necessary
84
-
85
- There are other responses you can receive from an item creation such as an MFA response and an error response (they're all in hash form).
86
-
87
- An MFA response (HTTP 210) looks like this:
88
- ```ruby
89
- { "access_token" => String,
90
- "device" => nullable String,
91
- "device_list" => nullable [Object],
92
- "mfa_type" => String Enum (device, device_list, questions, selections),
93
- "questions" => nullable [String],
94
- "request_id" => String,
95
- "selections" => nullable [Object] }
96
- ```
97
-
98
- An example of how an MFA onboarding flow looks like can be seen below in the Examples section.
99
-
100
- If an error occurs during the creation, an error will be thrown. The class for the error has values that you can access by using the following keys.
101
- ```ruby
102
- { "error_type" => String,
103
- "error_code" => String,
104
- "error_message" => String,
105
- "display_message" => (nullable) String,
106
- "request_id" => String }
107
- ```
108
- Additional information on the meaning or usage of each field can be found in our [API Error documentation](https://plaid.com/docs/api#errors).
109
-
110
- You can also add options such as `transactions.await_results` or `webhook` to your item creation, use keyed arguments:
111
-
112
- ```ruby
113
- item = client.item.create(credentials: { username: 'user_good',
114
- password: 'pass_good' },
115
- institution_id: 'ins_109509',
116
- initial_products: %i(auth identity transactions),
117
- transactions_await_results: true,
118
- webhook: 'https://plaid.com/webhook-test')
119
- ```
120
-
121
- More information about item creation options can be found in our [API documentation](https://plaid.com/docs/api#post-itemcreate).
122
-
123
-
124
50
  ## Examples
125
51
 
126
- ### Exchanging a Link public token for a Plaid access_token
52
+ ### Exchanging a Link public_token for a Plaid access_token
127
53
 
128
54
  If you have a [Link](https://github.com/plaid/link) `public token`, use this function to get an `access_token`: `client.item.public_token.exchange(public_token)`
129
55
 
@@ -134,75 +60,6 @@ response = client.item.public_token.exchange(public_token)
134
60
  access_token = response['access_token']
135
61
  ```
136
62
 
137
- ### Handle MFA during item creation
138
-
139
- If MFA is requested by the financial institution, there will be additional steps required to finish the item creation flow:
140
- ```ruby
141
- require 'plaid'
142
-
143
- def answer_mfa(client, access_token, data)
144
- case data['mfa_type']
145
- when 'questions'
146
- answer_questions(client, access_token, data['questions'])
147
- when 'device_list'
148
- answer_device_list(client, access_token, data['device_list'])
149
- when 'selections'
150
- answer_selections(client, access_token, data['selections'])
151
- when 'device'
152
- answer_device(client, access_token, data['device'])
153
- else
154
- raise 'Unknown MFA type from Plaid'
155
- end
156
- end
157
-
158
- def answer_questions(client, access_token, _questions)
159
- # We have magically inferred the answer here, so we respond immediately.
160
- # In the real world, we would present the questions to our user and
161
- # submit their responses.
162
- answers = ['answer_0_0']
163
- client.item.mfa(access_token, 'questions', answers)
164
- end
165
-
166
- def answer_device_list(client, access_token, device_list)
167
- # We have picked the first device here.
168
- # In the real world, we would ask our user which device the passcode
169
- # should be sent to.
170
- device = device_list[0]['device_id']
171
- client.item.mfa(access_token, 'device_list', [device])
172
- end
173
-
174
- def answer_device(client, access_token, _device)
175
- # Another magically inferred answer.
176
- # In the real world, we would ask our user for the passcode they received.
177
- client.item.mfa(access_token, 'device', ['1234'])
178
- end
179
-
180
- def answer_selections(client, access_token, _selections)
181
- # We have magically inferred the answer here, so we respond immediately.
182
- # In the real world, we would present the selection question and choices
183
- # to our user and submit their responses.
184
- answers = %w(tomato ketchup)
185
- client.item.mfa(access_token, 'selections', answers)
186
- end
187
-
188
- begin
189
- client = Plaid::Client.new(env: :sandbox,
190
- client_id: '***',
191
- secret: '***',
192
- public_key: '***')
193
-
194
- response = client.item.create(credentials: { username: 'user_good',
195
- password: 'mfa_device' },
196
- institution_id: 'ins_109508',
197
- initial_products: %i(transactions auth))
198
-
199
- access_token = response['access_token']
200
- response = answer_mfa(client, access_token, response) while response.key?('mfa_type')
201
- rescue Plaid::PlaidError
202
- raise 'Error in main flow.'
203
- end
204
- ```
205
-
206
63
  ### Deleting an item
207
64
 
208
65
  ```ruby
@@ -220,8 +77,8 @@ response = client.item.create(credentials: { username: 'user_good',
220
77
 
221
78
  access_token = response['access_token']
222
79
 
223
- # Provide the access_token for the Item you want to delete
224
- client.item.delete(access_token)
80
+ # Provide the access_token for the Item you want to remove
81
+ client.item.remove(access_token)
225
82
  ```
226
83
 
227
84
  ### Get paginated transactions
@@ -245,7 +102,7 @@ transactions = transaction_response['transactions']
245
102
 
246
103
  # the transactions in the response are paginated, so make multiple calls while
247
104
  # increasing the offset to retrieve all transactions
248
- while transactions.length < response['total_transactions']
105
+ while transactions.length < transaction_response['total_transactions']
249
106
  transaction_response = client.transactions.get(access_token,
250
107
  '2016-07-12',
251
108
  '2017-01-09',
@@ -255,7 +112,7 @@ end
255
112
 
256
113
  ```
257
114
 
258
- ### Obtaining user-related data
115
+ ### Obtaining Item-related data
259
116
 
260
117
  If you have an `access_token`, you can use following code to retreive data:
261
118
  ```ruby
@@ -353,3 +210,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/plaid/
353
210
  ## License
354
211
 
355
212
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
213
+
214
+ [1]: https://blog.plaid.com/improving-our-api/
215
+ [2]: https://github.com/plaid/plaid-ruby-legacy
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler/gem_tasks'
2
2
  require 'sdoc'
3
3
  require 'rdoc/task'
4
4
  require 'rake/testtask'
5
+ require 'dotenv/load'
5
6
  require 'fileutils'
6
7
 
7
8
  RDoc::Task.new do |rdoc|
@@ -9,7 +10,7 @@ RDoc::Task.new do |rdoc|
9
10
  rdoc.generator = 'sdoc'
10
11
  rdoc.main = 'README.md'
11
12
 
12
- rdoc.rdoc_files.include('README.md', 'LICENSE', 'UPGRADING.md', 'lib/**/*.rb')
13
+ rdoc.rdoc_files.include('README.md', 'LICENSE.txt', 'lib/**/*.rb')
13
14
  rdoc.markup = 'tomdoc'
14
15
  end
15
16
 
@@ -50,4 +51,35 @@ task update_github_docs: %i(rdoc update_gh_pages) do
50
51
  sh 'git push origin gh-pages'
51
52
  end
52
53
 
53
- task default: :test
54
+ desc 'Hide real credentials in VCR cassettes'
55
+ task :vcr_hide_credentials do
56
+
57
+ all_creds = %w(PLAID_RUBY_CLIENT_ID PLAID_RUBY_SECRET PLAID_RUBY_PUBLIC_KEY)
58
+
59
+ all_creds.each do |cred|
60
+ fail "#{cred} is not set" unless ENV[cred]
61
+ end
62
+
63
+ Dir['test/vcr_cassettes/*'].each do |fn|
64
+ data = File.read(fn)
65
+ data_0 = data.clone
66
+
67
+ all_creds.each do |cred|
68
+ data.gsub! ENV[cred], cred
69
+ end
70
+
71
+ if data != data_0
72
+ File.open(fn, 'w') { |f| f.write data }
73
+
74
+ puts ">> Updated #{fn}"
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ task :test_stubbed do
81
+ ENV['STUB_API'] ||= '1'
82
+ Rake::Task['test'].invoke
83
+ end
84
+
85
+ task default: :test_stubbed
@@ -0,0 +1,64 @@
1
+ ## Upgrading from 3.x.x to 4.0.0
2
+
3
+ Version 4.0.0 supports [Plaid's new API](https://blog.plaid.com/improving-our-api/). Use the [transition guide](https://plaid.com/docs/link/transition-guide) to update your integration.
4
+
5
+ ## Upgrading from 2.x.x to 3.0.0
6
+
7
+ Version 3.0.0 makes `Plaid::Institution` use new `institutions/all` endpoint
8
+ of Plaid API which unites "native" and "long tail" institutions.
9
+ `Plaid::LongTailInstitution` class is removed, its functionality is
10
+ concentrated in `Plaid::Institution`.
11
+
12
+ Use `Plaid::Institution.all` instead of `Plaid::LongTailInstitution.all` (the
13
+ semantics is the same, with added products param).
14
+
15
+ Use `Plaid::Institution.search` instead of `Plaid::LongTailInstitution.search`.
16
+
17
+ Use `Plaid::Institution.search_by_id` instead of `Plaid::LongTailInstitution.get`.
18
+
19
+
20
+ ## Upgrading from 1.x to 2.0.0
21
+
22
+ Make sure you use Ruby 2.0 or higher.
23
+
24
+ Update the `Plaid.config` block:
25
+
26
+ ```ruby
27
+ Plaid.config do |p|
28
+ p.client_id = '<<< Plaid provided client ID >>>' # WAS: customer_id
29
+ p.secret = '<<< Plaid provided secret key >>>' # No change
30
+ p.env = :tartan # or :api for production # WAS: environment_location
31
+ end
32
+ ```
33
+
34
+ Use `Plaid::User.create` instead of `Plaid.add_user` (**NOTE**: parameter order has changed!)
35
+
36
+ Use `Plaid::User.load` instead of `Plaid.set_user` (**NOTE**: parameter order has changed!)
37
+
38
+ Use `Plaid::User.exchange_token` instead of `Plaid.exchange_token` (**NOTE**: parameter list has changed!)
39
+
40
+ Use `Plaid::User.create` or (`.load`) and `Plaid::User#transactions` instead of `Plaid.transactions`.
41
+
42
+ Use `Plaid::Institution.all` and `Plaid::Institution.get` instead of `Plaid.institution`.
43
+
44
+ Use `Plaid::Category.all` and `Plaid::Category.get` instead of `Plaid.category`.
45
+
46
+ `Plaid::Account#institution_type` was renamed to `Plaid::Account#institution`.
47
+
48
+ `Plaid::Transaction#account` was renamed to `Plaid::Transaction#account_id`.
49
+
50
+ `Plaid::Transaction#date` is a Date, not a String object now.
51
+
52
+ `Plaid::Transaction#cat` was removed. Use `Plaid::Transaction#category_hierarchy` and `Plaid::Transaction#category_id` directly.
53
+
54
+ `Plaid::Transaction#category` was renamed to `Plaid::Transaction#category_hierarchy`.
55
+
56
+ `Plaid::Transaction#pending_transaction` was renamed to `Plaid::Transaction#pending_transaction_id`.
57
+
58
+ Use `Plaid::User#mfa_step` instead of `Plaid::User#select_mfa_method` and `Plaid::User#mfa_authentication`.
59
+
60
+ `Plaid::User#permit?` was removed. You don't need this.
61
+
62
+ `Plaid::User.delete_user` was renamed to `Plaid::User.delete`.
63
+
64
+ **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/circle.yml CHANGED
@@ -1,3 +1,9 @@
1
1
  machine:
2
2
  ruby:
3
- version: 2.2.2
3
+ version: 2.3.4
4
+
5
+ test:
6
+ pre:
7
+ - bundle exec rake test:
8
+ environment:
9
+ STUB_API: yes
@@ -1,5 +1,8 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ require_relative 'plaid/middleware'
1
5
  require_relative 'plaid/client'
2
- require_relative 'plaid/connect'
3
6
  require_relative 'plaid/errors'
4
7
  require_relative 'plaid/version'
5
8
  require_relative 'plaid/products/accounts'
@@ -13,7 +16,3 @@ require_relative 'plaid/products/item'
13
16
  require_relative 'plaid/products/processor'
14
17
  require_relative 'plaid/products/sandbox'
15
18
  require_relative 'plaid/products/transactions'
16
-
17
- # Public: The Plaid namespace.
18
- module Plaid
19
- end
@@ -1,36 +1,29 @@
1
1
  # Public: The Plaid namespace.
2
2
  module Plaid
3
- # Internal: Map environment to Plaid environment URL
4
- #
5
- # env - The type of the environment in symbol form
6
- #
7
- # Returns a string representing an environment URL
8
- def self.url_from_env(env)
9
- case env
10
- when :sandbox
11
- 'https://sandbox.plaid.com/'
12
- when :development
13
- 'https://development.plaid.com/'
14
- when :production
15
- 'https://production.plaid.com/'
16
- end
17
- end
18
-
19
- # Public: A class encapsulating client_id, secret, public key, and Plaid environment.
3
+ # Public: The main interface to Plaid API.
20
4
  class Client
21
- # Internal: Set Plaid environment to use
22
- #
23
- # Handles converting an env symbol into an environment URL
5
+ # Public: All possible environments for the client to use.
6
+ ENVIRONMENTS = %i(sandbox development production)
7
+
8
+ # Public: The current environment in use (one of ENVIRONMENTS).
9
+ attr_reader :env
10
+
11
+ # Public: Construct a Client instance
24
12
  #
25
- # env - The Symbol (:sandbox, :development, :production)
13
+ # Optionally takes a block to allow overriding the default Faraday connection options.
26
14
  #
27
- # Returns a string representing the environment URL or raises an error
28
- def env_map(env)
29
- (@env = Plaid.url_from_env(env)) || raise(ArgumentError, 'Invalid value for ' \
30
- 'Plaid::Client.env ' \
31
- "(#{env.inspect}): " \
32
- 'must be :sandbox, '\
33
- ':development, or :production ')
15
+ # env - The Symbol (:sandbox, :development, :production)
16
+ # client_id - The String Plaid account client ID to authenticate requests
17
+ # secret - The String Plaid account secret to authenticate requests
18
+ # public_key - The String Plaid account public key to authenticate requests
19
+ def initialize(env:, client_id:, secret:, public_key:, &block)
20
+ @env = env.to_sym
21
+ @api_host = api_host
22
+ @client_id = client_id
23
+ @secret = secret
24
+ @public_key = public_key
25
+
26
+ create_connection(&block)
34
27
  end
35
28
 
36
29
  # Public: Memoized class instance to make requests from Plaid::Account
@@ -88,19 +81,6 @@ module Plaid
88
81
  @transactions ||= Plaid::Transactions.new(self)
89
82
  end
90
83
 
91
- # Public: Construct a Client instance
92
- #
93
- # env - The Symbol (:sandbox, :development, :production)
94
- # client_id - The String Plaid account client ID to authenticate requests
95
- # secret - The String Plaid account secret to authenticate requests
96
- # public_key - The String Plaid account public key to authenticate requests
97
- def initialize(env:, client_id:, secret:, public_key:)
98
- @env = env_map(env)
99
- @client_id = client_id
100
- @secret = secret
101
- @public_key = public_key
102
- end
103
-
104
84
  # Public: Make a post request
105
85
  #
106
86
  # path - Path or URL to make the request to
@@ -108,7 +88,7 @@ module Plaid
108
88
  #
109
89
  # Returns the resulting parsed JSON of the request
110
90
  def post(path, payload)
111
- Plaid::Connect.post(File.join(@env, path), payload)
91
+ @connection.post(path, payload).body
112
92
  end
113
93
 
114
94
  # Public: Make a post request with appended authentication fields
@@ -118,9 +98,9 @@ module Plaid
118
98
  #
119
99
  # Returns the resulting parsed JSON of the request
120
100
  def post_with_auth(path, payload)
121
- auth = { client_id: @client_id,
122
- secret: @secret }
123
- Plaid::Connect.post(File.join(@env, path), payload.merge(auth))
101
+ @connection.post(
102
+ path,
103
+ payload.merge(client_id: @client_id, secret: @secret)).body
124
104
  end
125
105
 
126
106
  # Public: Make a post request with appended public key field.
@@ -130,8 +110,46 @@ module Plaid
130
110
  #
131
111
  # Returns the resulting parsed JSON of the request.
132
112
  def post_with_public_key(path, payload)
133
- public_key = { public_key: @public_key }
134
- Plaid::Connect.post(File.join(@env, path), payload.merge(public_key))
113
+ @connection.post(path, payload.merge(public_key: @public_key)).body
114
+ end
115
+
116
+ protected
117
+
118
+ # Internal: Gets the API hostname for given environment.
119
+ #
120
+ # env - The Symbol (:sandbox, :development, :production)
121
+ #
122
+ # Returns a String representing the environment URL.
123
+ # Raises ArgumentError if environment is unknown.
124
+ def api_host
125
+ unless ENVIRONMENTS.include?(@env)
126
+ raise ArgumentError,
127
+ "Invalid value for env (#{@env.inspect}): must be one of " +
128
+ ENVIRONMENTS.map(&:inspect) * ', '
129
+ end
130
+
131
+ "https://#{@env}.plaid.com"
132
+ end
133
+
134
+ # Internal: Initializes a new Plaid connection object via Faraday.
135
+ #
136
+ # Optionally takes a block to allow overriding the defaults.
137
+ def create_connection(&block)
138
+ @connection = Faraday.new(url: @api_host) do |builder|
139
+ block_given? ? yield(builder) : build_default_connection(builder)
140
+ end
141
+ end
142
+
143
+ # Internal: Set Plaid defaults on the Faraday connection.
144
+ #
145
+ # builder - The Faraday builder object.
146
+ def build_default_connection(builder)
147
+ builder.options[:timeout] = Plaid::Middleware::NETWORK_TIMEOUT
148
+ builder.headers = Plaid::Middleware::NETWORK_HEADERS
149
+ builder.request :json
150
+ builder.use Plaid::Middleware
151
+ builder.response :json, content_type: /\bjson$/
152
+ builder.adapter Faraday.default_adapter
135
153
  end
136
154
  end
137
155
  end
@@ -1,6 +1,12 @@
1
1
  module Plaid
2
- # Internal: Base class for Plaid errors
3
- class PlaidError < StandardError
2
+ # Public: Base class for Plaid SDK errors
3
+ class PlaidError < StandardError; end
4
+
5
+ # Public: returned on Plaid server or network issues
6
+ class PlaidServerError < PlaidError; end
7
+
8
+ # Public: Base class for any error returned by the API
9
+ class PlaidAPIError < PlaidError
4
10
  attr_reader :error_type, :error_code, :error_message, :display_message, :request_id
5
11
 
6
12
  # Internal: Initialize an error with proper attributes
@@ -27,28 +33,28 @@ module Plaid
27
33
  end
28
34
 
29
35
  # Public: returned when the request is malformed and cannot be processed.
30
- class InvalidRequestError < PlaidError; end
36
+ class InvalidRequestError < PlaidAPIError; end
31
37
 
32
38
  # Public: returned when all fields are provided and are in the correct format,
33
39
  # but the values provided are incorrect in some way.
34
- class InvalidInputError < PlaidError; end
40
+ class InvalidInputError < PlaidAPIError; end
35
41
 
36
42
  # Public: returned when the request is valid but has exceeded established rate limits.
37
- class RateLimitExceededError < PlaidError; end
43
+ class RateLimitExceededError < PlaidAPIError; end
38
44
 
39
45
  # Public: returned during planned maintenance windows and
40
46
  # in response to API internal server errors.
41
- class APIError < PlaidError; end
47
+ class APIError < PlaidAPIError; end
42
48
 
43
49
  # Public: indicates that information provided for the item (such as credentials or MFA)
44
50
  # may be invalid or that the item is not supported on Plaid's platform.
45
- class ItemError < PlaidError; end
51
+ class ItemError < PlaidAPIError; end
46
52
 
47
53
  # Internal: A module that provides utilities for errors.
48
54
  module Error
49
- # Internal: Map error_type to PlaidError
55
+ # Internal: Map error_type to PlaidAPIError
50
56
  #
51
- # Maps an error_type from an error HTTP response to an actual PlaidError class instance
57
+ # Maps an error_type from an error HTTP response to an actual PlaidAPIError class instance
52
58
  #
53
59
  # error_type - The type of the error as indicated by the error response body
54
60
  #
@@ -66,7 +72,7 @@ module Plaid
66
72
  when 'ITEM_ERROR'
67
73
  Plaid::ItemError
68
74
  else
69
- Plaid::PlaidError
75
+ Plaid::PlaidAPIError
70
76
  end
71
77
  end
72
78
  end
@@ -0,0 +1,25 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ require_relative 'version'
6
+
7
+ module Plaid
8
+ class Middleware < ::Faraday::Response::Middleware
9
+ # Internal: Headers used for correct request and SDK tracking.
10
+ NETWORK_HEADERS = { 'User-Agent' => "Plaid Ruby v#{Plaid::VERSION}",
11
+ 'Content-Type' => 'application/json' }.freeze
12
+
13
+ # Internal: Default read timeout for HTTP calls in seconds.
14
+ NETWORK_TIMEOUT = 600
15
+
16
+ def on_complete(env)
17
+ return unless Faraday::Response::RaiseError::ClientErrorStatuses.include?(env[:status])
18
+ raise Plaid::Error.error_from_type(env.body['error_type']).new(env.body['error_type'],
19
+ env.body['error_code'],
20
+ env.body['error_message'],
21
+ env.body['display_message'],
22
+ env.body['request_id'])
23
+ end
24
+ end
25
+ end
@@ -11,10 +11,17 @@ module Plaid
11
11
  # and access_token's item
12
12
  #
13
13
  # access_token - access_token who's item to fetch credit_details for
14
+ # account_ids - Specific account ids to fetch credit_details for (optional)
14
15
  #
15
16
  # Returns a parsed JSON of credit_details information
16
- def get(access_token)
17
- payload = { access_token: access_token }
17
+ def get(access_token, account_ids: nil)
18
+ options_payload = {}
19
+ options_payload[:account_ids] = account_ids unless account_ids.nil?
20
+ payload = {
21
+ access_token: access_token,
22
+ options: options_payload
23
+ }
24
+
18
25
  @client.post_with_auth('credit_details/get', payload)
19
26
  end
20
27
  end
@@ -10,13 +10,16 @@ module Plaid
10
10
  # Does a POST /institutions/get call pulls a list of supported Plaid institutions
11
11
  # with the information for each institution
12
12
  #
13
- # count - Amount of institutions to pull
14
- # offset - Offset to start pulling institutions
13
+ # count - Amount of institutions to pull
14
+ # offset - Offset to start pulling institutions
15
+ # options - Options for filtering institutions
15
16
  #
16
17
  # Returns a parsed JSON of listed institution information
17
- def get(count:, offset:)
18
+ def get(count:, offset:, options: nil)
18
19
  payload = { count: count,
19
20
  offset: offset }
21
+ payload[:options] = options unless options.nil?
22
+
20
23
  @client.post_with_auth('institutions/get', payload)
21
24
  end
22
25
 
@@ -226,5 +226,17 @@ module Plaid
226
226
  payload = { access_token: access_token }
227
227
  @client.post_with_auth('item/delete', payload)
228
228
  end
229
+
230
+ # Public: Removes an item
231
+ #
232
+ # Does a POST /item/remove call which is used to remove an item
233
+ #
234
+ # access_token - access_token who's item to remove
235
+ #
236
+ # Returns a parsed JSON of remove result
237
+ def remove(access_token)
238
+ payload = { access_token: access_token }
239
+ @client.post_with_auth('item/remove', payload)
240
+ end
229
241
  end
230
242
  end
@@ -57,18 +57,5 @@ module Plaid
57
57
  options: options_payload }
58
58
  @client.post_with_auth('transactions/get', payload)
59
59
  end
60
-
61
- # Public: Deactivate transactions for given access_token.
62
- #
63
- # Does a POST /transactions/deactivate call which deactivates the transaction product
64
- # for a given access_token
65
- #
66
- # access_token - access_token to deactivate transactions for
67
- #
68
- # Returns a parsed JSON containing a message describing deactivation success.
69
- def deactivate(access_token)
70
- payload = { access_token: access_token }
71
- @client.post_with_auth('transactions/deactivate', payload)
72
- end
73
60
  end
74
61
  end
@@ -1,3 +1,3 @@
1
1
  module Plaid
2
- VERSION = '4.0.0'.freeze
2
+ VERSION = '4.1.0'.freeze
3
3
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
19
  # to allow pushing to a single host or delete this section to allow pushing to any host.
20
20
  if spec.respond_to?(:metadata)
21
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
22
  else
23
23
  raise 'RubyGems 2.0 or newer is required to protect against ' \
24
24
  'public gem pushes.'
@@ -32,10 +32,16 @@ Gem::Specification.new do |spec|
32
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
33
  spec.require_paths = ['lib']
34
34
 
35
- spec.required_ruby_version = '>= 2.0.0'
35
+ spec.required_ruby_version = '>= 2.1.0'
36
+
37
+ spec.add_dependency 'faraday'
38
+ spec.add_dependency 'faraday_middleware'
36
39
 
37
40
  spec.add_development_dependency 'bundler', '~> 1.7'
38
- spec.add_development_dependency 'rake', '~> 10.0'
41
+ spec.add_development_dependency 'dotenv'
42
+ spec.add_development_dependency 'rake', '>= 10.0'
39
43
  spec.add_development_dependency 'sdoc', '~> 0.4.1'
40
- spec.add_development_dependency 'minitest', '~> 5.8'
44
+ spec.add_development_dependency 'minitest', '~> 5.10'
45
+ spec.add_development_dependency 'minitest-around', '~> 0.4.0'
46
+ spec.add_development_dependency 'vcr', '~> 3.0.3'
41
47
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plaid
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edmund Loo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-09 00:00:00.000000000 Z
11
+ date: 2018-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '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'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -24,18 +52,32 @@ dependencies:
24
52
  - - "~>"
25
53
  - !ruby/object:Gem::Version
26
54
  version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: rake
29
71
  requirement: !ruby/object:Gem::Requirement
30
72
  requirements:
31
- - - "~>"
73
+ - - ">="
32
74
  - !ruby/object:Gem::Version
33
75
  version: '10.0'
34
76
  type: :development
35
77
  prerelease: false
36
78
  version_requirements: !ruby/object:Gem::Requirement
37
79
  requirements:
38
- - - "~>"
80
+ - - ">="
39
81
  - !ruby/object:Gem::Version
40
82
  version: '10.0'
41
83
  - !ruby/object:Gem::Dependency
@@ -58,14 +100,42 @@ dependencies:
58
100
  requirements:
59
101
  - - "~>"
60
102
  - !ruby/object:Gem::Version
61
- version: '5.8'
103
+ version: '5.10'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.10'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest-around
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.4.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.4.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: vcr
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 3.0.3
62
132
  type: :development
63
133
  prerelease: false
64
134
  version_requirements: !ruby/object:Gem::Requirement
65
135
  requirements:
66
136
  - - "~>"
67
137
  - !ruby/object:Gem::Version
68
- version: '5.8'
138
+ version: 3.0.3
69
139
  description: Ruby gem wrapper for the Plaid API. Read more at the homepage, the wiki,
70
140
  or in the Plaid documentation.
71
141
  email:
@@ -74,18 +144,22 @@ executables: []
74
144
  extensions: []
75
145
  extra_rdoc_files: []
76
146
  files:
147
+ - ".env.sample"
148
+ - CHANGELOG.md
77
149
  - CONTRIBUTING.md
78
150
  - Gemfile
79
151
  - LICENSE.txt
152
+ - PUBLISHING.md
80
153
  - README.md
81
154
  - Rakefile
155
+ - UPGRADING.md
82
156
  - bin/console
83
157
  - bin/setup
84
158
  - circle.yml
85
159
  - lib/plaid.rb
86
160
  - lib/plaid/client.rb
87
- - lib/plaid/connect.rb
88
161
  - lib/plaid/errors.rb
162
+ - lib/plaid/middleware.rb
89
163
  - lib/plaid/products/accounts.rb
90
164
  - lib/plaid/products/auth.rb
91
165
  - lib/plaid/products/categories.rb
@@ -112,7 +186,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
186
  requirements:
113
187
  - - ">="
114
188
  - !ruby/object:Gem::Version
115
- version: 2.0.0
189
+ version: 2.1.0
116
190
  required_rubygems_version: !ruby/object:Gem::Requirement
117
191
  requirements:
118
192
  - - ">="
@@ -1,75 +0,0 @@
1
- require 'json'
2
- require 'net/http'
3
- require 'uri'
4
-
5
- require_relative 'version'
6
-
7
- module Plaid
8
- # Internal: A module encapsulating HTTP post requests
9
- module Connect
10
- # Internal: Headers used for correct request and SDK tracking.
11
- NETWORK_HEADERS = { 'User-Agent' => "Plaid Ruby v#{Plaid::VERSION}",
12
- 'Content-Type' => 'application/json' }.freeze
13
-
14
- # Internal: Default read timeout for HTTP calls in seconds.
15
- NETWORK_TIMEOUT = 600
16
-
17
- # Internal: Run POST request on path with payload.
18
- #
19
- # path - The path to send the request to.
20
- # payload - The hash with data.
21
- #
22
- # Returns the parsed JSON response body.
23
- def self.post(path, payload)
24
- uri = URI.parse(path)
25
-
26
- http = Net::HTTP.new(uri.host, uri.port)
27
- http.use_ssl = true
28
-
29
- http.read_timeout = Plaid::Connect::NETWORK_TIMEOUT
30
-
31
- request = Net::HTTP::Post.new(uri.path, Plaid::Connect::NETWORK_HEADERS)
32
- request.body = JSON.generate(payload)
33
-
34
- Plaid::Connect.run http, request
35
- end
36
-
37
- # Internal: Run the request and process the response.
38
- #
39
- # http - Object created by Net::HTTP.new
40
- # request - Object created by Net::HTTP::Post.new (for POST)
41
- #
42
- # Returns the parsed JSON body or raises an appropriate PlaidError
43
- def self.run(http, request)
44
- response = http.request(request)
45
-
46
- if response.body.nil? || response.body.empty?
47
- raise Plaid::PlaidError.new(0, 'Server error', 'Try to connect later')
48
- end
49
-
50
- # All responses are expected to have a JSON body, so we always parse,
51
- # not looking at the status code.
52
- body = JSON.parse(response.body)
53
-
54
- case response
55
- when Net::HTTPSuccess
56
- body
57
- else
58
- raise_error(body)
59
- end
60
- end
61
-
62
- # Internal: Raise an error with the class depending on reponse body.
63
- #
64
- # body - A parsed response body with error.
65
- #
66
- # Raises a PlaidError
67
- def self.raise_error(body)
68
- raise Plaid::Error.error_from_type(body['error_type']).new(body['error_type'],
69
- body['error_code'],
70
- body['error_message'],
71
- body['display_message'],
72
- body['request_id'])
73
- end
74
- end
75
- end