Skip to content

Commit 359fe57

Browse files
committedSep 15, 2020
Merge remote-tracking branch 'upstream/master' into adding-webhook-support
2 parents 1cf6712 + 77af45a commit 359fe57

21 files changed

+1752
-195
lines changed
 

‎.tool-versions

-1
This file was deleted.

‎.travis.yml

+3
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ matrix:
1414
exclude:
1515
- elixir: 1.8.0
1616
otp_release: 19.3
17+
env:
18+
- MIX_ENV=test
19+
script: mix coveralls.travis
1720

‎README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Plaid
22

33
[![Build Status](https://travis-ci.org/wfgilman/plaid-elixir.svg?branch=master)](https://travis-ci.org/wfgilman/plaid-elixir)
4+
[![Coverage Status](https://coveralls.io/repos/github/wfgilman/plaid-elixir/badge.svg?branch=master)](https://coveralls.io/github/wfgilman/plaid-elixir?branch=master)
45
[![Hex.pm Version](https://img.shields.io/hexpm/v/plaid_elixir.svg)](https://hex.pm/packages/plaid_elixir)
6+
[![Hex.pm Download Total](https://img.shields.io/hexpm/dt/plaid_elixir.svg)](https://hex.pm/packages/plaid_elixir)
57

68
[Documentation](https://hexdocs.pm/plaid_elixir)
79

@@ -11,7 +13,7 @@ Supported Plaid products:
1113

1214
- [x] Transactions
1315
- [x] Auth
14-
- [ ] Identity
16+
- [x] Identity
1517
- [x] Balance
1618
- [x] Income
1719
- [ ] Assets
@@ -25,7 +27,7 @@ Add to your dependencies in `mix.exs`. The hex specification is required.
2527

2628
```elixir
2729
def deps do
28-
[{:plaid, "~> 1.7", hex: :plaid_elixir}]
30+
[{:plaid, "~> 2.0", hex: :plaid_elixir}]
2931
end
3032
```
3133

@@ -69,6 +71,7 @@ Plaid.Accounts.get(%{access_token: "my-token"}, %{root_uri: "http://sandbox.plai
6971
Access tokens are required for almost all calls to Plaid. However, they can only be obtained
7072
using [Plaid Link](https://plaid.com/docs/link/transition-guide/#creating-items-with-link).
7173

74+
Call the `/link` endpoint to create a link token that you'll use to initialize Plaid Link.
7275
Once a user successfully connects to his institution using Plaid Link, a
7376
public token is returned to the client. This single-use public token can be exchanged
7477
for an access token and item id (both of which should be stored) using
@@ -84,4 +87,4 @@ As of version `1.2`, this library natively supports serialization of its structs
8487

8588
This library uses [bypass](https://github.com/PSPDFKit-labs/bypass) to simulate HTTP responses from Plaid.
8689

87-
It uses Elixir's native formatter as of `1.3.2`
90+
It uses Elixir's native formatter as of `1.3.2` and Credo.

‎lib/plaid.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ defmodule Plaid do
9595
end
9696

9797
def process_response_body(body) do
98-
Poison.Parser.parse!(body)
98+
Poison.Parser.parse!(body, %{})
9999
end
100100

101101
defp get_request_headers do

‎lib/plaid/accounts.ex

+76
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ defmodule Plaid.Accounts do
2828
@derive Jason.Encoder
2929
defstruct account_id: nil,
3030
balances: nil,
31+
owners: nil,
3132
name: nil,
3233
mask: nil,
3334
official_name: nil,
@@ -37,6 +38,7 @@ defmodule Plaid.Accounts do
3738
@type t :: %__MODULE__{
3839
account_id: String.t(),
3940
balances: Plaid.Accounts.Account.Balance.t(),
41+
owners: [Plaid.Accounts.Account.Owner.t()],
4042
name: String.t(),
4143
mask: String.t(),
4244
official_name: String.t(),
@@ -64,6 +66,80 @@ defmodule Plaid.Accounts do
6466
unofficial_currency_code: String.t()
6567
}
6668
end
69+
70+
defmodule Owner do
71+
@moduledoc """
72+
Plaid Account Owner data structure.
73+
"""
74+
75+
@derive Jason.Encoder
76+
defstruct addresses: nil,
77+
emails: nil,
78+
names: nil,
79+
phone_numbers: nil
80+
81+
@type t :: %__MODULE__{
82+
addresses: [Plaid.Accounts.Owner.Address],
83+
emails: [Plaid.Accounts.Owner.Email],
84+
names: [String.t()],
85+
phone_numbers: [Plaid.Accounts.Owner.PhoneNumber]
86+
}
87+
88+
defmodule Address do
89+
@moduledoc """
90+
Plaid Account Owner Address data structure.
91+
"""
92+
93+
@derive Jason.Encoder
94+
defstruct data: %{city: nil, region: nil, street: nil, postal_code: nil, country: nil},
95+
primary: false
96+
97+
@type t :: %__MODULE__{
98+
data: %{
99+
city: String.t(),
100+
region: String.t(),
101+
street: String.t(),
102+
postal_code: String.t(),
103+
country: String.t()
104+
},
105+
primary: boolean()
106+
}
107+
end
108+
109+
defmodule Email do
110+
@moduledoc """
111+
Plaid Account Owner Email data structure.
112+
"""
113+
114+
@derive Jason.Encoder
115+
defstruct data: nil,
116+
primary: false,
117+
type: nil
118+
119+
@type t :: %__MODULE__{
120+
data: String.t(),
121+
primary: boolean(),
122+
type: String.t()
123+
}
124+
end
125+
126+
defmodule PhoneNumber do
127+
@moduledoc """
128+
Plaid Account Owner PhoneNumber data structure.
129+
"""
130+
131+
@derive Jason.Encoder
132+
defstruct data: nil,
133+
primary: false,
134+
type: nil
135+
136+
@type t :: %__MODULE__{
137+
data: String.t(),
138+
primary: boolean(),
139+
type: String.t()
140+
}
141+
end
142+
end
67143
end
68144

69145
@doc """

‎lib/plaid/auth.ex

+58-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ defmodule Plaid.Auth do
1313
@type t :: %__MODULE__{
1414
accounts: [Plaid.Accounts.Account.t()],
1515
item: Plaid.Item.t(),
16-
numbers: %{ach: [Plaid.Auth.Numbers.ACH.t()]},
16+
numbers: %{
17+
ach: [Plaid.Auth.Numbers.ACH.t()],
18+
eft: [Plaid.Auth.Numbers.EFT.t()],
19+
international: [Plaid.Auth.Numbers.International.t()],
20+
bacs: [Plaid.Auth.Numbers.BACS.t()]
21+
},
1722
request_id: String.t()
1823
}
1924
@type params :: %{
@@ -34,15 +39,18 @@ defmodule Plaid.Auth do
3439
"""
3540

3641
@derive Jason.Encoder
37-
defstruct ach: [], eft: []
42+
defstruct ach: [], eft: [], international: [], bacs: []
3843

3944
@type t :: %__MODULE__{
40-
ach: [Plaid.Auth.Numbers.ACH.t()]
45+
ach: [Plaid.Auth.Numbers.ACH.t()],
46+
eft: [Plaid.Auth.Numbers.EFT.t()],
47+
international: [Plaid.Auth.Numbers.International.t()],
48+
bacs: [Plaid.Auth.Numbers.BACS.t()]
4149
}
4250

4351
defmodule ACH do
4452
@moduledoc """
45-
Plaid Account Number data structure.
53+
Plaid Account Number ACH data structure.
4654
"""
4755

4856
@derive Jason.Encoder
@@ -55,6 +63,52 @@ defmodule Plaid.Auth do
5563
wire_routing: String.t()
5664
}
5765
end
66+
67+
defmodule EFT do
68+
@moduledoc """
69+
Plaid Account Number EFT data structure
70+
"""
71+
72+
@derive Jason.Encoder
73+
defstruct account: nil, account_id: nil, institution: nil, branch: nil
74+
75+
@type t :: %__MODULE__{
76+
account: String.t(),
77+
account_id: String.t(),
78+
institution: String.t(),
79+
branch: String.t()
80+
}
81+
end
82+
83+
defmodule International do
84+
@moduledoc """
85+
Plaid Account Number Institution data structure.
86+
"""
87+
88+
@derive Jason.Encoder
89+
defstruct account_id: nil, bic: nil, iban: nil
90+
91+
@type t :: %__MODULE__{
92+
account_id: String.t(),
93+
bic: String.t(),
94+
iban: String.t()
95+
}
96+
end
97+
98+
defmodule BACS do
99+
@moduledoc """
100+
Plaid Account Number BACS data structure.
101+
"""
102+
103+
@derive Jason.Encoder
104+
defstruct account: nil, account_id: nil, sort_code: nil
105+
106+
@type t :: %__MODULE__{
107+
account: String.t(),
108+
account_id: String.t(),
109+
sort_code: String.t()
110+
}
111+
end
58112
end
59113

60114
@doc """

‎lib/plaid/identity.ex

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
defmodule Plaid.Identity do
2+
@moduledoc """
3+
Functions for Plaid `identity` endpoint.
4+
"""
5+
6+
import Plaid, only: [make_request_with_cred: 4, validate_cred: 1]
7+
8+
alias Plaid.Utils
9+
10+
@derive Jason.Encoder
11+
defstruct accounts: [], item: nil, request_id: nil
12+
13+
@type t :: %__MODULE__{
14+
accounts: [Plaid.Accounts.Account.t()],
15+
item: Plaid.Item.t(),
16+
request_id: String.t()
17+
}
18+
@type params :: %{
19+
required(:access_token) => String.t()
20+
}
21+
@type config :: %{required(atom) => String.t()}
22+
23+
@endpoint :identity
24+
25+
@doc """
26+
Gets identity data associated with an Item.
27+
28+
Parameters
29+
```
30+
%{
31+
access_token: "access-env-identifier"
32+
}
33+
```
34+
"""
35+
@spec get(params, config | nil) :: {:ok, Plaid.Identity.t()} | {:error, Plaid.Error.t()}
36+
def get(params, config \\ %{}) do
37+
config = validate_cred(config)
38+
endpoint = "#{@endpoint}/get"
39+
40+
:post
41+
|> make_request_with_cred(endpoint, config, params)
42+
|> Utils.handle_resp(@endpoint)
43+
end
44+
end

‎lib/plaid/institutions.ex

+199-43
Original file line numberDiff line numberDiff line change
@@ -26,78 +26,225 @@ defmodule Plaid.Institutions do
2626
"""
2727

2828
@derive Jason.Encoder
29-
defstruct brand_name: nil,
30-
brand_subheading: nil,
31-
colors: nil,
29+
defstruct country_codes: [],
3230
credentials: [],
3331
has_mfa: nil,
34-
health_status: nil,
32+
input_spec: nil,
3533
institution_id: nil,
36-
legacy_institution_code: nil,
37-
legacy_institution_type: nil,
38-
link_health_status: nil,
3934
logo: nil,
4035
mfa: [],
4136
mfa_code_type: nil,
4237
name: nil,
43-
name_break: nil,
44-
portal: nil,
38+
oauth: nil,
39+
primary_color: nil,
4540
products: [],
4641
request_id: nil,
47-
url: nil,
48-
url_account_locked: nil,
49-
url_account_setup: nil,
50-
url_forgotten_password: nil
42+
routing_numbers: [],
43+
status: nil,
44+
url: nil
5145

5246
@type t :: %__MODULE__{
53-
brand_name: String.t(),
54-
brand_subheading: String.t(),
55-
colors: Plaid.Institutions.Institution.Colors.t(),
47+
country_codes: [String.t()],
5648
credentials: [Plaid.Institutions.Institution.Credentials.t()],
5749
has_mfa: false | true,
58-
health_status: String.t(),
50+
input_spec: String.t(),
5951
institution_id: String.t(),
60-
legacy_institution_code: String.t(),
61-
legacy_institution_type: String.t(),
62-
link_health_status: String.t(),
6352
logo: String.t(),
6453
mfa: [String.t()],
6554
mfa_code_type: String.t(),
6655
name: String.t(),
67-
name_break: String.t(),
68-
portal: String.t(),
56+
oauth: boolean(),
57+
primary_color: String.t(),
6958
products: [String.t()],
7059
request_id: String.t(),
71-
url: String.t(),
72-
url_account_locked: String.t(),
73-
url_account_setup: String.t(),
74-
url_forgotten_password: String.t()
60+
routing_numbers: [String.t()],
61+
status: Plaid.Institutions.Institution.Status.t(),
62+
url: String.t()
7563
}
7664

77-
defmodule Colors do
65+
defmodule Credentials do
7866
@moduledoc """
79-
Plaid Institution Colors data structure.
67+
Plaid Institution Credentials data structure.
8068
"""
8169

8270
@derive Jason.Encoder
83-
defstruct dark: nil, darker: nil, light: nil, primary: nil
84-
85-
@type t :: %__MODULE__{
86-
dark: String.t(),
87-
darker: String.t(),
88-
light: String.t(),
89-
primary: String.t()
90-
}
71+
defstruct label: nil, name: nil, type: nil
72+
@type t :: %__MODULE__{label: String.t(), name: String.t(), type: String.t()}
9173
end
9274

93-
defmodule Credentials do
75+
defmodule Status do
9476
@moduledoc """
95-
Plaid Institution Credentials data structure.
77+
Plaid Institution Status data structure.
9678
"""
9779

9880
@derive Jason.Encoder
99-
defstruct label: nil, name: nil, type: nil
100-
@type t :: %__MODULE__{label: String.t(), name: String.t(), type: String.t()}
81+
defstruct item_logins: nil,
82+
transactions_updates: nil,
83+
auth: nil,
84+
balance: nil,
85+
identity: nil
86+
87+
@type t :: %__MODULE__{
88+
item_logins: Plaid.Institutions.Institution.Status.ItemLogins.t(),
89+
transactions_updates: Plaid.Institutions.Institution.Status.TransactionsUpdates.t(),
90+
auth: Plaid.Institutions.Institution.Status.Auth.t(),
91+
balance: Plaid.Institutions.Institution.Status.Balance.t(),
92+
identity: Plaid.Institutions.Institution.Status.Identity.t()
93+
}
94+
95+
defmodule ItemLogins do
96+
@moduledoc """
97+
Plaid Institution Item Logins Status data structure.
98+
"""
99+
100+
@derive Jason.Encoder
101+
defstruct status: nil, last_status_change: nil, breakdown: nil
102+
103+
@type t :: %__MODULE__{
104+
status: String.t(),
105+
last_status_change: String.t(),
106+
breakdown: Plaid.Institutions.Institution.Status.ItemLogins.Breakdown.t()
107+
}
108+
109+
defmodule Breakdown do
110+
@moduledoc """
111+
Plaid Institution Item Logins Breakdown Status data structure.
112+
"""
113+
114+
@derive Jason.Encoder
115+
defstruct success: nil, error_plaid: nil, error_institution: nil
116+
117+
@type t :: %__MODULE__{
118+
success: number(),
119+
error_plaid: number(),
120+
error_institution: number()
121+
}
122+
end
123+
end
124+
125+
defmodule TransactionsUpdates do
126+
@moduledoc """
127+
Plaid Institution Transactions Updates Status data structure.
128+
"""
129+
130+
@derive Jason.Encoder
131+
defstruct status: nil, last_status_change: nil, breakdown: nil
132+
133+
@type t :: %__MODULE__{
134+
status: String.t(),
135+
last_status_change: String.t(),
136+
breakdown: Plaid.Institutions.Institution.Status.TransactionsUpdates.Breakdown.t()
137+
}
138+
139+
defmodule Breakdown do
140+
@moduledoc """
141+
Plaid Institution Transaction Updates Breakdown Status data structure.
142+
"""
143+
144+
@derive Jason.Encoder
145+
defstruct refresh_interval: nil,
146+
success: nil,
147+
error_plaid: nil,
148+
error_institution: nil
149+
150+
@type t :: %__MODULE__{
151+
refresh_interval: String.t(),
152+
success: number(),
153+
error_plaid: number(),
154+
error_institution: number()
155+
}
156+
end
157+
end
158+
159+
defmodule Auth do
160+
@moduledoc """
161+
Plaid Institution Auth Status data structure.
162+
"""
163+
164+
@derive Jason.Encoder
165+
defstruct status: nil, last_status_change: nil, breakdown: nil
166+
167+
@type t :: %__MODULE__{
168+
status: String.t(),
169+
last_status_change: String.t(),
170+
breakdown: Plaid.Institutions.Institution.Status.Auth.Breakdown.t()
171+
}
172+
173+
defmodule Breakdown do
174+
@moduledoc """
175+
Plaid Institution Auth Breakdown Status data structure.
176+
"""
177+
178+
@derive Jason.Encoder
179+
defstruct success: nil, error_plaid: nil, error_institution: nil
180+
181+
@type t :: %__MODULE__{
182+
success: number(),
183+
error_plaid: number(),
184+
error_institution: number()
185+
}
186+
end
187+
end
188+
189+
defmodule Balance do
190+
@moduledoc """
191+
Plaid Institution Balance Status data structure.
192+
"""
193+
194+
@derive Jason.Encoder
195+
defstruct status: nil, last_status_change: nil, breakdown: nil
196+
197+
@type t :: %__MODULE__{
198+
status: String.t(),
199+
last_status_change: String.t(),
200+
breakdown: Plaid.Institutions.Institution.Status.Balance.Breakdown.t()
201+
}
202+
203+
defmodule Breakdown do
204+
@moduledoc """
205+
Plaid Institution Balance Breakdown Status data structure.
206+
"""
207+
208+
@derive Jason.Encoder
209+
defstruct success: nil, error_plaid: nil, error_institution: nil
210+
211+
@type t :: %__MODULE__{
212+
success: number(),
213+
error_plaid: number(),
214+
error_institution: number()
215+
}
216+
end
217+
end
218+
219+
defmodule Identity do
220+
@moduledoc """
221+
Plaid Institution Identity Status data structure.
222+
"""
223+
224+
@derive Jason.Encoder
225+
defstruct status: nil, last_status_change: nil, breakdown: nil
226+
227+
@type t :: %__MODULE__{
228+
status: String.t(),
229+
last_status_change: String.t(),
230+
breakdown: Plaid.Institutions.Institution.Status.Identity.Breakdown.t()
231+
}
232+
233+
defmodule Breakdown do
234+
@moduledoc """
235+
Plaid Institution Identity Breakdown Status data structure.
236+
"""
237+
238+
@derive Jason.Encoder
239+
defstruct success: nil, error_plaid: nil, error_institution: nil
240+
241+
@type t :: %__MODULE__{
242+
success: number(),
243+
error_plaid: number(),
244+
error_institution: number()
245+
}
246+
end
247+
end
101248
end
102249
end
103250

@@ -120,12 +267,21 @@ defmodule Plaid.Institutions do
120267

121268
@doc """
122269
Gets an institution by id.
270+
271+
Parameters
272+
```
273+
"ins_109512"
274+
275+
OR
276+
277+
%{institution_id: "ins_109512", options: %{include_optional_metadata: true, include_status: false}}
278+
```
123279
"""
124-
@spec get_by_id(String.t(), config | nil) ::
280+
@spec get_by_id(String.t() | params, config | nil) ::
125281
{:ok, Plaid.Institutions.Institution.t()} | {:error, Plaid.Error.t()}
126-
def get_by_id(id, config \\ %{}) do
282+
def get_by_id(params, config \\ %{}) do
127283
config = validate_public_key(config)
128-
params = %{institution_id: id}
284+
params = if is_binary(params), do: %{institution_id: params}, else: params
129285
endpoint = "#{@endpoint}/get_by_id"
130286

131287
make_request_with_cred(:post, endpoint, config, params)

‎lib/plaid/item.ex

+103-4
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,74 @@ defmodule Plaid.Item do
1414
institution_id: nil,
1515
item_id: nil,
1616
webhook: nil,
17-
request_id: nil
17+
consent_expiration_time: nil,
18+
request_id: nil,
19+
status: nil
1820

1921
@type t :: %__MODULE__{
2022
available_products: [String.t()],
2123
billed_products: [String.t()],
22-
error: String.t(),
24+
error: String.t() | nil,
2325
institution_id: String.t(),
2426
item_id: String.t(),
2527
webhook: String.t(),
26-
request_id: String.t()
28+
consent_expiration_time: String.t(),
29+
request_id: String.t(),
30+
status: Plaid.Item.Status.t()
2731
}
2832
@type params :: %{required(atom) => String.t()}
2933
@type config :: %{required(atom) => String.t()}
34+
@type service :: :dwolla | :modern_treasury
3035

3136
@endpoint :item
3237

38+
defmodule Status do
39+
@moduledoc """
40+
Plaid Item Status data structure.
41+
"""
42+
43+
@derive Jason.Encoder
44+
defstruct investments: nil,
45+
transactions: nil,
46+
last_webhook: nil
47+
48+
@type t :: %__MODULE__{
49+
investments: Plaid.Item.Status.Investments.t(),
50+
transactions: Plaid.Item.Status.Transactions.t(),
51+
last_webhook: Plaid.Item.Status.LastWebhook.t()
52+
}
53+
54+
defmodule Investments do
55+
@moduledoc """
56+
Plaid Item Status Investments data structure.
57+
"""
58+
59+
@derive Jason.Encoder
60+
defstruct last_successful_update: nil, last_failed_update: nil
61+
@type t :: %__MODULE__{last_successful_update: String.t(), last_failed_update: String.t()}
62+
end
63+
64+
defmodule Transactions do
65+
@moduledoc """
66+
Plaid Item Status Transactions data structure.
67+
"""
68+
69+
@derive Jason.Encoder
70+
defstruct last_successful_update: nil, last_failed_update: nil
71+
@type t :: %__MODULE__{last_successful_update: String.t(), last_failed_update: String.t()}
72+
end
73+
74+
defmodule LastWebhook do
75+
@moduledoc """
76+
Plaid Item Status LastWebhook data structure.
77+
"""
78+
79+
@derive Jason.Encoder
80+
defstruct sent_at: nil, code_sent: nil
81+
@type t :: %__MODULE__{sent_at: String.t(), code_sent: String.t()}
82+
end
83+
end
84+
3385
@doc """
3486
Gets an Item.
3587
@@ -184,10 +236,57 @@ defmodule Plaid.Item do
184236
{:ok, %{processor_token: "some-token", request_id: "k522f2"}}
185237
```
186238
"""
239+
@deprecated "Use create_processor_token/3 instead"
187240
@spec create_processor_token(params, config | nil) :: {:ok, map} | {:error, Plaid.Error.t()}
188241
def create_processor_token(params, config \\ %{}) do
242+
create_processor_token(params, :dwolla, config)
243+
end
244+
245+
@doc """
246+
Creates a processor token used to integrate with services external to Plaid.
247+
248+
Parameters
249+
```
250+
%{access_token: "access-env-identifier", account_id: "plaid-account-id"}
251+
```
252+
253+
Response
254+
```
255+
{:ok, %{processor_token: "some-token", request_id: "k522f2"}}
256+
```
257+
"""
258+
@spec create_processor_token(params, service, config | nil) ::
259+
{:ok, map} | {:error, Plaid.Error.t()}
260+
def create_processor_token(params, service, config) do
261+
config = validate_cred(config)
262+
endpoint = "processor/#{service_to_string(service)}/processor_token/create"
263+
264+
make_request_with_cred(:post, endpoint, config, params)
265+
|> Utils.handle_resp(@endpoint)
266+
end
267+
268+
defp service_to_string(:dwolla), do: "dwolla"
269+
defp service_to_string(:modern_treasury), do: "modern_treasury"
270+
271+
@doc """
272+
[Creates a stripe bank account token](https://stripe.com/docs/ach)
273+
used to create an authenticated funding source with Stripe.
274+
275+
Parameters
276+
```
277+
%{access_token: "access-env-identifier", account_id: "plaid-account-id"}
278+
```
279+
280+
Response
281+
```
282+
{:ok, %{stripe_bank_account_token: "btok_Kb62HbBqrrvdf8pBsAdt", request_id: "[Unique request ID]"}}
283+
```
284+
"""
285+
@spec create_stripe_bank_account_token(params, config | nil) ::
286+
{:ok, map} | {:error, Plaid.Error.t()}
287+
def create_stripe_bank_account_token(params, config \\ %{}) do
189288
config = validate_cred(config)
190-
endpoint = "processor/dwolla/processor_token/create"
289+
endpoint = "processor/stripe/bank_account_token/create"
191290

192291
make_request_with_cred(:post, endpoint, config, params)
193292
|> Utils.handle_resp(@endpoint)

‎lib/plaid/link.ex

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
defmodule Plaid.Link do
2+
@moduledoc """
3+
Functions for Plaid `link` endpoint.
4+
"""
5+
6+
import Plaid, only: [make_request_with_cred: 4, validate_cred: 1]
7+
8+
alias Plaid.Utils
9+
10+
@derive Jason.Encoder
11+
defstruct link_token: nil,
12+
expiration: nil,
13+
request_id: nil
14+
15+
@type t :: %__MODULE__{
16+
link_token: String.t(),
17+
expiration: String.t(),
18+
request_id: String.t()
19+
}
20+
@type params :: %{required(atom) => String.t()}
21+
@type config :: %{required(atom) => String.t()}
22+
23+
@endpoint :link
24+
25+
@doc """
26+
Creates a Link Token.
27+
28+
Parameters
29+
```
30+
%{
31+
client_name: "",
32+
language: "",
33+
country_codes: "",
34+
user: %{client_user_id: ""}
35+
}
36+
```
37+
"""
38+
@spec create_link_token(params, config | nil) ::
39+
{:ok, Plaid.Link.t()} | {:error, Plaid.Error.t()}
40+
def create_link_token(params, config \\ %{}) do
41+
config = validate_cred(config)
42+
endpoint = "#{@endpoint}/token/create"
43+
44+
make_request_with_cred(:post, endpoint, config, params)
45+
|> Utils.handle_resp(@endpoint)
46+
end
47+
end

‎lib/plaid/utils.ex

+186-79
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ defmodule Plaid.Utils do
1717
end
1818

1919
def handle_resp({:ok, %HTTPoison.Response{} = resp}, _endpoint) do
20-
{:error, Poison.Decode.decode(resp.body, as: %Plaid.Error{})}
20+
{:error, Poison.Decode.transform(resp.body, %{as: %Plaid.Error{}})}
2121
end
2222

2323
def handle_resp({:error, %HTTPoison.Error{} = error}, _endpoint) do
@@ -29,99 +29,193 @@ defmodule Plaid.Utils do
2929
"""
3030
@spec map_response(response, endpoint) :: any
3131
def map_response(response, :categories) do
32-
Poison.Decode.decode(response,
33-
as: %Plaid.Categories{
34-
categories: [
35-
%Plaid.Categories.Category{}
36-
]
32+
Poison.Decode.transform(
33+
response,
34+
%{
35+
as: %Plaid.Categories{
36+
categories: [
37+
%Plaid.Categories.Category{}
38+
]
39+
}
3740
}
3841
)
3942
end
4043

4144
def map_response(response, :income) do
42-
Poison.Decode.decode(response,
43-
as: %Plaid.Income{
44-
item: %Plaid.Item{},
45-
income: %Plaid.Income.Income{
46-
income_streams: [
47-
%Plaid.Income.Income.IncomeStream{}
48-
]
45+
Poison.Decode.transform(
46+
response,
47+
%{
48+
as: %Plaid.Income{
49+
item: %Plaid.Item{},
50+
income: %Plaid.Income.Income{
51+
income_streams: [
52+
%Plaid.Income.Income.IncomeStream{}
53+
]
54+
}
4955
}
5056
}
5157
)
5258
end
5359

5460
def map_response(response, :institutions) do
55-
Poison.Decode.decode(response,
56-
as: %Plaid.Institutions{
57-
institutions: [
58-
%Plaid.Institutions.Institution{
59-
credentials: [%Plaid.Institutions.Institution.Credentials{}]
60-
}
61-
]
61+
Poison.Decode.transform(
62+
response,
63+
%{
64+
as: %Plaid.Institutions{
65+
institutions: [
66+
%Plaid.Institutions.Institution{
67+
credentials: [%Plaid.Institutions.Institution.Credentials{}],
68+
status: %Plaid.Institutions.Institution.Status{
69+
item_logins: %Plaid.Institutions.Institution.Status.ItemLogins{
70+
breakdown: %Plaid.Institutions.Institution.Status.ItemLogins.Breakdown{}
71+
},
72+
transactions_updates: %Plaid.Institutions.Institution.Status.TransactionsUpdates{
73+
breakdown:
74+
%Plaid.Institutions.Institution.Status.TransactionsUpdates.Breakdown{}
75+
},
76+
auth: %Plaid.Institutions.Institution.Status.Auth{
77+
breakdown: %Plaid.Institutions.Institution.Status.Auth.Breakdown{}
78+
},
79+
balance: %Plaid.Institutions.Institution.Status.Balance{
80+
breakdown: %Plaid.Institutions.Institution.Status.Balance.Breakdown{}
81+
},
82+
identity: %Plaid.Institutions.Institution.Status.Identity{
83+
breakdown: %Plaid.Institutions.Institution.Status.Identity.Breakdown{}
84+
}
85+
}
86+
}
87+
]
88+
}
6289
}
6390
)
6491
end
6592

6693
def map_response(%{"institution" => institution} = response, :institution) do
6794
new_response = response |> Map.take(["request_id"]) |> Map.merge(institution)
6895

69-
Poison.Decode.decode(new_response,
70-
as: %Plaid.Institutions.Institution{
71-
credentials: [%Plaid.Institutions.Institution.Credentials{}]
96+
Poison.Decode.transform(
97+
new_response,
98+
%{
99+
as: %Plaid.Institutions.Institution{
100+
credentials: [%Plaid.Institutions.Institution.Credentials{}],
101+
status: %Plaid.Institutions.Institution.Status{
102+
item_logins: %Plaid.Institutions.Institution.Status.ItemLogins{
103+
breakdown: %Plaid.Institutions.Institution.Status.ItemLogins.Breakdown{}
104+
},
105+
transactions_updates: %Plaid.Institutions.Institution.Status.TransactionsUpdates{
106+
breakdown: %Plaid.Institutions.Institution.Status.TransactionsUpdates.Breakdown{}
107+
},
108+
auth: %Plaid.Institutions.Institution.Status.Auth{
109+
breakdown: %Plaid.Institutions.Institution.Status.Auth.Breakdown{}
110+
},
111+
balance: %Plaid.Institutions.Institution.Status.Balance{
112+
breakdown: %Plaid.Institutions.Institution.Status.Balance.Breakdown{}
113+
},
114+
identity: %Plaid.Institutions.Institution.Status.Identity{
115+
breakdown: %Plaid.Institutions.Institution.Status.Identity.Breakdown{}
116+
}
117+
}
118+
}
72119
}
73120
)
74121
end
75122

76123
def map_response(response, :transactions) do
77-
Poison.Decode.decode(response,
78-
as: %Plaid.Transactions{
79-
accounts: [
80-
%Plaid.Accounts.Account{
81-
balances: %Plaid.Accounts.Account.Balance{}
82-
}
83-
],
84-
transactions: [
85-
%Plaid.Transactions.Transaction{
86-
location: %Plaid.Transactions.Transaction.Location{},
87-
payment_meta: %Plaid.Transactions.Transaction.PaymentMeta{}
88-
}
89-
],
90-
item: %Plaid.Item{}
124+
Poison.Decode.transform(
125+
response,
126+
%{
127+
as: %Plaid.Transactions{
128+
accounts: [
129+
%Plaid.Accounts.Account{
130+
balances: %Plaid.Accounts.Account.Balance{}
131+
}
132+
],
133+
transactions: [
134+
%Plaid.Transactions.Transaction{
135+
location: %Plaid.Transactions.Transaction.Location{},
136+
payment_meta: %Plaid.Transactions.Transaction.PaymentMeta{}
137+
}
138+
],
139+
item: %Plaid.Item{}
140+
}
91141
}
92142
)
93143
end
94144

95145
def map_response(response, :accounts) do
96-
Poison.Decode.decode(response,
97-
as: %Plaid.Accounts{
98-
accounts: [
99-
%Plaid.Accounts.Account{balances: %Plaid.Accounts.Account.Balance{}}
100-
],
101-
item: %Plaid.Item{}
146+
Poison.Decode.transform(
147+
response,
148+
%{
149+
as: %Plaid.Accounts{
150+
accounts: [
151+
%Plaid.Accounts.Account{balances: %Plaid.Accounts.Account.Balance{}}
152+
],
153+
item: %Plaid.Item{}
154+
}
102155
}
103156
)
104157
end
105158

106159
def map_response(response, :auth) do
107-
Poison.Decode.decode(response,
108-
as: %Plaid.Auth{
109-
numbers: %Plaid.Auth.Numbers{
110-
ach: [%Plaid.Auth.Numbers.ACH{}]
111-
},
112-
item: %Plaid.Item{},
113-
accounts: [
114-
%Plaid.Accounts.Account{
115-
balances: %Plaid.Accounts.Account.Balance{}
116-
}
117-
]
160+
Poison.Decode.transform(
161+
response,
162+
%{
163+
as: %Plaid.Auth{
164+
numbers: %Plaid.Auth.Numbers{
165+
ach: [%Plaid.Auth.Numbers.ACH{}],
166+
eft: [%Plaid.Auth.Numbers.EFT{}],
167+
international: [%Plaid.Auth.Numbers.International{}],
168+
bacs: [%Plaid.Auth.Numbers.BACS{}]
169+
},
170+
item: %Plaid.Item{},
171+
accounts: [
172+
%Plaid.Accounts.Account{
173+
balances: %Plaid.Accounts.Account.Balance{}
174+
}
175+
]
176+
}
177+
}
178+
)
179+
end
180+
181+
def map_response(response, :identity) do
182+
Poison.Decode.transform(
183+
response,
184+
%{
185+
as: %Plaid.Identity{
186+
item: %Plaid.Item{},
187+
accounts: [
188+
%Plaid.Accounts.Account{
189+
balances: %Plaid.Accounts.Account.Balance{},
190+
owners: [
191+
%Plaid.Accounts.Account.Owner{
192+
addresses: [%Plaid.Accounts.Account.Owner.Address{}],
193+
emails: [%Plaid.Accounts.Account.Owner.Email{}],
194+
phone_numbers: [%Plaid.Accounts.Account.Owner.PhoneNumber{}]
195+
}
196+
]
197+
}
198+
]
199+
}
118200
}
119201
)
120202
end
121203

122204
def map_response(%{"item" => item} = response, :item) do
123-
new_response = response |> Map.take(["request_id"]) |> Map.merge(item)
124-
Poison.Decode.decode(new_response, as: %Plaid.Item{})
205+
new_response = response |> Map.take(["request_id", "status"]) |> Map.merge(item)
206+
207+
Poison.Decode.transform(
208+
new_response,
209+
%{
210+
as: %Plaid.Item{
211+
status: %Plaid.Item.Status{
212+
investments: %Plaid.Item.Status.Investments{},
213+
transactions: %Plaid.Item.Status.Transactions{},
214+
last_webhook: %Plaid.Item.Status.LastWebhook{}
215+
}
216+
}
217+
}
218+
)
125219
end
126220

127221
def map_response(%{"new_access_token" => _} = response, :item) do
@@ -173,38 +267,51 @@ defmodule Plaid.Utils do
173267
end
174268

175269
def map_response(response, :"investments/holdings") do
176-
Poison.Decode.decode(response,
177-
as: %Plaid.Investments.Holdings{
178-
accounts: [
179-
%Plaid.Accounts.Account{
180-
balances: %Plaid.Accounts.Account.Balance{}
181-
}
182-
],
183-
securities: [%Plaid.Investments.Security{}],
184-
holdings: [%Plaid.Investments.Holdings.Holding{}],
185-
item: %Plaid.Item{}
270+
Poison.Decode.transform(
271+
response,
272+
%{
273+
as: %Plaid.Investments.Holdings{
274+
accounts: [
275+
%Plaid.Accounts.Account{
276+
balances: %Plaid.Accounts.Account.Balance{}
277+
}
278+
],
279+
securities: [%Plaid.Investments.Security{}],
280+
holdings: [%Plaid.Investments.Holdings.Holding{}],
281+
item: %Plaid.Item{}
282+
}
186283
}
187284
)
188285
end
189286

190287
def map_response(response, :"investments/transactions") do
191-
Poison.Decode.decode(response,
192-
as: %Plaid.Investments.Transactions{
193-
accounts: [
194-
%Plaid.Accounts.Account{
195-
balances: %Plaid.Accounts.Account.Balance{}
196-
}
197-
],
198-
securities: [%Plaid.Investments.Security{}],
199-
investment_transactions: [%Plaid.Investments.Transactions.Transaction{}],
200-
item: %Plaid.Item{}
288+
Poison.Decode.transform(
289+
response,
290+
%{
291+
as: %Plaid.Investments.Transactions{
292+
accounts: [
293+
%Plaid.Accounts.Account{
294+
balances: %Plaid.Accounts.Account.Balance{}
295+
}
296+
],
297+
securities: [%Plaid.Investments.Security{}],
298+
investment_transactions: [%Plaid.Investments.Transactions.Transaction{}],
299+
item: %Plaid.Item{}
300+
}
201301
}
202302
)
203303
end
204304

205-
def map_response(response, :webhook_verification_key) do
206-
Poison.Decode.decode(response,
207-
as: %Plaid.Webhook.WebHookVerificationKey{}
305+
def map_response(response, :link) do
306+
Poison.Decode.transform(
307+
response,
308+
%{
309+
as: %Plaid.Link{}
310+
}
208311
)
209312
end
313+
314+
def map_response(response, :webhook_verification_key) do
315+
Poison.Decode.transform(response, %{as: %Plaid.WebhookVerificationKey{}})
316+
end
210317
end

‎lib/plaid/webhook_verification_key.ex

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
defmodule Plaid.WebhookVerificationKey do
2+
@moduledoc """
3+
Functions for Plaid `webhook_verification_key` endpoint.
4+
"""
5+
6+
import Plaid, only: [make_request_with_cred: 4, validate_cred: 1]
7+
8+
alias Plaid.Utils
9+
10+
@derive Jason.Encoder
11+
defstruct key: %{},
12+
request_id: nil
13+
14+
@type t :: %__MODULE__{
15+
key: map(),
16+
request_id: String.t()
17+
}
18+
@type params :: %{required(atom) => String.t()}
19+
@type config :: %{required(atom) => String.t()}
20+
21+
@endpoint :webhook_verification_key
22+
23+
@doc """
24+
Gets a webhook verification key (JWK).
25+
26+
Parameters
27+
```
28+
%{
29+
key_id: "The key ID (kid) from the webhook's JWT header."
30+
}
31+
```
32+
"""
33+
@spec get(params, config | nil) ::
34+
{:ok, Plaid.Link.t()} | {:error, Plaid.Error.t()}
35+
def get(params, config \\ %{}) do
36+
config = validate_cred(config)
37+
endpoint = "#{@endpoint}/get"
38+
39+
make_request_with_cred(:post, endpoint, config, params)
40+
|> Utils.handle_resp(@endpoint)
41+
end
42+
end

‎mix.exs

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ defmodule Plaid.Mixfile do
88
def project do
99
[
1010
app: :plaid,
11-
version: "1.7.2",
11+
version: "2.2.0",
1212
description: @description,
1313
elixir: "~> 1.5",
1414
elixirc_paths: elixirc_paths(Mix.env()),
@@ -31,13 +31,13 @@ defmodule Plaid.Mixfile do
3131
defp deps do
3232
[
3333
{:httpoison, "~> 1.4"},
34-
{:poison, "~> 3.0"},
34+
{:poison, "~> 4.0"},
3535
{:jason, "~> 1.1"},
3636
{:joken, "~> 2.0"},
3737
{:bypass, "~> 0.8", only: [:test]},
3838
{:credo, "~> 0.5", only: [:dev], runtime: false},
3939
{:excoveralls, "~> 0.6", only: [:test]},
40-
{:ex_doc, "~> 0.19", only: [:dev], runtime: false},
40+
{:ex_doc, "~> 0.21", only: [:dev], runtime: false},
4141
{:dialyxir, "~> 1.0.0-rc.6", only: [:dev, :test], runtime: false}
4242
]
4343
end
@@ -54,7 +54,8 @@ defmodule Plaid.Mixfile do
5454

5555
defp docs do
5656
[
57-
extras: ["parameters.md"]
57+
extras: ["parameters.md"],
58+
logo: "plaid-logo.png"
5859
]
5960
end
6061
end

‎mix.lock

+26-24
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
%{
22
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
3-
"bypass": {:hex, :bypass, "0.8.1", "16d409e05530ece4a72fabcf021a3e5c7e15dcc77f911423196a0c551f2a15ca", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "61fc89e67e448785905d35b2b06a3a36ae0cf0857c343fd65c753af42406f31a"},
4-
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "01d479edba0569a7b7a2c8bf923feeb6dc6a358edc2965ef69aea9ba288bb243"},
5-
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "f4763bbe08233eceed6f24bc4fcc8d71c17cfeafa6439157c57349aa1bb4f17c"},
6-
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm", "db622da03aa039e6366ab953e31186cc8190d32905e33788a1acb22744e6abd2"},
7-
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "dcd1d45626f6a02abeef3fc424eaf101b05a851d3cceb9535b8ea3e14c3c17e6"},
8-
"dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "49496d63267bc1a4614ffd5f67c45d9fc3ea62701a6797975bc98bc156d2763f"},
9-
"earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm", "f8b8820099caf0d5e72ae6482d2b0da96f213cbbe2b5b2191a37966e119eaa27"},
10-
"erlex": {:hex, :erlex, "0.2.1", "cee02918660807cbba9a7229cae9b42d1c6143b768c781fa6cee1eaf03ad860b", [:mix], [], "hexpm", "df65aa8e1e926941982b208f5957158a52b21fbba06ba8141fff2b8c5ce87574"},
11-
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "dc87f778d8260da0189a622f62790f6202af72f2f3dee6e78d91a18dd2fcd137"},
12-
"excoveralls": {:hex, :excoveralls, "0.9.0", "dd597ccf119aa0be0c1c6681215df588596397833b8dd010fe3d1a48090f3119", [:mix], [{:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8d53498b4950f5a04fccd4d203c964b78e77d6459efc1dc418347628f9672788"},
13-
"hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "ed15491f324aa0e95647dca8ef4340418dac479d1204d57e455d52dcfba3f705"},
14-
"httpoison": {:hex, :httpoison, "1.4.0", "e0b3c2ad6fa573134e42194d13e925acfa8f89d138bc621ffb7b1989e6d22e73", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "37b6f39cb92136ee72276f4bf4da81495e7935d720c3a15daf1553953153e3f7"},
15-
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
16-
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"},
3+
"bypass": {:hex, :bypass, "0.9.0", "4cedcd326eeec497e0090a73d351cbd0f11e39329ddf9095931b03da9b6dc417", [:mix], [{:cowboy, "~> 1.0 or ~> 2.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ede64318ac7bff9126d83a962a1605f4fd407fa0d1a6c844b3b012773d6beadd"},
4+
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
5+
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
6+
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
7+
"credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "539596b6774069260d5938aa73042a2f5157e1c0215aa35f5a53d83889546d14"},
8+
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"},
9+
"earmark_parser": {:hex, :earmark_parser, "1.4.10", "6603d7a603b9c18d3d20db69921527f82ef09990885ed7525003c7fe7dc86c56", [:mix], [], "hexpm", "8e2d5370b732385db2c9b22215c3f59c84ac7dda7ed7e544d7c459496ae519c0"},
10+
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
11+
"ex_doc": {:hex, :ex_doc, "0.22.5", "d2312c99f52cb1f98371e28f68259b4e0cee13d0122baedaf74a8e44e299a35b", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "d6ac685fd0b226805db96f7fbb384cbf8e01903ddb902ff66c9b189104705cae"},
12+
"excoveralls": {:hex, :excoveralls, "0.13.1", "b9f1697f7c9e0cfe15d1a1d737fb169c398803ffcbc57e672aa007e9fd42864c", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b4bb550e045def1b4d531a37fb766cbbe1307f7628bf8f0414168b3f52021cce"},
13+
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
14+
"httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "975cc87c845a103d3d1ea1ccfd68a2700c211a434d8428b10c323dc95dc5b980"},
15+
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
16+
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
1717
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
1818
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
19-
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d7152ff93f2eac07905f510dfa03397134345ba4673a00fbf7119bab98632940"},
20-
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "4a36dd2d0d5c5f98d95b3f410d7071cd661d5af310472229dd0e92161f168a44"},
19+
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
20+
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
2121
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
22-
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm", "5e839994289d60326aa86020c4fbd9c6938af188ecddab2579f07b66cd665328"},
23-
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm", "7a4c8e1115a2732a67d7624e28cf6c9f30c66711a9e92928e745c255887ba465"},
24-
"nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm", "ebb595e19456a72786db6dcd370d320350cb624f0b6203fcc7e23161d49b0ffb"},
22+
"mime": {:hex, :mime, "1.4.0", "5066f14944b470286146047d2f73518cf5cca82f8e4815cf35d196b58cf07c47", [:mix], [], "hexpm", "75fa42c4228ea9a23f70f123c74ba7cece6a03b1fd474fe13f6a7a85c6ea4ff6"},
23+
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
24+
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
2525
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
26-
"plug": {:hex, :plug, "1.5.1", "1ff35bdecfb616f1a2b1c935ab5e4c47303f866cb929d2a76f0541e553a58165", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.3", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "aa312d5df0f815eed879aaa77543534e21c78ca4a7e983083698fc190796fd9c"},
27-
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
28-
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm", "6e56493a862433fccc3aca3025c946d6720d8eedf6e3e6fb911952a7071c357f"},
29-
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm", "603561dc0fd62f4f2ea9b890f4e20e1a0d388746d6e20557cafb1b16950de88c"},
30-
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
26+
"plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"},
27+
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
28+
"poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"},
29+
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
30+
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
31+
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
32+
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
3133
}

‎plaid-logo.png

5.11 KB
Loading

‎test/lib/plaid/identity_test.exs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule Plaid.IdentityTest do
2+
use ExUnit.Case
3+
4+
import Plaid.Factory
5+
6+
setup do
7+
bypass = Bypass.open()
8+
Application.put_env(:plaid, :root_uri, "http://localhost:#{bypass.port}/")
9+
{:ok, bypass: bypass}
10+
end
11+
12+
describe "identity" do
13+
test "get/1 requests POST and returns Plaid.Identity", %{bypass: bypass} do
14+
body = http_response_body(:identity)
15+
16+
Bypass.expect(bypass, fn conn ->
17+
assert "POST" == conn.method
18+
assert "identity/get" == Enum.join(conn.path_info, "/")
19+
Plug.Conn.resp(conn, 200, Poison.encode!(body))
20+
end)
21+
22+
assert {:ok, resp} = Plaid.Identity.get(%{access_token: "my-token"})
23+
assert Plaid.Identity == resp.__struct__
24+
assert {:ok, _} = Jason.encode(resp)
25+
end
26+
end
27+
end

‎test/lib/plaid/institutions_test.exs

+19-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ defmodule Plaid.InstitutionsTest do
2424
assert {:ok, _} = Jason.encode(resp)
2525
end
2626

27-
test "get_by_id/1 requests POST and returns Plaid.Institutions.Institution", %{bypass: bypass} do
27+
test "get_by_id/1 requests POST with string params and returns Plaid.Institutions.Institution",
28+
%{bypass: bypass} do
2829
body = http_response_body(:institution)
2930

3031
Bypass.expect(bypass, fn conn ->
@@ -38,6 +39,23 @@ defmodule Plaid.InstitutionsTest do
3839
assert {:ok, _} = Jason.encode(resp)
3940
end
4041

42+
test "get_by_id/1 requests POST with map params and returns Plaid.Institutions.Institution",
43+
%{bypass: bypass} do
44+
body = http_response_body(:institution)
45+
46+
Bypass.expect(bypass, fn conn ->
47+
assert "POST" == conn.method
48+
assert "institutions/get_by_id" == Enum.join(conn.path_info, "/")
49+
Plug.Conn.resp(conn, 200, Poison.encode!(body))
50+
end)
51+
52+
assert {:ok, resp} =
53+
Plaid.Institutions.get_by_id(%{institution_id: "ins_109512", options: %{}})
54+
55+
assert Plaid.Institutions.Institution == resp.__struct__
56+
assert {:ok, _} = Jason.encode(resp)
57+
end
58+
4159
test "search/1 requests POST and returns Plaid.Institutions", %{bypass: bypass} do
4260
body = http_response_body(:institutions)
4361

‎test/lib/plaid/item_test.exs

+25-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ defmodule Plaid.ItemTest do
2222
assert {:ok, resp} = Plaid.Item.get(%{access_token: "my-token"})
2323
assert Plaid.Item == resp.__struct__
2424
assert {:ok, _} = Jason.encode(resp)
25+
assert Plaid.Item.Status == resp.status.__struct__
26+
assert Plaid.Item.Status.Investments == resp.status.investments.__struct__
27+
assert Plaid.Item.Status.Transactions == resp.status.transactions.__struct__
28+
assert Plaid.Item.Status.LastWebhook == resp.status.last_webhook.__struct__
29+
assert resp.status.last_webhook.code_sent == body["status"]["last_webhook"]["code_sent"]
2530
end
2631

2732
test "exchange_public_token/1 requests POST and returns map", %{bypass: bypass} do
@@ -114,7 +119,7 @@ defmodule Plaid.ItemTest do
114119
assert resp.deleted == body["deleted"]
115120
end
116121

117-
test "create_processor_token/1 request POST and returns token", %{bypass: bypass} do
122+
test "deprecated: create_processor_token/1 request POST and returns token", %{bypass: bypass} do
118123
body = http_response_body(:processor_token)
119124

120125
Bypass.expect(bypass, fn conn ->
@@ -129,6 +134,25 @@ defmodule Plaid.ItemTest do
129134
assert resp.processor_token
130135
end
131136

137+
test "create_processor_token/3 request POST and returns token", %{bypass: bypass} do
138+
body = http_response_body(:processor_token)
139+
140+
Bypass.expect(bypass, fn conn ->
141+
assert "POST" == conn.method
142+
assert "processor/dwolla/processor_token/create" == Enum.join(conn.path_info, "/")
143+
Plug.Conn.resp(conn, 200, Poison.encode!(body))
144+
end)
145+
146+
assert {:ok, resp} =
147+
Plaid.Item.create_processor_token(
148+
%{access_token: "token", account_id: "id"},
149+
:dwolla,
150+
%{}
151+
)
152+
153+
assert resp.processor_token
154+
end
155+
132156
test "create_stripe_bank_account_token/1 request POST and returns token", %{bypass: bypass} do
133157
body = http_response_body(:stripe_bank_account_token)
134158

‎test/lib/plaid/link_test.exs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
defmodule Plaid.LinkTest do
2+
use ExUnit.Case
3+
4+
import Plaid.Factory
5+
6+
setup do
7+
bypass = Bypass.open()
8+
Application.put_env(:plaid, :root_uri, "http://localhost:#{bypass.port}/")
9+
{:ok, bypass: bypass}
10+
end
11+
12+
test "create_link_token/1 request POST and returns map", %{bypass: bypass} do
13+
body = http_response_body(:create_link_token)
14+
15+
Bypass.expect(bypass, fn conn ->
16+
assert "POST" == conn.method
17+
assert "link/token/create" == Enum.join(conn.path_info, "/")
18+
Plug.Conn.resp(conn, 200, Poison.encode!(body))
19+
end)
20+
21+
assert {:ok, resp} =
22+
Plaid.Link.create_link_token(%{
23+
client_name: "My App",
24+
country_codes: ["US"],
25+
language: "en",
26+
products: ["transactions"],
27+
user: %{client_user_id: "UNIQUE_USER_ID"},
28+
webhook: "https://sample.webhook.com"
29+
})
30+
31+
assert resp.link_token == body["link_token"]
32+
assert resp.expiration == body["expiration"]
33+
assert resp.request_id == body["request_id"]
34+
end
35+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
defmodule Plaid.WebhookVerificationKeyTest do
2+
use ExUnit.Case
3+
4+
import Plaid.Factory
5+
6+
setup do
7+
bypass = Bypass.open()
8+
Application.put_env(:plaid, :root_uri, "http://localhost:#{bypass.port}/")
9+
{:ok, bypass: bypass}
10+
end
11+
12+
test "get/1 request POST and returns map", %{bypass: bypass} do
13+
body = http_response_body(:webhook_verification_key)
14+
15+
Bypass.expect(bypass, fn conn ->
16+
assert "POST" == conn.method
17+
assert "webhook_verification_key/get" == Enum.join(conn.path_info, "/")
18+
Plug.Conn.resp(conn, 200, Poison.encode!(body))
19+
end)
20+
21+
assert {:ok, resp} =
22+
Plaid.WebhookVerificationKey.get(%{
23+
key_id: "bfbd5111-8e33-4643-8ced-b2e642a72f3c"
24+
})
25+
26+
assert resp.key == body["key"]
27+
assert resp.request_id == body["request_id"]
28+
end
29+
end

‎test/support/factory.ex

+821-30
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.