fake_braintree 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,7 +9,7 @@ module FakeBraintree
9
9
  end
10
10
 
11
11
  def gzipped_response(status_code, uncompressed_content)
12
- [status_code, { "Content-Encoding" => "gzip" }, gzip(uncompressed_content)]
12
+ [status_code, { 'Content-Encoding' => 'gzip' }, gzip(uncompressed_content)]
13
13
  end
14
14
 
15
15
  def md5(content)
@@ -5,11 +5,12 @@ module FakeBraintree
5
5
  attr_reader :id
6
6
 
7
7
  def initialize(params, merchant_id)
8
- hash, query = *params[:tr_data].split("|", 2)
9
- @transparent_data = Rack::Utils.parse_query(query)
8
+ hash, query = *params[:tr_data].split('|', 2)
9
+ @transparent_data = Rack::Utils.parse_nested_query(query)
10
10
  @merchant_id = merchant_id
11
11
  @id = create_id(@merchant_id)
12
12
  @params = params
13
+ @kind = @transparent_data['kind']
13
14
  end
14
15
 
15
16
  def url
@@ -17,17 +18,28 @@ module FakeBraintree
17
18
  end
18
19
 
19
20
  def confirm
20
- Customer.new(@params["customer"], {:merchant_id => @merchant_id}).create
21
+ if @kind == 'create_customer'
22
+ Customer.new(@params['customer'], {:merchant_id => @merchant_id}).create
23
+ elsif @kind == 'create_payment_method'
24
+ credit_card_options = {:merchant_id => @merchant_id}
25
+ credit_card_options.merge!(@transparent_data['credit_card'].fetch('options', {}))
26
+
27
+ credit_card_options.symbolize_keys!
28
+ CreditCard.new(@params['credit_card'].merge(@transparent_data['credit_card']), credit_card_options).create
29
+ end
21
30
  end
22
31
 
23
32
  private
24
33
 
25
34
  def uri
26
- URI.parse(@transparent_data["redirect_url"]).merge("?#{base_query}&hash=#{hash(base_query)}")
35
+ uri = URI.parse(@transparent_data['redirect_url'])
36
+ merged_query = [uri.query, base_query].compact.join('&')
37
+ uri.query = "#{merged_query}&hash=#{hash(merged_query)}"
38
+ uri
27
39
  end
28
40
 
29
41
  def base_query
30
- "http_status=200&id=#{@id}&kind=create_customer"
42
+ "http_status=200&id=#{@id}&kind=#{@kind}"
31
43
  end
32
44
 
33
45
  def hash(string)
@@ -3,12 +3,8 @@ class FakeBraintree::Registry
3
3
  clear!
4
4
  end
5
5
 
6
- attr_accessor :customers,
7
- :subscriptions,
8
- :failures,
9
- :transactions,
10
- :redirects,
11
- :credit_cards
6
+ attr_accessor :customers,:subscriptions, :failures, :transactions, :redirects,
7
+ :credit_cards
12
8
 
13
9
  def clear!
14
10
  @customers = {}
@@ -10,20 +10,25 @@ module FakeBraintree
10
10
  include Helpers
11
11
 
12
12
  helpers do
13
- def hash_from_request_body_with_key(request, key)
14
- Hash.from_xml(request.body).delete(key)
13
+ def hash_from_request_body_with_key(key)
14
+ value = Hash.from_xml(request.body).delete(key)
15
+ if value.is_a?(String) # This happens if there isn't actually nested data under `key`
16
+ {}
17
+ else
18
+ value
19
+ end
15
20
  end
16
21
  end
17
22
 
18
23
  # Braintree::Customer.create
19
- post "/merchants/:merchant_id/customers" do
20
- customer_hash = hash_from_request_body_with_key(request, "customer")
24
+ post '/merchants/:merchant_id/customers' do
25
+ customer_hash = hash_from_request_body_with_key('customer')
21
26
  options = {:merchant_id => params[:merchant_id]}
22
27
  Customer.new(customer_hash, options).create
23
28
  end
24
29
 
25
30
  # Braintree::Customer.find
26
- get "/merchants/:merchant_id/customers/:id" do
31
+ get '/merchants/:merchant_id/customers/:id' do
27
32
  customer = FakeBraintree.registry.customers[params[:id]]
28
33
  if customer
29
34
  gzipped_response(200, customer.to_xml(:root => 'customer'))
@@ -33,28 +38,28 @@ module FakeBraintree
33
38
  end
34
39
 
35
40
  # Braintree::Customer.update
36
- put "/merchants/:merchant_id/customers/:id" do
37
- customer_hash = hash_from_request_body_with_key(request, "customer")
41
+ put '/merchants/:merchant_id/customers/:id' do
42
+ customer_hash = hash_from_request_body_with_key('customer')
38
43
  options = {:id => params[:id], :merchant_id => params[:merchant_id]}
39
44
  Customer.new(customer_hash, options).update
40
45
  end
41
46
 
42
47
  # Braintree::Customer.delete
43
- delete "/merchants/:merchant_id/customers/:id" do
48
+ delete '/merchants/:merchant_id/customers/:id' do
44
49
  customer_hash = {}
45
50
  options = {:id => params[:id], :merchant_id => params[:merchant_id]}
46
51
  Customer.new(customer_hash, options).delete
47
52
  end
48
53
 
49
54
  # Braintree::Subscription.create
50
- post "/merchants/:merchant_id/subscriptions" do
51
- subscription_hash = hash_from_request_body_with_key(request, "subscription")
55
+ post '/merchants/:merchant_id/subscriptions' do
56
+ subscription_hash = hash_from_request_body_with_key('subscription')
52
57
  options = {:merchant_id => params[:merchant_id]}
53
58
  Subscription.new(subscription_hash, options).create
54
59
  end
55
60
 
56
61
  # Braintree::Subscription.find
57
- get "/merchants/:merchant_id/subscriptions/:id" do
62
+ get '/merchants/:merchant_id/subscriptions/:id' do
58
63
  subscription = FakeBraintree.registry.subscriptions[params[:id]]
59
64
  if subscription
60
65
  gzipped_response(200, subscription.to_xml(:root => 'subscription'))
@@ -64,98 +69,111 @@ module FakeBraintree
64
69
  end
65
70
 
66
71
  # Braintree::Subscription.update
67
- put "/merchants/:merchant_id/subscriptions/:id" do
68
- subscription_hash = hash_from_request_body_with_key(request, "subscription")
72
+ put '/merchants/:merchant_id/subscriptions/:id' do
73
+ subscription_hash = hash_from_request_body_with_key('subscription')
69
74
  options = {:id => params[:id], :merchant_id => params[:merchant_id]}
70
75
  Subscription.new(subscription_hash, options).update
71
76
  end
72
77
 
73
78
  # Braintree::Subscription.cancel
74
- put "/merchants/:merchant_id/subscriptions/:id/cancel" do
75
- updates = {"status" => Braintree::Subscription::Status::Canceled}
79
+ put '/merchants/:merchant_id/subscriptions/:id/cancel' do
80
+ updates = {'status' => Braintree::Subscription::Status::Canceled}
76
81
  options = {:id => params[:id], :merchant_id => params[:merchant_id]}
77
82
  Subscription.new(updates, options).update
78
83
  end
79
84
 
80
85
  # Braintree::CreditCard.find
81
- get "/merchants/:merchant_id/payment_methods/:credit_card_token" do
86
+ get '/merchants/:merchant_id/payment_methods/:credit_card_token' do
82
87
  credit_card = FakeBraintree.registry.credit_cards[params[:credit_card_token]]
83
- gzipped_response(200, credit_card.to_xml(:root => "credit_card"))
84
- end
85
-
86
- post "/merchants/:merchant_id/payment_methods" do
87
- opts = {
88
- :token => md5("#{Time.now}#{rand}"),
89
- :merchant_id => params[:merchant_id]
90
- }
91
- data = hash_from_request_body_with_key(request, 'credit_card')
92
- if data[:token]
93
- opts[:token] = data[:token]
88
+ if credit_card
89
+ gzipped_response(200, credit_card.to_xml(:root => 'credit_card'))
90
+ else
91
+ gzipped_response(404, {})
94
92
  end
95
- credit_card = CreditCard.new(data, opts)
96
- FakeBraintree.registry.credit_cards[opts[:token]] = credit_card
97
- gzipped_response(200, credit_card.to_xml)
98
93
  end
99
94
 
100
95
  # Braintree::CreditCard.update
101
- put "/merchants/:merchant_id/payment_methods/:credit_card_token" do
96
+ put '/merchants/:merchant_id/payment_methods/:credit_card_token' do
102
97
  credit_card = FakeBraintree.registry.credit_cards[params[:credit_card_token]]
103
- updates = hash_from_request_body_with_key(request, "credit_card")
98
+ updates = hash_from_request_body_with_key('credit_card')
104
99
  options = {:token => params[:credit_card_token], :merchant_id => params[:merchant_id]}
105
100
  CreditCard.new(updates, options).update
106
101
  end
107
102
 
103
+ # Braintree::CreditCard.create
104
+ post '/merchants/:merchant_id/payment_methods' do
105
+ credit_card_hash = hash_from_request_body_with_key('credit_card')
106
+ options = {:merchant_id => params[:merchant_id]}
107
+
108
+ if credit_card_hash['options']
109
+ options.merge!(credit_card_hash.delete('options')).symbolize_keys!
110
+ end
111
+
112
+ CreditCard.new(credit_card_hash, options).create
113
+ end
114
+
108
115
  # Braintree::Transaction.sale
109
116
  # Braintree::CreditCard.sale
110
- post "/merchants/:merchant_id/transactions" do
117
+ post '/merchants/:merchant_id/transactions' do
111
118
  if FakeBraintree.decline_all_cards?
112
119
  gzipped_response(422, FakeBraintree.create_failure.to_xml(:root => 'api_error_response'))
113
120
  else
114
- transaction = hash_from_request_body_with_key(request, "transaction")
115
- transaction_id = md5("#{params[:merchant_id]}#{Time.now.to_f}")
116
- transaction_response = {"id" => transaction_id, "amount" => transaction["amount"]}
121
+ transaction = hash_from_request_body_with_key('transaction')
122
+ transaction_id = md5("#{params[:merchant_id]}#{Time.now.to_f}")
123
+ options = transaction["options"] || {}
124
+ status = "authorized"
125
+ if options.fetch("submit_for_settlement", false) == true
126
+ status = "submitted_for_settlement"
127
+ end
128
+ transaction_response = {'id' => transaction_id, 'amount' => transaction['amount'], 'status' => status}
117
129
  FakeBraintree.registry.transactions[transaction_id] = transaction_response
118
- gzipped_response(200, transaction_response.to_xml(:root => "transaction"))
130
+ gzipped_response(200, transaction_response.to_xml(:root => 'transaction'))
119
131
  end
120
132
  end
121
133
 
122
134
  # Braintree::Transaction.find
123
- get "/merchants/:merchant_id/transactions/:transaction_id" do
135
+ get '/merchants/:merchant_id/transactions/:transaction_id' do
124
136
  transaction = FakeBraintree.registry.transactions[params[:transaction_id]]
125
137
  if transaction
126
- gzipped_response(200, transaction.to_xml(:root => "transaction"))
138
+ gzipped_response(200, transaction.to_xml(:root => 'transaction'))
127
139
  else
128
140
  gzipped_response(404, {})
129
141
  end
130
142
  end
131
143
 
132
144
  # Braintree::Transaction.refund
133
- # Braintree::CreditCard.refund
134
- post "/merchants/:merchant_id/transactions/:transaction_id/refund" do
135
- if FakeBraintree.decline_all_cards?
136
- gzipped_response(422, FakeBraintree.create_failure.to_xml(:root => 'api_error_response'))
137
- else
138
- transaction = hash_from_request_body_with_key(request, "transaction")
139
- transaction_id = md5("#{params[:merchant_id]}#{Time.now.to_f}")
140
- transaction_response = {"id" => transaction_id, "amount" => transaction["amount"], "type" => "credit"}
141
- FakeBraintree.registry.transactions[transaction_id] = transaction_response
142
- gzipped_response(200, transaction_response.to_xml(:root => "transaction"))
143
- end
145
+ post '/merchants/:merchant_id/transactions/:transaction_id/refund' do
146
+ transaction = hash_from_request_body_with_key('transaction')
147
+ transaction_id = md5('#{params[:merchant_id]}#{Time.now.to_f}')
148
+ transaction_response = {'id' => transaction_id, 'amount' => transaction['amount'], 'type' => 'credit'}
149
+ FakeBraintree.registry.transactions[transaction_id] = transaction_response
150
+ gzipped_response(200, transaction_response.to_xml(:root => 'transaction'))
151
+ end
152
+
153
+ # Braintree::Transaction.void
154
+ put '/merchants/:merchant_id/transactions/:transaction_id/void' do
155
+ transaction = FakeBraintree.registry.transactions[params[:transaction_id]]
156
+ transaction_response = {'id' => transaction['id'],
157
+ 'type' => transaction['sale'],
158
+ 'amount' => transaction['amount'],
159
+ 'status' => Braintree::Transaction::Status::Voided}
160
+ FakeBraintree.registry.transactions[transaction['id']] = transaction_response
161
+ gzipped_response(200, transaction_response.to_xml(:root => 'transaction'))
144
162
  end
145
163
 
146
164
  # Braintree::TransparentRedirect.url
147
- post "/merchants/:merchant_id/transparent_redirect_requests" do
165
+ post '/merchants/:merchant_id/transparent_redirect_requests' do
148
166
  if params[:tr_data]
149
167
  redirect = Redirect.new(params, params[:merchant_id])
150
168
  FakeBraintree.registry.redirects[redirect.id] = redirect
151
169
  redirect to(redirect.url), 303
152
170
  else
153
- [422, { "Content-Type" => "text/html" }, ["Invalid submission"]]
171
+ [422, { 'Content-Type' => 'text/html' }, ['Invalid submission']]
154
172
  end
155
173
  end
156
174
 
157
175
  # Braintree::TransparentRedirect.confirm
158
- post "/merchants/:merchant_id/transparent_redirect_requests/:id/confirm" do
176
+ post '/merchants/:merchant_id/transparent_redirect_requests/:id/confirm' do
159
177
  redirect = FakeBraintree.registry.redirects[params[:id]]
160
178
  redirect.confirm
161
179
  end
@@ -3,8 +3,10 @@ module FakeBraintree
3
3
  include Helpers
4
4
 
5
5
  def initialize(subscription_hash_from_params, options)
6
- @subscription_hash = subscription_hash_from_params.merge("merchant_id" => options[:merchant_id],
7
- "id" => options[:id])
6
+ @subscription_hash = subscription_hash_from_params.merge(
7
+ 'merchant_id' => options[:merchant_id],
8
+ 'id' => options[:id]
9
+ )
8
10
  set_subscription_id
9
11
  set_subscription_status
10
12
  end
@@ -26,10 +28,12 @@ module FakeBraintree
26
28
  private
27
29
 
28
30
  def subscription_hash
29
- @subscription_hash.merge({"transactions" => [],
30
- "add_ons" => added_add_ons,
31
- "discounts" => added_discounts,
32
- "next_billing_date" => braintree_formatted_date(1.month.from_now)})
31
+ @subscription_hash.merge(
32
+ 'transactions' => [],
33
+ 'add_ons' => added_add_ons,
34
+ 'discounts' => added_discounts,
35
+ 'next_billing_date' => braintree_formatted_date(1.month.from_now)
36
+ )
33
37
  end
34
38
 
35
39
  def update_existing_subscription(updates)
@@ -38,7 +42,7 @@ module FakeBraintree
38
42
  end
39
43
 
40
44
  def create_subscription_with(new_subscription_hash)
41
- FakeBraintree.registry.subscriptions[new_subscription_hash["id"]] = new_subscription_hash
45
+ FakeBraintree.registry.subscriptions[new_subscription_hash['id']] = new_subscription_hash
42
46
  end
43
47
 
44
48
  def subscription_from_registry
@@ -54,31 +58,31 @@ module FakeBraintree
54
58
  end
55
59
 
56
60
  def added_add_ons
57
- if @subscription_hash["add_ons"].is_a?(Hash) && @subscription_hash["add_ons"]["add"]
58
- @subscription_hash["add_ons"]["add"].map { |add_on| { "id" => add_on["inherited_from_id"] } }
61
+ if @subscription_hash['add_ons'].is_a?(Hash) && @subscription_hash['add_ons']['add']
62
+ @subscription_hash['add_ons']['add'].map { |add_on| { 'id' => add_on['inherited_from_id'] } }
59
63
  else
60
64
  []
61
65
  end
62
66
  end
63
67
 
64
68
  def added_discounts
65
- if @subscription_hash["discounts"].is_a?(Hash) && @subscription_hash["discounts"]["add"]
66
- @subscription_hash["discounts"]["add"].map { |discount| { "id" => discount["inherited_from_id"] } }
69
+ if @subscription_hash['discounts'].is_a?(Hash) && @subscription_hash['discounts']['add']
70
+ @subscription_hash['discounts']['add'].map { |discount| { 'id' => discount['inherited_from_id'] } }
67
71
  else
68
72
  []
69
73
  end
70
74
  end
71
75
 
72
76
  def set_subscription_id
73
- @subscription_hash["id"] ||= generate_new_subscription_id
77
+ @subscription_hash['id'] ||= generate_new_subscription_id
74
78
  end
75
79
 
76
80
  def set_subscription_status
77
- @subscription_hash["status"] ||= active_status
81
+ @subscription_hash['status'] ||= active_status
78
82
  end
79
83
 
80
84
  def subscription_id
81
- subscription_hash["id"]
85
+ subscription_hash['id']
82
86
  end
83
87
 
84
88
  def generate_new_subscription_id
@@ -86,7 +90,7 @@ module FakeBraintree
86
90
  end
87
91
 
88
92
  def payment_method_token
89
- @subscription_hash["payment_method_token"]
93
+ @subscription_hash['payment_method_token']
90
94
  end
91
95
 
92
96
  def active_status
@@ -94,7 +98,7 @@ module FakeBraintree
94
98
  end
95
99
 
96
100
  def response_for_created_subscription(hash)
97
- gzipped_response(201, hash.to_xml(:root => "subscription"))
101
+ gzipped_response(201, hash.to_xml(:root => 'subscription'))
98
102
  end
99
103
 
100
104
  def response_for_subscription_not_found
@@ -102,7 +106,7 @@ module FakeBraintree
102
106
  end
103
107
 
104
108
  def response_for_created_subscription(hash)
105
- gzipped_response(201, hash.to_xml(:root => "subscription"))
109
+ gzipped_response(201, hash.to_xml(:root => 'subscription'))
106
110
  end
107
111
  end
108
112
  end
@@ -1,3 +1,3 @@
1
1
  module FakeBraintree
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Braintree::CreditCard.find" do
4
- it "gets the correct credit card" do
3
+ describe 'Braintree::CreditCard.find' do
4
+ it 'gets the correct credit card' do
5
5
  credit_card = Braintree::CreditCard.find(token)
6
6
 
7
7
  credit_card.last_4.should == TEST_CC_NUMBER[-4,4]
@@ -14,30 +14,82 @@ describe "Braintree::CreditCard.find" do
14
14
  let(:token) { braintree_credit_card_token(TEST_CC_NUMBER, [month, year].join('/')) }
15
15
  end
16
16
 
17
- describe "Braintree::CreditCard.create" do
18
- let(:month) { '04' }
19
- let(:year) { '2016' }
20
- let(:token) { braintree_credit_card_token(TEST_CC_NUMBER, [month, year].join('/')) }
21
- it "successfully creates card with valid data" do
22
- result = Braintree::CreditCard.create :token => token,
23
- :number => TEST_CC_NUMBER
17
+ describe 'Braintree::CreditCard.sale' do
18
+ it 'successfully creates a sale' do
19
+ result = Braintree::CreditCard.sale(cc_token, :amount => 10.00)
24
20
  result.should be_success
25
-
26
- Braintree::CreditCard.find(token).should be
21
+ Braintree::Transaction.find(result.transaction.id).should be
27
22
  end
28
23
  end
29
24
 
30
- describe "Braintree::CreditCard.sale" do
31
- it "successfully creates a sale" do
32
- result = Braintree::CreditCard.sale(cc_token, :amount => 10.00)
25
+
26
+ describe 'Braintree::CreditCard.create' do
27
+ def build_credit_card_hash
28
+ {
29
+ :customer_id => @customer && @customer.id,
30
+ :number => '4111111111111111',
31
+ :cvv => '123',
32
+ :token => 'token',
33
+ :expiration_date => '07/2020',
34
+ :billing_address => {
35
+ :postal_code => '94110'
36
+ },
37
+ :options => {
38
+ :make_default => true
39
+ }
40
+ }
41
+ end
42
+
43
+ it 'allows creating a credit card without a customer' do
44
+ result = Braintree::CreditCard.create(build_credit_card_hash)
33
45
  result.should be_success
34
- Braintree::Transaction.find(result.transaction.id).should be
46
+ Braintree::CreditCard.find('token').should_not be_nil
47
+ end
48
+
49
+ context 'with a customer' do
50
+ before do
51
+ @customer = Braintree::Customer.create.customer
52
+ end
53
+
54
+ it 'fails to create a credit card if decline_all_cards is set' do
55
+ FakeBraintree.decline_all_cards!
56
+ result = Braintree::CreditCard.create(build_credit_card_hash)
57
+ result.should_not be_success
58
+ expect { Braintree::CreditCard.find('token') }.to raise_exception Braintree::NotFoundError
59
+ FakeBraintree.clear!
60
+ end
61
+
62
+ it 'fails to create a credit card if verify_all_cards is set and card is invalid' do
63
+ FakeBraintree.verify_all_cards!
64
+ result = Braintree::CreditCard.create(build_credit_card_hash.merge(:number => '12345'))
65
+ result.should_not be_success
66
+ expect { Braintree::CreditCard.find('token') }.to raise_exception Braintree::NotFoundError
67
+ FakeBraintree.verify_all_cards = false
68
+ end
69
+
70
+ it 'successfully creates a credit card' do
71
+ result = Braintree::CreditCard.create(build_credit_card_hash)
72
+ result.should be_success
73
+ Braintree::Customer.find(@customer.id).credit_cards.last.token.should == 'token'
74
+ Braintree::Customer.find(@customer.id).credit_cards.last.default?.should be_true
75
+ end
76
+
77
+ it 'only allows one credit card to be default' do
78
+ result = Braintree::CreditCard.create(build_credit_card_hash)
79
+ result.should be_success
80
+ result = Braintree::CreditCard.create(build_credit_card_hash)
81
+ result.should be_success
82
+ # Reload the customer
83
+ @customer = Braintree::Customer.find(@customer.id)
84
+ @customer.credit_cards.select {|c| c.default?}.length.should == 1
85
+ @customer.credit_cards.length.should == 2
86
+ end
35
87
  end
36
88
  end
37
89
 
38
- describe "Braintree::CreditCard.update" do
39
- it "successfully updates the credit card" do
40
- new_expiration_date = "08/2012"
90
+ describe 'Braintree::CreditCard.update' do
91
+ it 'successfully updates the credit card' do
92
+ new_expiration_date = '08/2012'
41
93
  token = cc_token
42
94
 
43
95
  result = Braintree::CreditCard.update(token, :expiration_date => new_expiration_date)
@@ -45,7 +97,7 @@ describe "Braintree::CreditCard.update" do
45
97
  Braintree::CreditCard.find(token).expiration_date.should == new_expiration_date
46
98
  end
47
99
 
48
- it "raises an error for a nonexistent credit card" do
49
- lambda { Braintree::CreditCard.update("foo", {:number => TEST_CC_NUMBER}) }.should raise_error(Braintree::NotFoundError)
100
+ it 'raises an error for a nonexistent credit card' do
101
+ lambda { Braintree::CreditCard.update('foo', {:number => TEST_CC_NUMBER}) }.should raise_error(Braintree::NotFoundError)
50
102
  end
51
103
  end