fake_braintree 0.2.1 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/Gemfile +1 -1
- data/LICENSE +1 -4
- data/NEWS.md +62 -0
- data/README.md +3 -1
- data/Rakefile +4 -4
- data/fake_braintree.gemspec +8 -8
- data/lib/fake_braintree.rb +36 -26
- data/lib/fake_braintree/credit_card.rb +57 -6
- data/lib/fake_braintree/customer.rb +31 -23
- data/lib/fake_braintree/helpers.rb +1 -1
- data/lib/fake_braintree/redirect.rb +17 -5
- data/lib/fake_braintree/registry.rb +2 -6
- data/lib/fake_braintree/sinatra_app.rb +71 -53
- data/lib/fake_braintree/subscription.rb +21 -17
- data/lib/fake_braintree/version.rb +1 -1
- data/spec/fake_braintree/credit_card_spec.rb +72 -20
- data/spec/fake_braintree/customer_spec.rb +86 -50
- data/spec/fake_braintree/registry_spec.rb +6 -7
- data/spec/fake_braintree/subscription_spec.rb +34 -28
- data/spec/fake_braintree/transaction_spec.rb +76 -19
- data/spec/fake_braintree/transparent_redirect_spec.rb +73 -26
- data/spec/fake_braintree_spec.rb +33 -33
- data/spec/spec_helper.rb +1 -1
- data/spec/support/braintree_helpers.rb +1 -1
- data/spec/support/matchers/clear_hash_when_cleared.rb +2 -2
- data/spec/support/matchers/have_accessor_for.rb +1 -1
- metadata +7 -7
- data/Changelog.md +0 -41
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
|
2
1
|
LICENSE
|
3
2
|
|
4
3
|
The MIT License
|
5
4
|
|
6
|
-
Copyright (c) 2011-
|
5
|
+
Copyright (c) 2011-2013 thoughtbot, inc.
|
7
6
|
|
8
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
8
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -22,5 +21,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
21
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
22
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
23
|
THE SOFTWARE.
|
25
|
-
|
26
|
-
|
data/NEWS.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# 0.3
|
2
|
+
|
3
|
+
* Braintree::Transaction.void updates the existing sale transaction instead of
|
4
|
+
creating a new transaction of type Braintree::Transaction::Status::Voided
|
5
|
+
|
6
|
+
* Preserve `redirect_url` query parameters for transparent redirect (#36)
|
7
|
+
|
8
|
+
* Transactions can be submitted for settlement by passing an options hash to
|
9
|
+
Braintree::Transaction.create as shown in the [Braintree documentation](https://www.braintreepayments.com/docs/ruby/transactions/create#full_example) (#31)
|
10
|
+
|
11
|
+
* When a customer is created with a credit card
|
12
|
+
(`Braintree::Customer.create(:credit_card => ...`), that credit card is set as
|
13
|
+
the customer's default card.
|
14
|
+
|
15
|
+
# 0.2.1
|
16
|
+
* Fake refunds via `Braintree::Transaction.refund` and
|
17
|
+
`Braintree::CreditCard.refund`.
|
18
|
+
* Create credit cards via `Braintree::CreditCard.create(:token => token,
|
19
|
+
:number => TEST_CC_NUMBER)`
|
20
|
+
* Depend on Thin instead of Mongrel (fixes NotImplementedError).
|
21
|
+
|
22
|
+
# 0.2.0
|
23
|
+
* Generated transactions (from `FakeBraintree.generate_transaction`) now include
|
24
|
+
the amount.
|
25
|
+
* `Braintree::Customer.update` will reject updates that contain credit cards that
|
26
|
+
have been marked as a failure in the registry.
|
27
|
+
|
28
|
+
# 0.1.1
|
29
|
+
* `Braintree::CreditCard.update` now works
|
30
|
+
|
31
|
+
# 0.1.0
|
32
|
+
* `FakeBraintree.{customers, transactions, failures, subscriptions, redirects}`
|
33
|
+
are now accessed via `FakeBraintree.registry`. For example,
|
34
|
+
`FakeBraintree.customers` is now `FakeBraintree.registry.customers`
|
35
|
+
* `FakeBraintree.credit_card_from_token` is now `FakeBraintree.registry.credit_card_from_token`
|
36
|
+
* The server code (it intercepts calls to Braintree) now lives in FakeBraintree::Server
|
37
|
+
* `Braintree::Customer.create` will use the provided customer ID instead of
|
38
|
+
overwriting it (#15).
|
39
|
+
* `Braintree::Subscription.cancel` now works
|
40
|
+
|
41
|
+
# 0.0.6
|
42
|
+
* Flesh out the README
|
43
|
+
* Add support for transparent redirect
|
44
|
+
* Add basic support for adding add-ons
|
45
|
+
* Add basic support for adding discounts
|
46
|
+
* Add support for `Braintree::Customer.update`
|
47
|
+
* Add support for `Braintree::Customer.delete`
|
48
|
+
* Add support for `Braintree::Subscription.delete`
|
49
|
+
* Lots of internal refactorings
|
50
|
+
|
51
|
+
# 0.0.5
|
52
|
+
* Add support for `Braintree::Customer.find`
|
53
|
+
|
54
|
+
# 0.0.4
|
55
|
+
* Allow for very basic card verification
|
56
|
+
|
57
|
+
# 0.0.3
|
58
|
+
* Ensure `FakeBraintree.log_file_path` directory exists
|
59
|
+
* The `FakeBraintree.log_file_path` attribute can now be read (it could only be set before)
|
60
|
+
* Clear log when `FakeBraintree.clear!` is called
|
61
|
+
* Correctly handle nonexistent subscriptions when using
|
62
|
+
`Braintree::Subscription.find`
|
data/README.md
CHANGED
@@ -33,6 +33,8 @@ of them (yet).
|
|
33
33
|
|
34
34
|
### Transaction
|
35
35
|
* `Braintree::Transaction.sale`
|
36
|
+
* `Braintree::Transaction.refund`
|
37
|
+
* `Braintree::Transaction.void`
|
36
38
|
|
37
39
|
### TransparentRedirect
|
38
40
|
* `Braintree::TransparentRedirect.url`
|
@@ -172,4 +174,4 @@ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
|
172
174
|
License
|
173
175
|
-------
|
174
176
|
|
175
|
-
Fake Braintree is Copyright © 2011-
|
177
|
+
Fake Braintree is Copyright © 2011-2013 thoughtbot. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
|
data/Rakefile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec)
|
6
6
|
|
7
|
-
desc
|
7
|
+
desc 'Run specs'
|
8
8
|
task :default => [:spec]
|
data/fake_braintree.gemspec
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path(
|
3
|
-
require
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'fake_braintree/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
6
|
+
s.name = 'fake_braintree'
|
7
7
|
s.version = FakeBraintree::VERSION
|
8
|
-
s.authors = [
|
9
|
-
s.email = [
|
10
|
-
s.homepage =
|
8
|
+
s.authors = ['thoughtbot, inc.']
|
9
|
+
s.email = ['gabe@thoughtbot.com', 'ben@thoughtbot.com']
|
10
|
+
s.homepage = ''
|
11
11
|
s.summary = %q{A fake Braintree that you can run integration tests against}
|
12
12
|
s.description = %q{A fake Braintree that you can run integration tests against}
|
13
13
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
-
s.require_paths = [
|
17
|
+
s.require_paths = ['lib']
|
18
18
|
|
19
19
|
s.add_dependency 'capybara'
|
20
20
|
s.add_dependency 'activesupport'
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency 'braintree', '~> 2.5'
|
24
24
|
s.add_dependency 'thin'
|
25
25
|
|
26
|
-
s.add_development_dependency 'rspec', '~> 2.
|
26
|
+
s.add_development_dependency 'rspec', '~> 2.12.0'
|
27
27
|
s.add_development_dependency 'bourne', '~> 1.0'
|
28
28
|
s.add_development_dependency 'timecop', '~> 0.3.5'
|
29
29
|
s.add_development_dependency 'spork', '~> 0.9.0.rc9'
|
data/lib/fake_braintree.rb
CHANGED
@@ -46,26 +46,32 @@ module FakeBraintree
|
|
46
46
|
|
47
47
|
def self.failure_response(card_number = nil)
|
48
48
|
failure = registry.failures[card_number] || {}
|
49
|
-
failure[
|
50
|
-
|
51
|
-
{
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
failure['errors'] ||= { 'errors' => [] }
|
50
|
+
|
51
|
+
{
|
52
|
+
'message' => failure['message'],
|
53
|
+
'verification' => {
|
54
|
+
'status' => failure['status'],
|
55
|
+
'processor_response_text' => failure['message'],
|
56
|
+
'processor_response_code' => failure['code'],
|
57
|
+
'gateway_rejection_reason' => 'cvv',
|
58
|
+
'cvv_response_code' => failure['code']
|
59
|
+
},
|
60
|
+
'errors' => failure['errors'],
|
61
|
+
'params' => {}
|
62
|
+
}
|
59
63
|
end
|
60
64
|
|
61
65
|
def self.create_failure
|
62
66
|
{
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
'message' => 'Do Not Honor',
|
68
|
+
'verification' => {
|
69
|
+
'status' => 'processor_declined',
|
70
|
+
'processor_response_text' => 'Do Not Honor',
|
71
|
+
'processor_response_code' => '2000'
|
72
|
+
},
|
73
|
+
'errors' => { 'errors' => [] },
|
74
|
+
'params' => {}
|
69
75
|
}
|
70
76
|
end
|
71
77
|
|
@@ -82,23 +88,27 @@ module FakeBraintree
|
|
82
88
|
end
|
83
89
|
|
84
90
|
def self.generate_transaction(options = {})
|
85
|
-
history_item = {
|
86
|
-
|
87
|
-
|
91
|
+
history_item = {
|
92
|
+
'timestamp' => Time.now,
|
93
|
+
'amount' => options[:amount],
|
94
|
+
'status' => options[:status]
|
95
|
+
}
|
88
96
|
created_at = options[:created_at] || Time.now
|
89
|
-
{
|
90
|
-
|
91
|
-
|
92
|
-
|
97
|
+
{
|
98
|
+
'status_history' => [history_item],
|
99
|
+
'subscription_id' => options[:subscription_id],
|
100
|
+
'created_at' => created_at,
|
101
|
+
'amount' => options[:amount]
|
102
|
+
}
|
93
103
|
end
|
94
104
|
|
95
105
|
private
|
96
106
|
|
97
107
|
def self.set_configuration
|
98
108
|
Braintree::Configuration.environment = :development
|
99
|
-
Braintree::Configuration.merchant_id =
|
100
|
-
Braintree::Configuration.public_key =
|
101
|
-
Braintree::Configuration.private_key =
|
109
|
+
Braintree::Configuration.merchant_id = 'xxx'
|
110
|
+
Braintree::Configuration.public_key = 'xxx'
|
111
|
+
Braintree::Configuration.private_key = 'xxx'
|
102
112
|
end
|
103
113
|
|
104
114
|
def self.boot_server
|
@@ -7,6 +7,22 @@ module FakeBraintree
|
|
7
7
|
set_expiration_month_and_year
|
8
8
|
end
|
9
9
|
|
10
|
+
def create
|
11
|
+
if valid_number?
|
12
|
+
if token.nil?
|
13
|
+
@hash['token'] = generate_token
|
14
|
+
end
|
15
|
+
FakeBraintree.registry.credit_cards[token] = @hash
|
16
|
+
if customer = FakeBraintree.registry.customers[@hash['customer_id']]
|
17
|
+
customer['credit_cards'] << @hash
|
18
|
+
update_default_card
|
19
|
+
end
|
20
|
+
response_for_updated_card
|
21
|
+
else
|
22
|
+
response_for_invalid_card
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
10
26
|
def update
|
11
27
|
if credit_card_exists_in_registry?
|
12
28
|
update_existing_credit_card
|
@@ -20,10 +36,32 @@ module FakeBraintree
|
|
20
36
|
@hash.to_xml(:root => 'credit_card')
|
21
37
|
end
|
22
38
|
|
39
|
+
def valid_number?
|
40
|
+
if FakeBraintree.decline_all_cards?
|
41
|
+
false
|
42
|
+
elsif FakeBraintree.verify_all_cards
|
43
|
+
FakeBraintree::VALID_CREDIT_CARDS.include?(@hash['number'])
|
44
|
+
else
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
23
49
|
private
|
24
50
|
|
25
51
|
def update_existing_credit_card
|
26
52
|
@hash = credit_card_from_registry.merge!(@hash)
|
53
|
+
update_default_card
|
54
|
+
end
|
55
|
+
|
56
|
+
# When updating a card that has 'default' set to true, make sure
|
57
|
+
# only one card has the flag.
|
58
|
+
def update_default_card
|
59
|
+
if @hash['default']
|
60
|
+
FakeBraintree.registry.customers[@hash['customer_id']]['credit_cards'].each do |card|
|
61
|
+
card['default'] = false
|
62
|
+
end
|
63
|
+
@hash['default'] = true
|
64
|
+
end
|
27
65
|
end
|
28
66
|
|
29
67
|
def response_for_updated_card
|
@@ -42,6 +80,13 @@ module FakeBraintree
|
|
42
80
|
gzipped_response(404, FakeBraintree.failure_response.to_xml(:root => 'api_error_response'))
|
43
81
|
end
|
44
82
|
|
83
|
+
def response_for_invalid_card
|
84
|
+
gzipped_response(422, FakeBraintree.failure_response.merge(
|
85
|
+
'params' => {:credit_card => @hash}
|
86
|
+
).
|
87
|
+
to_xml(:root => 'api_error_response'))
|
88
|
+
end
|
89
|
+
|
45
90
|
def expiration_month
|
46
91
|
expiration_date_parts[0]
|
47
92
|
end
|
@@ -52,28 +97,34 @@ module FakeBraintree
|
|
52
97
|
|
53
98
|
def set_up_credit_card(credit_card_hash_from_params, options)
|
54
99
|
@hash = {
|
55
|
-
|
56
|
-
|
100
|
+
'token' => options[:token],
|
101
|
+
'merchant_id' => options[:merchant_id],
|
102
|
+
'customer_id' => options[:customer_id],
|
103
|
+
'default' => options[:make_default]
|
57
104
|
}.merge(credit_card_hash_from_params)
|
58
105
|
end
|
59
106
|
|
60
107
|
def set_expiration_month_and_year
|
61
108
|
if expiration_month
|
62
|
-
@hash[
|
109
|
+
@hash['expiration_month'] = expiration_month
|
63
110
|
end
|
64
111
|
|
65
112
|
if expiration_year
|
66
|
-
@hash[
|
113
|
+
@hash['expiration_year'] = expiration_year
|
67
114
|
end
|
68
115
|
end
|
69
116
|
|
117
|
+
def generate_token
|
118
|
+
md5("#{@hash['number']}#{@hash['merchant_id']}")
|
119
|
+
end
|
120
|
+
|
70
121
|
def token
|
71
122
|
@hash['token']
|
72
123
|
end
|
73
124
|
|
74
125
|
def expiration_date_parts
|
75
|
-
if @hash.key?(
|
76
|
-
@hash[
|
126
|
+
if @hash.key?('expiration_date')
|
127
|
+
@hash['expiration_date'].split('/')
|
77
128
|
else
|
78
129
|
[]
|
79
130
|
end
|
@@ -4,10 +4,10 @@ module FakeBraintree
|
|
4
4
|
|
5
5
|
def initialize(customer_hash_from_params, options)
|
6
6
|
@customer_hash = {
|
7
|
-
|
8
|
-
|
9
|
-
}
|
10
|
-
|
7
|
+
'id' => options[:id],
|
8
|
+
'merchant_id' => options[:merchant_id]
|
9
|
+
}
|
10
|
+
@customer_hash.merge!(customer_hash_from_params)
|
11
11
|
set_customer_id
|
12
12
|
end
|
13
13
|
|
@@ -15,9 +15,10 @@ module FakeBraintree
|
|
15
15
|
if invalid?
|
16
16
|
response_for_invalid_card
|
17
17
|
else
|
18
|
-
credit_cards = customer_hash[
|
18
|
+
credit_cards = customer_hash['credit_cards']
|
19
19
|
create_customer_with(customer_hash)
|
20
20
|
credit_cards.each { |card| add_credit_card_to_registry(card) }
|
21
|
+
set_default_credit_card credit_cards.first
|
21
22
|
response_for_created_customer(customer_hash)
|
22
23
|
end
|
23
24
|
end
|
@@ -48,11 +49,11 @@ module FakeBraintree
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def create_customer_with(hash)
|
51
|
-
FakeBraintree.registry.customers[hash[
|
52
|
+
FakeBraintree.registry.customers[hash['id']] = hash
|
52
53
|
end
|
53
54
|
|
54
55
|
def add_credit_card_to_registry(new_credit_card_hash)
|
55
|
-
token = new_credit_card_hash[
|
56
|
+
token = new_credit_card_hash['token']
|
56
57
|
FakeBraintree.registry.credit_cards[token] = new_credit_card_hash
|
57
58
|
end
|
58
59
|
|
@@ -61,7 +62,7 @@ module FakeBraintree
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def customer_hash
|
64
|
-
@customer_hash.merge(
|
65
|
+
@customer_hash.merge('credit_cards' => generate_credit_cards_from(@customer_hash['credit_card']))
|
65
66
|
end
|
66
67
|
|
67
68
|
def customer_from_registry
|
@@ -83,11 +84,11 @@ module FakeBraintree
|
|
83
84
|
def verify_credit_card?(customer_hash_for_verification)
|
84
85
|
return true if FakeBraintree.verify_all_cards
|
85
86
|
|
86
|
-
credit_card_hash_for_verification = customer_hash_for_verification[
|
87
|
+
credit_card_hash_for_verification = customer_hash_for_verification['credit_card']
|
87
88
|
if credit_card_hash_for_verification.is_a?(Hash) &&
|
88
|
-
credit_card_hash_for_verification.key?(
|
89
|
-
options = credit_card_hash_for_verification[
|
90
|
-
options[
|
89
|
+
credit_card_hash_for_verification.key?('options')
|
90
|
+
options = credit_card_hash_for_verification['options']
|
91
|
+
options['verify_card'] == true
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
@@ -97,20 +98,27 @@ module FakeBraintree
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def credit_card_number
|
100
|
-
credit_card_hash[
|
101
|
+
credit_card_hash['number']
|
102
|
+
end
|
103
|
+
|
104
|
+
def set_default_credit_card(credit_card_hash)
|
105
|
+
if credit_card_hash
|
106
|
+
CreditCard.new(credit_card_hash, :customer_id => @customer_hash['id'], :make_default => true).update
|
107
|
+
end
|
101
108
|
end
|
102
109
|
|
103
110
|
def generate_credit_cards_from(new_credit_card_hash)
|
104
111
|
if new_credit_card_hash.present? && new_credit_card_hash.is_a?(Hash)
|
105
|
-
new_credit_card_hash[
|
106
|
-
new_credit_card_hash[
|
112
|
+
new_credit_card_hash['bin'] = new_credit_card_hash['number'][0..5]
|
113
|
+
new_credit_card_hash['last_4'] = new_credit_card_hash['number'][-4..-1]
|
114
|
+
new_credit_card_hash['token'] = credit_card_token(new_credit_card_hash)
|
107
115
|
|
108
116
|
if credit_card_expiration_month
|
109
|
-
new_credit_card_hash[
|
117
|
+
new_credit_card_hash['expiration_month'] = credit_card_expiration_month
|
110
118
|
end
|
111
119
|
|
112
120
|
if credit_card_expiration_year
|
113
|
-
new_credit_card_hash[
|
121
|
+
new_credit_card_hash['expiration_year'] = credit_card_expiration_year
|
114
122
|
end
|
115
123
|
|
116
124
|
[new_credit_card_hash]
|
@@ -128,8 +136,8 @@ module FakeBraintree
|
|
128
136
|
end
|
129
137
|
|
130
138
|
def credit_card_expiration_date
|
131
|
-
if credit_card_hash.key?(
|
132
|
-
credit_card_hash[
|
139
|
+
if credit_card_hash.key?('expiration_date')
|
140
|
+
credit_card_hash['expiration_date'].split('/')
|
133
141
|
else
|
134
142
|
[]
|
135
143
|
end
|
@@ -164,7 +172,7 @@ module FakeBraintree
|
|
164
172
|
end
|
165
173
|
|
166
174
|
def customer_id
|
167
|
-
customer_hash[
|
175
|
+
customer_hash['id']
|
168
176
|
end
|
169
177
|
|
170
178
|
def has_credit_card?
|
@@ -172,15 +180,15 @@ module FakeBraintree
|
|
172
180
|
end
|
173
181
|
|
174
182
|
def credit_card_hash
|
175
|
-
@customer_hash[
|
183
|
+
@customer_hash['credit_card'] || {}
|
176
184
|
end
|
177
185
|
|
178
186
|
def set_customer_id
|
179
|
-
@customer_hash[
|
187
|
+
@customer_hash['id'] ||= create_id(@customer_hash['merchant_id'])
|
180
188
|
end
|
181
189
|
|
182
190
|
def credit_card_token(credit_card_hash_without_token)
|
183
|
-
md5("#{credit_card_hash_without_token[
|
191
|
+
md5("#{credit_card_hash_without_token['number']}#{@customer_hash['merchant_id']}")
|
184
192
|
end
|
185
193
|
end
|
186
194
|
end
|