plaid-kilt 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b97a9853fe6fc505a415b82c258f6d2962c1fb2
4
+ data.tar.gz: f804262388591a3f8f37af71e3133de10bde95d6
5
+ SHA512:
6
+ metadata.gz: 3a6e46925739f73d5a6710f0619c49b4fc20a6d207d46bb450096f9e2817a3b1ae6bcfa6110a49c398cc52279b5ae6e1e7696b9e4cefbe6c8c0664208225f981
7
+ data.tar.gz: 827cb24aacd8442693ea1d1e469b132b67cacd0410e3ee98011fff6c6b0e1a15718de1cc5031c2eab3f054dc4e750309b9cfa3d284350e3d31d756704a758cfc
Binary file
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", "~> 1.0"
12
+ gem "jeweler", "~> 1.8.7"
13
+ end
14
+
15
+ gem 'httparty'
16
+ gem 'yard'
@@ -0,0 +1,78 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (4.0.3)
5
+ i18n (~> 0.6, >= 0.6.4)
6
+ minitest (~> 4.2)
7
+ multi_json (~> 1.3)
8
+ thread_safe (~> 0.1)
9
+ tzinfo (~> 0.3.37)
10
+ addressable (2.3.5)
11
+ atomic (1.1.14)
12
+ builder (3.2.2)
13
+ faraday (0.8.8)
14
+ multipart-post (~> 1.2.0)
15
+ git (1.2.6)
16
+ github_api (0.10.1)
17
+ addressable
18
+ faraday (~> 0.8.1)
19
+ hashie (>= 1.2)
20
+ multi_json (~> 1.4)
21
+ nokogiri (~> 1.5.2)
22
+ oauth2
23
+ hashie (2.0.5)
24
+ highline (1.6.20)
25
+ httparty (0.12.0)
26
+ json (~> 1.8)
27
+ multi_xml (>= 0.5.2)
28
+ httpauth (0.2.0)
29
+ i18n (0.6.9)
30
+ jeweler (1.8.8)
31
+ builder
32
+ bundler (~> 1.0)
33
+ git (>= 1.2.5)
34
+ github_api (= 0.10.1)
35
+ highline (>= 1.6.15)
36
+ nokogiri (= 1.5.10)
37
+ rake
38
+ rdoc
39
+ json (1.8.1)
40
+ jwt (0.1.8)
41
+ multi_json (>= 1.5)
42
+ minitest (4.7.5)
43
+ multi_json (1.8.4)
44
+ multi_xml (0.5.5)
45
+ multipart-post (1.2.0)
46
+ nokogiri (1.5.10)
47
+ oauth2 (0.9.2)
48
+ faraday (~> 0.8)
49
+ httpauth (~> 0.2)
50
+ jwt (~> 0.1.4)
51
+ multi_json (~> 1.0)
52
+ multi_xml (~> 0.5)
53
+ rack (~> 1.2)
54
+ rack (1.5.2)
55
+ rake (10.1.1)
56
+ rdoc (3.12.2)
57
+ json (~> 1.4)
58
+ shoulda (3.5.0)
59
+ shoulda-context (~> 1.0, >= 1.0.1)
60
+ shoulda-matchers (>= 1.4.1, < 3.0)
61
+ shoulda-context (1.1.6)
62
+ shoulda-matchers (2.4.0)
63
+ activesupport (>= 3.0.0)
64
+ thread_safe (0.1.3)
65
+ atomic
66
+ tzinfo (0.3.38)
67
+ yard (0.8.7.3)
68
+
69
+ PLATFORMS
70
+ ruby
71
+
72
+ DEPENDENCIES
73
+ bundler (~> 1.0)
74
+ httparty
75
+ jeweler (~> 1.8.7)
76
+ rdoc (~> 3.12)
77
+ shoulda
78
+ yard
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 John Koisch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,157 @@
1
+ = plaid
2
+
3
+ Accessing the Plaid API using Ruby.
4
+
5
+ === Version
6
+
7
+ Head is 0.5.1
8
+
9
+ == API
10
+
11
+ === Grab a client
12
+
13
+ client = Plaid.client 'user@name.com','pass_word', 'amex'
14
+
15
+ Where the last value is the institution.
16
+
17
+ === Connect
18
+
19
+ Plaid uses the "connect" Plaid API call to initiate the user flow and to access information.
20
+
21
+ info = client.connect <= gets information from Plaid
22
+
23
+ === Responses
24
+
25
+ This gem returns values in response objects or error objects. This is not very ruby-esque,
26
+ but it helps to make sense of the Plaid environment as well as making HTTPParty consumable for a business.
27
+
28
+ Response objects are initialized when the client gets a valid response from plaid. In the case of non-MFA banks
29
+
30
+ r = client.connect
31
+
32
+ * r.raw_response <= the raw HTTPParty response from Plaid
33
+ * r.accounts <= Array of accounts associated with this institution for this user
34
+ * r.transactions <= Array of transactions associated with the accounts
35
+ * r.is_mfa? <= false for this request
36
+ * r.message <= "Successful retrieved information from bank"
37
+ * r.http_code <= 200 in this case
38
+
39
+ Accounts and Transactions are objects that follow the JSON from Plaid. Thus you can expect to see
40
+
41
+ r.transactions[66].category_id <= "52544965f71e87d007000119"
42
+ r.transactions[66].name <= "Online Payment Processed"
43
+ r.accounts[2].institution_type <= "fake_institution"
44
+ r.accounts[2].balance["current"] <= 8.98 (which ruby infers as a double)
45
+
46
+ More explicitly
47
+
48
+ r.accounts[2].inspect
49
+
50
+ "#<PlaidObject:0x007f92853ca770 @type=\"credit\", @_id=\"52db1be4be13cbc36e000006\", @_item=\"52af631671c3bd1b25000064\", @_user=\"52af630f71c3bd1b25000063\", @balance={\"available\"=>3721, \"current\"=>8.98}, @meta={\"limit\"=>3500, \"name\"=>\"Plaid Credit Card\", \"number\"=>\"93004\"}, @institution_type=\"fake_institution\">"
51
+
52
+ === Errors
53
+
54
+ Errors use the gem's PlaidError object
55
+
56
+ client = Plaid.client 'user@name.com','pass_word_xyz', 'amex'
57
+ r = client.connect
58
+
59
+ * r.http_code <= 402
60
+ * r.plaid_code <= Plaid's internal error code (https://plaid.com/expand#Response_Code_Detail)
61
+ * r.error_message <= "Error"
62
+ * r.resolution <= "The username or password provided were not correct."
63
+ * r.raw_response <= the raw HTTPParty response
64
+
65
+ More explicitly
66
+
67
+ r = client.connect
68
+
69
+ "#<PlaidError:0x007f928513d638 @code=\"1200\", @message=\"Error\", @resolve=\"The username or password provided were not correct.\", @response=#<HTTParty::Response:0x7f928513d318 parsed_response={\"code\"=>1200, \"message\"=>\"invalid credentials\", \"resolve\"=>\"The username or password provided were not correct.\"}, @response=#<Net::HTTPPaymentRequired 402 Payment Required readbody=true>, @headers={\"content-type\"=>[\"application/json; charset=utf-8\"], \"date\"=>[\"Sat, 15 Mar 2014 17:31:46 GMT\"], \"x-powered-by\"=>[\"Express\"], \"content-length\"=>[\"109\"], \"connection\"=>[\"Close\"]}>>"
70
+
71
+ === MFA
72
+
73
+ MFA requires some management of user flow, which is why this gem tries to make the returns / errors from API calls
74
+ explicit. If it seems heavyweight to you, it is because the MFA process is a heavyweight process. Plaid does a
75
+ better job managing it than some others, but if you are going to use Plaid extensively,
76
+ then you are going to have to cope with MFA. See the wiki for more information.
77
+
78
+ https://github.com/jkoisch/plaid/wiki/MFA
79
+
80
+ These two diagrams go through user set up and user flow with MFA, respectively.
81
+
82
+ https://github.com/jkoisch/plaid/blob/master/design/plaid_user_set_up.png
83
+ https://github.com/jkoisch/plaid/blob/master/design/plaid_flows_connect.png
84
+
85
+ === Raw response
86
+
87
+ You can, if you would rather, ignore the PlaidResponse objects.
88
+
89
+ #config/initializers/plaid.rb
90
+ config.save_full_response = true
91
+
92
+ #when you get your responses
93
+ x = client.connect
94
+ x.raw_response <= the raw http response to the request. Have fun.
95
+
96
+ === Thin Client
97
+
98
+ The Plaid gem also has a thin_client for some secure followup operations:
99
+
100
+ thin_client = Plaid.thin_client "me@example.com", "chase", "access_token"
101
+
102
+ This client can be used where user credentials are NOT needed, but you still want to access scecure information on
103
+ the Plaid side of things:
104
+
105
+ thin_client.followup <= GET operation to get account and transaction information
106
+ thin_client.get_balance <= GET operation to receive account balances
107
+ thin_client.get_entity("unique_plaid_entity") <= Entity context information
108
+
109
+ == Configuration
110
+
111
+ You need to configure access through your own initializers using the values you receive from Plaid.
112
+
113
+ #config/initializers/plaid.rb
114
+ Plaid.configure do |config|
115
+ config.client_id = 'JUNK'
116
+ config.secret = 'JUNK'
117
+ config.endpoint = 'https://tartan.plaid.com/'
118
+ config.certpath = 'ca-bundle.crt'
119
+ config.headers = {'Content-Type'=>'application/x-www-form-urlencoded'}
120
+ config.webhook_address = 'http://If.You.Need.To.You.Can.Use/plaid_webhook/antennas'
121
+ config.save_full_response = false
122
+ end
123
+
124
+ == Additional Client Methods
125
+
126
+ The Plaid Client is not-quite-skinny so that it can handle various parts of the returns from the Plaid API. This is to
127
+ support alternate user flows or user handling in implementation.
128
+
129
+ The Plaid Client supports the following attr_readers, with the attribute settings handled through normal client
130
+ management.
131
+
132
+ client.settings
133
+ client.username
134
+ client.password
135
+ client.institution
136
+ client.endpoint
137
+ client.secret
138
+ client.access_token
139
+ client.mfa_type
140
+ client.mfa_message
141
+ client.mfa_return <= an array of the MFA returns from Plaid
142
+
143
+ == Contributing to the plaid gem
144
+
145
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
146
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
147
+ * Fork the project.
148
+ * Start a feature/bugfix branch.
149
+ * Commit and push until you are happy with your contribution.
150
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
151
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
152
+
153
+ == Copyright
154
+
155
+ Copyright (c) 2014 John Koisch. See LICENSE.txt for
156
+ further details.
157
+
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "plaid-kilt"
18
+ gem.homepage = "http://github.com/jkoisch/plaid"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Access the Plaid API using Ruby}
21
+ gem.description = %Q{Access the Plaid API using Rubyre}
22
+ gem.email = "jk@cloudsway.com"
23
+ gem.authors = ["John Koisch"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ task :default => :test
36
+
37
+ require 'rdoc/task'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "plaid #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.1
@@ -0,0 +1,35 @@
1
+ module Plaid
2
+ class << self
3
+ def configure(&block)
4
+ Plaid::Client::Base.configure(&block)
5
+ end
6
+
7
+ # client for initializing access through Plaid
8
+ def client(user, email, password, institution)
9
+ Plaid::Client::Base.new(user, email, password, institution)
10
+ end
11
+
12
+ # a special thin client for accessing Plaid without credentials securely
13
+ def thin_client(e_mail, institution, access_token)
14
+ Plaid::Client::ThinClient.new(e_mail, institution, access_token)
15
+ end
16
+
17
+ #scaffolding are general purpose tools for dealing with Plaid
18
+ def scaffold
19
+ Plaid::Scaffold::Base
20
+ end
21
+ end
22
+ end
23
+
24
+ require 'plaid/client/client'
25
+ require 'plaid/client/thin_client'
26
+ require 'plaid/client/configuration'
27
+ require 'plaid/client/connect'
28
+ require 'plaid/client/entity'
29
+ require 'plaid/client/balance'
30
+ require 'plaid/client/followup'
31
+ require 'plaid/scaffold/scaffold'
32
+ require 'plaid/scaffold/institution'
33
+ require 'plaid/plaid_object'
34
+ require 'plaid/plaid_error'
35
+ require 'plaid/plaid_response'
@@ -0,0 +1,15 @@
1
+ module Plaid
2
+ module Client
3
+ module Balances
4
+ include Plaid::Client::Configurations
5
+
6
+ def get_balance
7
+ body = body_retrieve
8
+
9
+ response = self.class.get('/balance', :query => body)
10
+ handle(response) { PlaidResponse.new(response, "Retrieved Balance") }
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,101 @@
1
+ module Plaid
2
+ module Client
3
+ module Bodies
4
+ include Plaid::Client::Configurations
5
+
6
+ # Used before the organization is obtained and chosen by the user
7
+ def body
8
+ {
9
+ :client_id => self.client_id,
10
+ :secret => self.secret
11
+ }
12
+ end
13
+
14
+ # For accessing balances associated with an access_token via a GET
15
+ # * client_id
16
+ # * secret
17
+ # * access_token
18
+ # * institution type
19
+ # * email of the user
20
+ def body_retrieve
21
+ ret = Hash.new
22
+ ret[:access_token] = self.access_token
23
+ ret[:type] = self.institution
24
+ ret[:email] = self.email
25
+ ret.merge(body)
26
+ end
27
+
28
+ # the fundamental body object used in most calls to Plaid.
29
+ # * client_id
30
+ # * secret
31
+ # * institution_type
32
+ # * credentials
33
+ # * email
34
+ # * options {"list":true}
35
+ def body_original
36
+ ret = Hash.new
37
+ ret[:type] = self.institution
38
+ ret[:credentials] = {
39
+ "username" => self.username,
40
+ "password" => self.password
41
+ }
42
+ ret[:email] = 'me@example.com'
43
+ ret[:options] = options(nil,"list",true)
44
+ ret.merge(body)
45
+ end
46
+
47
+ def body_test
48
+ ret = hash.new
49
+ ret[:options] = options(nil,"pretty","true")
50
+ ret.merge(body_original)
51
+ end
52
+
53
+ #simple hash providing the entity_id to plaid.
54
+ def body_entity(entity_id)
55
+ {
56
+ :entity_id => entity_id,
57
+ :options => {"pretty"=>"true"}
58
+ }
59
+ end
60
+
61
+ #adds an mfa answer to the body.
62
+ #based on {#body}
63
+ def body_mfa(answer)
64
+ ret = Hash.new
65
+ ret[:mfa] = answer.to_s
66
+ ret[:access_token] = self.access_token
67
+ ret[:type] = self.institution
68
+ ret.merge(body)
69
+ end
70
+
71
+ #adds a webhook address to {#body_mfa}
72
+ def body_mfa_webhook(answer)
73
+ ret = Hash.new
74
+ ret[:options] = options(nil,"webhook", webhook_address )
75
+ ret.merge(body_mfa(answer))
76
+ end
77
+
78
+ def body_mfa_mode(mode)
79
+ ret = Hash.new
80
+ ret[:options] = options(nil, "send_method", mode)
81
+ ret[:access_token] = self.access_token
82
+ ret[:type] = self.institution
83
+ ret.merge(body)
84
+ end
85
+
86
+ def body_init_user
87
+ ret = Hash.new
88
+ ret[:options] = options(options(nil,"login",true),"webhook",webhook_address )
89
+ ret.merge(body_original)
90
+ end
91
+
92
+ #helper method to add options to an option hash
93
+ def options(original_hash=nil, key, value)
94
+ j = Hash.new
95
+ j[key] = value
96
+ j.merge(original_hash) unless original_hash.nil?
97
+ j
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,89 @@
1
+ module Plaid
2
+ module Client
3
+
4
+ class Base
5
+ attr_accessor :mfa_response, :mfa_type, :mfa_message, :username, :password, :institution, :endpoint, :secret, :access_token, :is_mfa_initialized, :email
6
+
7
+ require 'plaid/client/configuration'
8
+ require 'plaid/client/connect'
9
+ require 'plaid/client/body'
10
+ require 'plaid/plaid_object'
11
+ require 'plaid/plaid_error'
12
+ require 'plaid/plaid_response'
13
+ require 'httparty'
14
+ include Plaid::Client::Configurations
15
+ include Plaid::Client::Logins
16
+ include Plaid::Client::Bodies
17
+ include HTTParty
18
+
19
+ base_uri endpoint
20
+ format :json
21
+ ssl_ca_file certpath
22
+ ssl_version :SSLv3
23
+ debug_output $stdout
24
+
25
+ def initialize(user, e_mail, pass_word, institution)
26
+ self.username = user
27
+ self.email = e_mail
28
+ self.password = pass_word
29
+ self.institution = institution
30
+ self.mfa_response ||= []
31
+ end
32
+
33
+ def settings
34
+ puts "Base URI: " + endpoint.to_s
35
+ puts "Cert: " + certpath.to_s
36
+ puts "User: " + self.username.to_s
37
+ puts "Email: " + self.email.to_s
38
+ puts "Plaid Client_id: " + self.client_id.to_s
39
+ puts "Webhook address: " + webhook_address
40
+ puts "Save full response: " + save_full_response.to_s
41
+ end
42
+
43
+ #generic method for handling the structure of the response. Only creates an error object if there is an error (business error) from Plaid.com. Yields to the block with calling function
44
+ def handle(response)
45
+ if response.code.eql? 200
46
+ self.mfa_type = nil
47
+ self.mfa_message = nil
48
+ self.is_mfa_initialized = false
49
+ yield(response)
50
+ elsif response.code.eql? 201 #mfa
51
+ mfa_201 = PlaidResponse.new(response, "MFA")
52
+ self.access_token = mfa_201.access_token
53
+ self.mfa_type = mfa_201.raw_response.type
54
+ self.mfa_response << mfa_201
55
+ self.mfa_message = mfa_201.message
56
+ self.is_mfa_initialized = mfa_201.is_mfa?
57
+ mfa_201
58
+ else
59
+ PlaidError.new(response, "Error")
60
+ end
61
+ end
62
+
63
+ #for testing through IRB
64
+ def secrets(client_id, secret)
65
+ self.client_id = client_id
66
+ self.secret = secret
67
+ "Set"
68
+ end
69
+
70
+ def is_mfa?
71
+ @is_mfa_initialized
72
+ end
73
+
74
+ def plaid_response_codes
75
+ {
76
+ 200 => "Success",
77
+ 201 => "MFA Required",
78
+ 400 => "Bad Request",
79
+ 401 => "Unauthorized",
80
+ 402 => "Request Failed",
81
+ 404 => "Cannot be Found",
82
+ 500 => "Server Error"
83
+ }
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,79 @@
1
+ module Plaid
2
+ module Client
3
+ module Configurations
4
+ require 'active_support/concern'
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ #Plaid.com
9
+ add_config :endpoint
10
+ add_config :headers
11
+
12
+ #app
13
+ add_config :certpath
14
+ add_config :client_id
15
+ add_config :secret
16
+ add_config :webhook_address
17
+ add_config :save_full_response
18
+
19
+ # set default values
20
+ reset_config
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ def add_config(name)
26
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
27
+
28
+ def self.#{name}(value=nil)
29
+ @#{name} = value if value
30
+ return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
31
+ name = superclass.#{name}
32
+ return nil if name.nil? && !instance_variable_defined?("@#{name}")
33
+ @#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
34
+ end
35
+
36
+ def self.#{name}=(value)
37
+ @#{name} = value
38
+ end
39
+
40
+ def #{name}=(value)
41
+ @#{name} = value
42
+ end
43
+
44
+ def #{name}
45
+ value = @#{name} if instance_variable_defined?(:@#{name})
46
+ value = self.class.#{name} unless instance_variable_defined?(:@#{name})
47
+ if value.instance_of?(Proc)
48
+ value.arity >= 1 ? value.call(self) : value.call
49
+ else
50
+ value
51
+ end
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ def configure
57
+ yield self
58
+ end
59
+
60
+ ##
61
+ # sets configuration to default
62
+ #
63
+ def reset_config
64
+ configure do |config|
65
+
66
+ config.client_id = 'JUNK'
67
+ config.secret = 'JUNK'
68
+ config.endpoint = 'https://tartan.plaid.com/'
69
+ config.certpath = 'ca-bundle.crt'
70
+ config.headers = {'Content-Type'=>'application/x-www-form-urlencoded'}
71
+ config.webhook_address = 'http://domain.com/plaid_webhook/antennas'
72
+ config.save_full_response = true
73
+
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,67 @@
1
+ module Plaid
2
+ module Client
3
+ module Logins
4
+ include Plaid::Client::Configurations
5
+
6
+ #connects to the plaid api.
7
+ #this can be used to retrieve information, get the access token, and initialize a user.
8
+ #sets the access token on the {Plaid::Client::Base}
9
+ #@return {PlaidResponse}
10
+ def connect
11
+ body = body_original
12
+
13
+ response = self.class.post('/connect', :query => body)
14
+
15
+ handle(response) do |r|
16
+ plaid = PlaidResponse.new(r, "Successful retrieved information from bank")
17
+ self.access_token = plaid.access_token
18
+ plaid
19
+ end
20
+ end
21
+
22
+ #The basic way to submit MFA information to Plaid.
23
+ #Simply appends the answer into the parameters for submission.
24
+ #@return {PlaidResponse} or {PlaidError}
25
+ def connect_step(mfa_response)
26
+ body = body_mfa(mfa_response)
27
+ response = self.class.post('/connect/step', :query => body)
28
+
29
+ handle(response) { PlaidResponse.new(response, "Successful MFA submission - retrieved information from bank") }
30
+ end
31
+
32
+ #Submits an MFA answer to Plaid.
33
+ #Adds a webhook return address as a parameter.
34
+ #@return {PlaidResponse} or {PlaidError}
35
+ def connect_step_webhook(mfa_response)
36
+ body = body_mfa_webhook(mfa_response)
37
+ response = self.class.post('/connect/step', :query => body)
38
+
39
+ handle(response) { PlaidResponse.new(response, "Successful MFA submission - Webhook will notify when retrieved information from bank") }
40
+ end
41
+
42
+ # Plaid's preferred way to initialize a user to their bank via the Plaid Proxy.
43
+ #
44
+ def connect_init_user
45
+ body = body_init_user
46
+ response = self.class.post('/connect', :query => body)
47
+
48
+ handle(response) { PlaidResponse.new(response, "Successfully added user; Wait on Webhook Response") }
49
+ end
50
+
51
+ def connect_filter_response
52
+
53
+ end
54
+
55
+ def connect_step_specify_mode(mode)
56
+ body = body_mfa_mode(mode)
57
+ response = self.class.post('/connect/step', :query => body)
58
+ handle(response) { PlaidResponse.new(response, "Successful MFA mode submission - You will now be asked to
59
+ input your code.") }
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
@@ -0,0 +1,16 @@
1
+ module Plaid
2
+ module Client
3
+ module Entities
4
+ include Plaid::Client::Configurations
5
+
6
+ def get_entity(entity_id)
7
+ body = body_entity(entity_id)
8
+
9
+ response = self.class.get('/entity', :query => body)
10
+
11
+ handle(response) { PlaidResponse.new(response, "Retrieved Entity")}
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module Plaid
2
+ module Client
3
+ module Followups
4
+ include Plaid::Client::Configurations
5
+
6
+ #explicitly for following up after an init to retrieve updated information from Plaid
7
+ def followup
8
+ body = body_retrieve
9
+ response = self.class.get('/connect', :query => body)
10
+
11
+ handle(response) { PlaidResponse.new(response, "Successfully retrieved Transactions") }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ module Plaid
2
+ module Client
3
+ class ThinClient
4
+ attr_accessor :institution, :endpoint, :secret, :access_token, :email, :client_id
5
+
6
+ require 'plaid/client/configuration'
7
+ require 'plaid/client/followup'
8
+ require 'plaid/client/entity'
9
+ require 'plaid/client/balance'
10
+ require 'plaid/client/body'
11
+ require 'plaid/plaid_object'
12
+ require 'plaid/plaid_error'
13
+ require 'plaid/plaid_response'
14
+ require 'httparty'
15
+ include Plaid::Client::Configurations
16
+ include Plaid::Client::Bodies
17
+ include Plaid::Client::Entities
18
+ include Plaid::Client::Balances
19
+ include Plaid::Client::Followups
20
+ include HTTParty
21
+
22
+ base_uri endpoint
23
+ format :json
24
+ ssl_ca_file certpath
25
+ ssl_version :SSLv3
26
+ debug_output $stdout
27
+
28
+ def initialize(e_mail, institution, access_token)
29
+ self.email = e_mail
30
+ self.institution = institution
31
+ self.access_token = access_token
32
+ end
33
+
34
+ #for testing through IRB
35
+ def secrets(client_id, secret)
36
+ self.client_id = client_id
37
+ self.secret = secret
38
+ "Set"
39
+ end
40
+
41
+ def settings
42
+ puts "Base URI: " + endpoint.to_s
43
+ puts "Cert: " + certpath.to_s
44
+ puts "Email: " + self.email.to_s
45
+ puts "Plaid Client_id: " + self.client_id.to_s
46
+ puts "Webhook address: " + webhook_address
47
+ puts "Save full response: " + save_full_response.to_s
48
+ end
49
+
50
+ #generic method for handling the structure of the response. Only creates an error object if there is an error (business error) from Plaid.com. Yields to the block with calling function
51
+ def handle(response)
52
+ if response.code.eql? 200
53
+ yield(response)
54
+ else
55
+ PlaidError.new(response, "Error")
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ class PlaidError < PlaidObject
2
+
3
+ require 'plaid/plaid_object'
4
+
5
+ @error = nil
6
+ @message = nil
7
+ @response = nil
8
+
9
+ def initialize(response, message)
10
+ if response.parsed_response
11
+ super(response.parsed_response)
12
+ else
13
+ super(response)
14
+ end
15
+ @response = response
16
+ @message = message
17
+ end
18
+
19
+ def error_message
20
+ self.message
21
+ end
22
+
23
+ def http_code
24
+ @response.code if @response
25
+ end
26
+
27
+ def plaid_code
28
+ response.code
29
+ end
30
+
31
+ def resolution
32
+ self.resolve
33
+ end
34
+
35
+ def raw_response
36
+ @response if @response
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ class PlaidObject
2
+
3
+ def initialize(hash)
4
+ hash.keys.each do |key|
5
+ self.class.send(:define_method, "#{key}") do
6
+ instance_variable_get("@#{key}")
7
+ end
8
+
9
+ if hash[key].is_a? Array
10
+ arr = []
11
+ hash[key].each do |chunk|
12
+ if chunk.is_a? Hash
13
+ arr << PlaidObject.new(chunk)
14
+ else
15
+ arr << chunk
16
+ end
17
+ end
18
+ instance_variable_set("@#{key}", arr)
19
+ elsif hash[key].is_a? Hash
20
+ h = hash[key]
21
+ instance_variable_set("@#{key}", h)
22
+ else
23
+ eval("@#{key} = '#{encode(hash[key].to_s)}'")
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ def encode(str)
30
+ str.gsub("'", "")
31
+ end
32
+
33
+ end
@@ -0,0 +1,80 @@
1
+ class PlaidResponse
2
+ include Plaid::Client::Configurations
3
+
4
+ require 'plaid/plaid_object'
5
+ require 'plaid/client/configuration'
6
+
7
+ attr_reader :http_code, :mfa_message, :accounts, :transactions, :access_token
8
+
9
+ @response = nil
10
+ @message = "N/A"
11
+ @mfa_type
12
+ @mfa_message
13
+ @questions = nil
14
+ @mfa_modes = nil
15
+ @is_mfa_initialized = false
16
+
17
+ def initialize(response, message=nil)
18
+ @http_code = response.code
19
+ zed = PlaidObject.new(response)
20
+
21
+ if save_full_response.eql?(save_full_response)
22
+ @response = zed
23
+ end
24
+
25
+ unless message.eql?("MFA")
26
+ @accounts = zed.accounts
27
+ @transactions = zed.transactions
28
+ @is_mfa_initialized = false
29
+ else
30
+ manage_mfa_type(zed)
31
+ @is_mfa_initialized = true
32
+ end
33
+
34
+ @access_token = zed.access_token
35
+ @message = message if message
36
+ zed = nil
37
+ end
38
+
39
+ def raw_response
40
+ @response
41
+ end
42
+
43
+ def message
44
+ @message.eql?("MFA") ? @message + ": " + @mfa_message : @message
45
+ end
46
+
47
+ def mfa_type
48
+ @message.eql?("MFA") ? @mfa_type : "Not a response from an MFA request to a bank"
49
+ end
50
+
51
+ def is_mfa?
52
+ @is_mfa_initialized
53
+ end
54
+
55
+ def mfa_modes
56
+ @mfa_modes
57
+ end
58
+
59
+ def manage_mfa_type(zed)
60
+ @mfa_type = zed.type
61
+
62
+ if @mfa_type.eql?("device")
63
+ @mfa_message = zed.mfa["message"]
64
+ elsif @mfa_type.eql?("questions")
65
+ @questions ||= []
66
+ zed.mfa.each do |q|
67
+ @questions << q.question
68
+ end
69
+ @mfa_message = @questions.reverse.pop
70
+ elsif @mfa_type.eql?("list")
71
+ @mfa_modes ||= []
72
+ @mfa_message = "There are several ways to authenticate, or it will default to your email"
73
+ zed.mfa.each do |q|
74
+ @mfa_modes << q
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,26 @@
1
+ module Plaid
2
+ module Scaffold
3
+ module Institutions
4
+
5
+ module ClassMethods
6
+ def index_institutions
7
+ ret = []
8
+ response = self.get('/institutions')
9
+ response.each do |piece|
10
+ ret << PlaidObject.new(piece)
11
+ end
12
+ self.institutions = ret
13
+ end
14
+
15
+ def get_institution(id)
16
+ response = self.get('/institutions/' + id.to_s)
17
+ PlaidObject.new(response)
18
+ end
19
+ end
20
+
21
+ def self.included(base)
22
+ base.extend(ClassMethods)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ module Plaid
2
+ module Scaffold
3
+ class Base
4
+
5
+ require 'plaid/scaffold/institution'
6
+ require 'plaid/client/configuration'
7
+ require 'plaid/plaid_object'
8
+ require 'plaid/plaid_error'
9
+ require 'plaid/plaid_response'
10
+ require 'httparty'
11
+ require 'singleton'
12
+ include Plaid::Scaffold::Institutions
13
+ include Plaid::Client::Configurations
14
+ include HTTParty
15
+ include Singleton
16
+
17
+ base_uri endpoint
18
+ format :json
19
+ ssl_ca_file certpath
20
+ ssl_version :SSLv3
21
+ debug_output $stdout
22
+
23
+ @@institutions ||= []
24
+
25
+ def self.institutions=(i)
26
+ @@institutions = i
27
+ end
28
+
29
+ def self.institutions
30
+ if @@institutions.empty?
31
+ self.index_institutions rescue "There is something wrong with Plaid!"
32
+ end
33
+
34
+ @@institutions
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module Plaid
2
+ module Text
3
+ class Narrative
4
+ class << self
5
+
6
+ def introduction
7
+
8
+ end
9
+
10
+
11
+ def submit_credentials
12
+
13
+ end
14
+
15
+
16
+ def get_transaction
17
+
18
+ end
19
+
20
+ def updating_institution_info
21
+
22
+ end
23
+
24
+ def mfa
25
+
26
+ end
27
+
28
+
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'plaid'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestPlaid < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: plaid-kilt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
+ platform: ruby
6
+ authors:
7
+ - John Koisch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
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: yard
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'
41
+ - !ruby/object:Gem::Dependency
42
+ name: shoulda
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jeweler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.8.7
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 1.8.7
97
+ description: Access the Plaid API using Rubyre
98
+ email: jk@cloudsway.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files:
102
+ - LICENSE.txt
103
+ - README.rdoc
104
+ files:
105
+ - .DS_Store
106
+ - .document
107
+ - Gemfile
108
+ - Gemfile.lock
109
+ - LICENSE.txt
110
+ - README.rdoc
111
+ - Rakefile
112
+ - VERSION
113
+ - design/plaid_flows_connect.png
114
+ - design/plaid_user_set_up.png
115
+ - lib/plaid.rb
116
+ - lib/plaid/client/balance.rb
117
+ - lib/plaid/client/body.rb
118
+ - lib/plaid/client/client.rb
119
+ - lib/plaid/client/configuration.rb
120
+ - lib/plaid/client/connect.rb
121
+ - lib/plaid/client/entity.rb
122
+ - lib/plaid/client/followup.rb
123
+ - lib/plaid/client/thin_client.rb
124
+ - lib/plaid/plaid_error.rb
125
+ - lib/plaid/plaid_object.rb
126
+ - lib/plaid/plaid_response.rb
127
+ - lib/plaid/scaffold/institution.rb
128
+ - lib/plaid/scaffold/scaffold.rb
129
+ - lib/plaid/text/exposition.rb
130
+ - test/helper.rb
131
+ - test/test_plaid.rb
132
+ homepage: http://github.com/jkoisch/plaid
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.1.11
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Access the Plaid API using Ruby
156
+ test_files: []