Skip to content

Commit 695d05c

Browse files
davidstumpalexmamonchik
authored andcommitted
Implements ability to test external accounts
This commit adds the ability to test external accounts for Managed Accounts. Managed Accounts can have external accounts (very similar to sources for Customers) that can be either a bank account or a debit card. These changes include all of the mocks, specs, helpers and logic needed to start testing these external accounts.
1 parent 55c7d4c commit 695d05c

File tree

8 files changed

+277
-7
lines changed

8 files changed

+277
-7
lines changed

Diff for: lib/stripe_mock.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
require 'stripe_mock/api/webhooks'
3939

4040
require 'stripe_mock/request_handlers/helpers/bank_account_helpers.rb'
41+
require 'stripe_mock/request_handlers/helpers/external_account_helpers.rb'
4142
require 'stripe_mock/request_handlers/helpers/card_helpers.rb'
4243
require 'stripe_mock/request_handlers/helpers/charge_helpers.rb'
4344
require 'stripe_mock/request_handlers/helpers/coupon_helpers.rb'
@@ -47,7 +48,7 @@
4748
require 'stripe_mock/request_handlers/validators/param_validators.rb'
4849

4950
require 'stripe_mock/request_handlers/accounts.rb'
50-
require 'stripe_mock/request_handlers/balance.rb'
51+
require 'stripe_mock/request_handlers/external_accounts.rb'
5152
require 'stripe_mock/request_handlers/balance_transactions.rb'
5253
require 'stripe_mock/request_handlers/charges.rb'
5354
require 'stripe_mock/request_handlers/cards.rb'

Diff for: lib/stripe_mock/instance.rb

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def self.handler_for_method_url(method_url)
2020
@@handlers.find {|h| method_url =~ h[:route] }
2121
end
2222

23+
include StripeMock::RequestHandlers::ExternalAccounts
2324
include StripeMock::RequestHandlers::Accounts
2425
include StripeMock::RequestHandlers::Balance
2526
include StripeMock::RequestHandlers::BalanceTransactions
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module StripeMock
2+
module RequestHandlers
3+
module ExternalAccounts
4+
5+
def ExternalAccounts.included(klass)
6+
klass.add_handler 'get /v1/accounts/(.*)/external_accounts', :retrieve_external_accounts
7+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts', :create_external_account
8+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)/verify', :verify_external_account
9+
klass.add_handler 'get /v1/accounts/(.*)/external_accounts/(.*)', :retrieve_external_account
10+
klass.add_handler 'delete /v1/accounts/(.*)/external_accounts/(.*)', :delete_external_account
11+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)', :update_external_account
12+
end
13+
14+
def create_external_account(route, method_url, params, headers)
15+
route =~ method_url
16+
add_external_account_to(:account, $1, params, accounts)
17+
end
18+
19+
def retrieve_external_accounts(route, method_url, params, headers)
20+
route =~ method_url
21+
retrieve_object_cards(:account, $1, accounts)
22+
end
23+
24+
def retrieve_external_account(route, method_url, params, headers)
25+
route =~ method_url
26+
account = assert_existence :account, $1, accounts[$1]
27+
28+
assert_existence :card, $2, get_card(account, $2)
29+
end
30+
31+
def delete_external_account(route, method_url, params, headers)
32+
route =~ method_url
33+
delete_card_from(:account, $1, $2, accounts)
34+
end
35+
36+
def update_external_account(route, method_url, params, headers)
37+
route =~ method_url
38+
account = assert_existence :account, $1, accounts[$1]
39+
40+
card = assert_existence :card, $2, get_card(account, $2)
41+
card.merge!(params)
42+
card
43+
end
44+
45+
def verify_external_account(route, method_url, params, headers)
46+
route =~ method_url
47+
account = assert_existence :account, $1, accounts[$1]
48+
49+
external_account = assert_existence :bank_account, $2, verify_bank_account(account, $2)
50+
external_account
51+
end
52+
53+
end
54+
end
55+
end

Diff for: lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module RequestHandlers
33
module Helpers
44

55
def verify_bank_account(object, bank_account_id, class_name='Customer')
6-
bank_accounts = object[:bank_accounts] || object[:sources]
6+
bank_accounts = object[:external_accounts] || object[:bank_accounts] || object[:sources]
77
bank_account = bank_accounts[:data].find{|acc| acc[:id] == bank_account_id }
88
return if bank_account.nil?
99
bank_account['status'] = 'verified'

Diff for: lib/stripe_mock/request_handlers/helpers/card_helpers.rb

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module RequestHandlers
33
module Helpers
44

55
def get_card(object, card_id, class_name='Customer')
6-
cards = object[:cards] || object[:sources]
6+
cards = object[:cards] || object[:sources] || object[:external_accounts]
77
card = cards[:data].find{|cc| cc[:id] == card_id }
88
if card.nil?
99
if class_name == 'Recipient'
@@ -36,7 +36,7 @@ def add_source_to_object(type, source, object, replace_current=false)
3636

3737
def add_card_to_object(type, card, object, replace_current=false)
3838
card[type] = object[:id]
39-
cards_or_sources = object[:cards] || object[:sources]
39+
cards_or_sources = object[:cards] || object[:sources] || object[:external_accounts]
4040

4141
is_customer = object.has_key?(:sources)
4242

@@ -58,7 +58,7 @@ def add_card_to_object(type, card, object, replace_current=false)
5858

5959
def retrieve_object_cards(type, type_id, objects)
6060
resource = assert_existence type, type_id, objects[type_id]
61-
cards = resource[:cards] || resource[:sources]
61+
cards = resource[:cards] || resource[:sources] || resource[:external_accounts]
6262

6363
Data.mock_list_object(cards[:data])
6464
end
@@ -69,7 +69,7 @@ def delete_card_from(type, type_id, card_id, objects)
6969
assert_existence :card, card_id, get_card(resource, card_id)
7070

7171
card = { id: card_id, deleted: true }
72-
cards_or_sources = resource[:cards] || resource[:sources]
72+
cards_or_sources = resource[:cards] || resource[:sources] || resource[:external_accounts]
7373
cards_or_sources[:data].reject!{|cc|
7474
cc[:id] == card[:id]
7575
}
@@ -103,7 +103,7 @@ def add_source_to(type, type_id, params, objects)
103103
def add_card_to(type, type_id, params, objects)
104104
resource = assert_existence type, type_id, objects[type_id]
105105

106-
card = card_from_params(params[:card] || params[:source])
106+
card = card_from_params(params[:card] || params[:source] || params[:external_accounts])
107107
add_card_to_object(type, card, resource)
108108
end
109109

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module StripeMock
2+
module RequestHandlers
3+
module Helpers
4+
5+
def add_external_account_to(type, type_id, params, objects)
6+
resource = assert_existence type, type_id, objects[type_id]
7+
8+
source =
9+
if params[:card]
10+
card_from_params(params[:card])
11+
elsif params[:bank_account]
12+
get_bank_by_token(params[:bank_account])
13+
else
14+
begin
15+
get_card_by_token(params[:external_account])
16+
rescue Stripe::InvalidRequestError
17+
get_bank_by_token(params[:external_account])
18+
end
19+
end
20+
add_external_account_to_object(type, source, resource)
21+
end
22+
23+
def add_external_account_to_object(type, source, object, replace_current=false)
24+
source[type] = object[:id]
25+
accounts = object[:external_accounts]
26+
27+
if replace_current && accounts[:data]
28+
accounts[:data].delete_if {|source| source[:id] == object[:default_source]}
29+
object[:default_source] = source[:id]
30+
accounts[:data] = [source]
31+
else
32+
accounts[:total_count] = (accounts[:total_count] || 0) + 1
33+
(accounts[:data] ||= []) << source
34+
end
35+
object[:default_source] = source[:id] if object[:default_source].nil?
36+
37+
source
38+
end
39+
40+
end
41+
end
42+
end
+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
require 'spec_helper'
2+
3+
shared_examples 'External Account API' do
4+
5+
it 'creates/returns a bank when using account.external_accounts.create given a bank token' do
6+
account = Stripe::Account.create(id: 'test_account', managed: true, country: "US")
7+
bank_token = stripe_helper.generate_bank_token(last4: "1123", exp_month: 11, exp_year: 2099)
8+
bank = account.external_accounts.create(external_account: bank_token)
9+
10+
expect(bank.account).to eq('test_account')
11+
expect(bank.last4).to eq("1123")
12+
expect(bank.exp_month).to eq(11)
13+
expect(bank.exp_year).to eq(2099)
14+
15+
account = Stripe::Account.retrieve('test_account')
16+
expect(account.external_accounts.count).to eq(1)
17+
bank = account.external_accounts.first
18+
expect(bank.account).to eq('test_account')
19+
expect(bank.last4).to eq("1123")
20+
expect(bank.exp_month).to eq(11)
21+
expect(bank.exp_year).to eq(2099)
22+
end
23+
24+
it 'creates/returns a bank when using account.external_accounts.create given bank params' do
25+
account = Stripe::Account.create(id: 'test_account', managed: true, country: "US")
26+
bank = account.external_accounts.create(external_account: {
27+
object: 'bank_account',
28+
account_number: '000123456789',
29+
routing_number: '110000000',
30+
country: 'US',
31+
currency: 'usd'
32+
})
33+
34+
expect(bank.account).to eq('test_account')
35+
expect(bank.routing_number).to eq('110000000')
36+
expect(bank.country).to eq('US')
37+
expect(bank.currency).to eq('usd')
38+
39+
account = Stripe::Account.retrieve('test_account')
40+
expect(account.external_accounts.count).to eq(1)
41+
bank = account.external_accounts.first
42+
expect(bank.account).to eq('test_account')
43+
expect(bank.routing_number).to eq('110000000')
44+
expect(bank.country).to eq('US')
45+
expect(bank.currency).to eq('usd')
46+
end
47+
48+
it "creates a single bank with a generated bank token", :live => true do
49+
account = Stripe::Account.create(managed: true, country: "US")
50+
expect(account.external_accounts.count).to eq 0
51+
52+
account.external_accounts.create external_account: stripe_helper.generate_bank_token
53+
# Yes, stripe-ruby does not actually add the new bank to the account instance
54+
expect(account.external_accounts.count).to eq 0
55+
56+
account2 = Stripe::Account.retrieve(account.id)
57+
expect(account2.external_accounts.count).to eq 1
58+
end
59+
60+
describe "retrieval and deletion with accounts" do
61+
let!(:account) { Stripe::Account.create(id: 'test_account', managed: true, country: "US") }
62+
let!(:bank_token) { stripe_helper.generate_bank_token(last4: "1123", exp_month: 11, exp_year: 2099) }
63+
let!(:bank) { account.external_accounts.create(external_account: bank_token) }
64+
65+
it "can retrieve all account's banks" do
66+
retrieved = account.external_accounts.all
67+
expect(retrieved.count).to eq(1)
68+
end
69+
70+
it "retrieves an account bank" do
71+
retrieved = account.external_accounts.retrieve(bank.id)
72+
expect(retrieved.to_s).to eq(bank.to_s)
73+
end
74+
75+
it "retrieves an account's bank after re-fetching the account" do
76+
retrieved = Stripe::Account.retrieve(account.id).external_accounts.retrieve(bank.id)
77+
expect(retrieved.id).to eq bank.id
78+
end
79+
80+
it "deletes an accounts bank" do
81+
bank.delete
82+
retrieved_acct = Stripe::Account.retrieve(account.id)
83+
expect(retrieved_acct.external_accounts.data).to be_empty
84+
end
85+
86+
context "deletion when the user has two external accounts" do
87+
let!(:bank_token_2) { stripe_helper.generate_bank_token(last4: "1123", exp_month: 11, exp_year: 2099) }
88+
let!(:bank_2) { account.external_accounts.create(external_account: bank_token_2) }
89+
90+
it "has just one bank anymore" do
91+
bank.delete
92+
retrieved_acct = Stripe::Account.retrieve(account.id)
93+
expect(retrieved_acct.external_accounts.data.count).to eq 1
94+
expect(retrieved_acct.external_accounts.data.first.id).to eq bank_2.id
95+
end
96+
end
97+
end
98+
99+
describe "Errors", :live => true do
100+
it "throws an error when the account does not have the retrieving bank id" do
101+
account = Stripe::Account.create(managed: true, country: "US")
102+
bank_id = "bank_123"
103+
expect { account.external_accounts.retrieve(bank_id) }.to raise_error {|e|
104+
expect(e).to be_a Stripe::InvalidRequestError
105+
expect(e.message).to match /no.*source/i
106+
expect(e.message).to include bank_id
107+
expect(e.param).to eq 'id'
108+
expect(e.http_status).to eq 404
109+
}
110+
end
111+
end
112+
113+
context "update bank" do
114+
let!(:account) { Stripe::Account.create(id: 'test_account', managed: true, country: "US") }
115+
let!(:bank_token) { stripe_helper.generate_bank_token(last4: "1123", exp_month: 11, exp_year: 2099) }
116+
let!(:bank) { account.external_accounts.create(external_account: bank_token) }
117+
118+
it "updates the bank" do
119+
exp_month = 10
120+
exp_year = 2098
121+
122+
bank.exp_month = exp_month
123+
bank.exp_year = exp_year
124+
bank.save
125+
126+
retrieved = account.external_accounts.retrieve(bank.id)
127+
128+
expect(retrieved.exp_month).to eq(exp_month)
129+
expect(retrieved.exp_year).to eq(exp_year)
130+
end
131+
end
132+
133+
context "retrieve multiple banks" do
134+
135+
it "retrieves a list of multiple banks" do
136+
account = Stripe::Account.create(id: 'test_account', managed: true, country: "US")
137+
138+
bank_token = stripe_helper.generate_bank_token(last4: "1123", exp_month: 11, exp_year: 2099)
139+
bank1 = account.external_accounts.create(external_accout: bank_token)
140+
bank_token = stripe_helper.generate_bank_token(last4: "1124", exp_month: 12, exp_year: 2098)
141+
bank2 = account.external_accounts.create(external_account: bank_token)
142+
143+
account = Stripe::Account.retrieve('test_account')
144+
145+
list = account.external_accounts.all
146+
147+
expect(list.object).to eq("list")
148+
expect(list.count).to eq(2)
149+
expect(list.data.length).to eq(2)
150+
151+
expect(list.data.first.object).to eq("bank_account")
152+
expect(list.data.first.to_hash).to eq(bank1.to_hash)
153+
154+
expect(list.data.last.object).to eq("bank_account")
155+
expect(list.data.last.to_hash).to eq(bank2.to_hash)
156+
end
157+
158+
it "retrieves an empty list if there's no subscriptions" do
159+
Stripe::Account.create(id: 'no_banks', managed: true, country: "US")
160+
account = Stripe::Account.retrieve('no_banks')
161+
162+
list = account.external_accounts.all
163+
164+
expect(list.object).to eq("list")
165+
expect(list.count).to eq(0)
166+
expect(list.data.length).to eq(0)
167+
end
168+
end
169+
170+
end

Diff for: spec/support/stripe_examples.rb

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def it_behaves_like_stripe(&block)
1313
it_behaves_like 'Card API', &block
1414
it_behaves_like 'Charge API', &block
1515
it_behaves_like 'Bank API', &block
16+
it_behaves_like 'External Account API', &block
1617
it_behaves_like 'Coupon API', &block
1718
it_behaves_like 'Customer API', &block
1819
it_behaves_like 'Dispute API', &block

0 commit comments

Comments
 (0)