braintree 2.30.0 → 2.30.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/lib/braintree.rb +5 -1
- data/lib/braintree/client_token.rb +18 -0
- data/lib/braintree/client_token_gateway.rb +30 -0
- data/lib/braintree/configuration.rb +29 -0
- data/lib/braintree/credit_card.rb +4 -0
- data/lib/braintree/credit_card_gateway.rb +9 -1
- data/lib/braintree/customer.rb +4 -0
- data/lib/braintree/gateway.rb +4 -0
- data/lib/braintree/http.rb +13 -1
- data/lib/braintree/sha256_digest.rb +13 -0
- data/lib/braintree/signature_service.rb +19 -0
- data/lib/braintree/subscription_gateway.rb +2 -0
- data/lib/braintree/successful_result.rb +4 -6
- data/lib/braintree/transaction_gateway.rb +1 -1
- data/lib/braintree/transparent_redirect_gateway.rb +3 -8
- data/lib/braintree/version.rb +1 -1
- data/lib/braintree/webhook_notification_gateway.rb +11 -5
- data/lib/braintree/webhook_testing_gateway.rb +13 -13
- data/spec/httpsd.pid +1 -1
- data/spec/integration/braintree/client_api/client_token_spec.rb +143 -0
- data/spec/integration/braintree/client_api/spec_helper.rb +80 -0
- data/spec/integration/braintree/credit_card_spec.rb +99 -0
- data/spec/integration/braintree/customer_spec.rb +24 -0
- data/spec/integration/braintree/subscription_spec.rb +40 -1
- data/spec/integration/braintree/transaction_search_spec.rb +2 -2
- data/spec/integration/braintree/transaction_spec.rb +27 -5
- data/spec/unit/braintree/client_token_spec.rb +37 -0
- data/spec/unit/braintree/configuration_spec.rb +30 -0
- data/spec/unit/braintree/credit_card_spec.rb +2 -0
- data/spec/unit/braintree/customer_spec.rb +2 -0
- data/spec/unit/braintree/digest_spec.rb +14 -0
- data/spec/unit/braintree/http_spec.rb +19 -0
- data/spec/unit/braintree/sha256_digest_spec.rb +11 -0
- data/spec/unit/braintree/signature_service_spec.rb +23 -0
- data/spec/unit/braintree/successful_result_spec.rb +7 -7
- data/spec/unit/braintree/transparent_redirect_spec.rb +8 -1
- data/spec/unit/braintree/webhook_notification_spec.rb +58 -4
- metadata +126 -121
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8344ccd108c80b288f1aa06fe5ba464b7b30fc72
|
4
|
+
data.tar.gz: 1f31b8e13d92045c7f195764aff85bf54d0e822f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1433ff6de818797a42e80d011fb76cbb8365e7b83f2071c2c8f8f9855bc1f9acee30c3a133f0d804453e1695345bcdd44c74248438730876017e67dac1279b9f
|
7
|
+
data.tar.gz: c2c7febb1f2827454eb232ed952e03500d0fb720dec58234cdbfe36b0c72b387065cc5fb8b968928f19788ff18a82497bbbeb1acf670598b2eb5d9a61ec71e07
|
data/LICENSE
CHANGED
data/lib/braintree.rb
CHANGED
@@ -22,10 +22,12 @@ require "braintree/modification"
|
|
22
22
|
|
23
23
|
require "braintree/add_on"
|
24
24
|
require "braintree/add_on_gateway"
|
25
|
-
require "braintree/address/country_names"
|
26
25
|
require "braintree/address"
|
26
|
+
require "braintree/address/country_names"
|
27
27
|
require "braintree/address_gateway"
|
28
28
|
require "braintree/advanced_search"
|
29
|
+
require "braintree/client_token"
|
30
|
+
require "braintree/client_token_gateway"
|
29
31
|
require "braintree/configuration"
|
30
32
|
require "braintree/credit_card"
|
31
33
|
require "braintree/credit_card_gateway"
|
@@ -56,6 +58,8 @@ require "braintree/plan_gateway"
|
|
56
58
|
require "braintree/settlement_batch_summary"
|
57
59
|
require "braintree/settlement_batch_summary_gateway"
|
58
60
|
require "braintree/resource_collection"
|
61
|
+
require "braintree/sha256_digest"
|
62
|
+
require "braintree/signature_service"
|
59
63
|
require "braintree/subscription"
|
60
64
|
require "braintree/subscription_gateway"
|
61
65
|
require "braintree/subscription_search"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Braintree
|
4
|
+
module ClientToken
|
5
|
+
def self.generate(options={})
|
6
|
+
_validate_options(options)
|
7
|
+
Configuration.gateway.client_token.generate(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self._validate_options(options)
|
11
|
+
[:make_default, :fail_on_duplicate_payment_method, :verify_card].each do |credit_card_option|
|
12
|
+
if options[credit_card_option]
|
13
|
+
raise ArgumentError.new("cannot specify #{credit_card_option} without a customer_id") unless options[:customer_id]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Braintree
|
2
|
+
class ClientTokenGateway
|
3
|
+
def initialize(gateway)
|
4
|
+
@gateway = gateway
|
5
|
+
@config = gateway.config
|
6
|
+
end
|
7
|
+
|
8
|
+
def generate(options={})
|
9
|
+
params = nil
|
10
|
+
if options
|
11
|
+
Util.verify_keys(ClientTokenGateway._generate_signature, options)
|
12
|
+
params = {:client_token => options}
|
13
|
+
end
|
14
|
+
result = @config.http.post("/client_token", params)
|
15
|
+
|
16
|
+
if result[:client_token]
|
17
|
+
result[:client_token][:value]
|
18
|
+
else
|
19
|
+
raise ArgumentError, result[:api_error_response][:message]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self._generate_signature # :nodoc:
|
24
|
+
[
|
25
|
+
:customer_id, :proxy_merchant_id,
|
26
|
+
{:options => [:make_default, :verify_card, :fail_on_duplicate_payment_method]}
|
27
|
+
]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -48,6 +48,14 @@ module Braintree
|
|
48
48
|
@logger ||= _default_logger
|
49
49
|
end
|
50
50
|
|
51
|
+
def self.signature_service
|
52
|
+
instantiate.signature_service
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.sha256_signature_service
|
56
|
+
instantiate.sha256_signature_service
|
57
|
+
end
|
58
|
+
|
51
59
|
def initialize(options = {})
|
52
60
|
[:endpoint, :environment, :public_key, :private_key, :custom_user_agent, :logger].each do |attr|
|
53
61
|
instance_variable_set "@#{attr}", options[attr]
|
@@ -110,6 +118,19 @@ module Braintree
|
|
110
118
|
end
|
111
119
|
end
|
112
120
|
|
121
|
+
def auth_url
|
122
|
+
case @environment
|
123
|
+
when :development
|
124
|
+
"http://auth.venmo.dev:9292"
|
125
|
+
when :production
|
126
|
+
"https://auth.venmo.com"
|
127
|
+
when :qa
|
128
|
+
"https://auth.venmo.qa2.braintreegateway.com"
|
129
|
+
when :sandbox
|
130
|
+
"https://auth.venmo.sandbox.braintreegateway.com"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
113
134
|
def ssl? # :nodoc:
|
114
135
|
case @environment
|
115
136
|
when :development
|
@@ -133,5 +154,13 @@ module Braintree
|
|
133
154
|
def inspect
|
134
155
|
super.gsub(/@private_key=\".*\"/, '@private_key="[FILTERED]"')
|
135
156
|
end
|
157
|
+
|
158
|
+
def signature_service
|
159
|
+
@signature_service ||= SignatureService.new(@private_key)
|
160
|
+
end
|
161
|
+
|
162
|
+
def sha256_signature_service
|
163
|
+
@sha256_signature_service ||= SignatureService.new(@private_key, SHA256Digest)
|
164
|
+
end
|
136
165
|
end
|
137
166
|
end
|
@@ -94,6 +94,10 @@ module Braintree
|
|
94
94
|
Configuration.gateway.credit_card.find(token)
|
95
95
|
end
|
96
96
|
|
97
|
+
def self.from_nonce(nonce)
|
98
|
+
Configuration.gateway.credit_card.from_nonce(nonce)
|
99
|
+
end
|
100
|
+
|
97
101
|
# See http://www.braintreepayments.com/docs/ruby/transactions/create_from_vault
|
98
102
|
def self.sale(token, transaction_attributes)
|
99
103
|
Configuration.gateway.transaction.sale(transaction_attributes.merge(:payment_method_token => token))
|
@@ -48,6 +48,14 @@ module Braintree
|
|
48
48
|
raise NotFoundError, "payment method with token #{token.inspect} not found"
|
49
49
|
end
|
50
50
|
|
51
|
+
def from_nonce(nonce)
|
52
|
+
raise ArgumentError if nonce.nil? || nonce.to_s.strip == ""
|
53
|
+
response = @config.http.get "/payment_methods/from_nonce/#{nonce}"
|
54
|
+
CreditCard._new(@gateway, response[:credit_card])
|
55
|
+
rescue NotFoundError
|
56
|
+
raise NotFoundError, "nonce #{nonce.inspect} locked, consumed, or not found"
|
57
|
+
end
|
58
|
+
|
51
59
|
def update(token, attributes)
|
52
60
|
Util.verify_keys(CreditCardGateway._update_signature, attributes)
|
53
61
|
_do_update(:put, "/payment_methods/#{token}", :credit_card => attributes)
|
@@ -80,7 +88,7 @@ module Braintree
|
|
80
88
|
signature = [
|
81
89
|
:billing_address_id, :cardholder_name, :cvv, :device_session_id, :expiration_date,
|
82
90
|
:expiration_month, :expiration_year, :number, :token, :venmo_sdk_payment_method_code,
|
83
|
-
:device_data, :fraud_merchant_id,
|
91
|
+
:device_data, :fraud_merchant_id, :payment_method_nonce,
|
84
92
|
{:options => options},
|
85
93
|
{:billing_address => billing_address_params}
|
86
94
|
]
|
data/lib/braintree/customer.rb
CHANGED
data/lib/braintree/gateway.rb
CHANGED
data/lib/braintree/http.rb
CHANGED
@@ -14,7 +14,8 @@ module Braintree
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def get(
|
17
|
+
def get(_path, query_params={})
|
18
|
+
path = _path + _build_query_string(query_params)
|
18
19
|
response = _http_do Net::HTTP::Get, path
|
19
20
|
if response.code.to_i == 200 || response.code.to_i == 422
|
20
21
|
Xml.hash_from_xml(_body(response))
|
@@ -46,6 +47,17 @@ module Braintree
|
|
46
47
|
Braintree::Xml.hash_to_xml params
|
47
48
|
end
|
48
49
|
|
50
|
+
def _build_query_string(params)
|
51
|
+
if params.empty?
|
52
|
+
""
|
53
|
+
else
|
54
|
+
"?" + params.map do |x, y|
|
55
|
+
raise(ArgumentError, "Nested hashes aren't supported in query parameters") if y.respond_to?(:to_hash)
|
56
|
+
"#{x}=#{y}"
|
57
|
+
end.join("&")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
49
61
|
def _http_do(http_verb, path, body = nil)
|
50
62
|
connection = Net::HTTP.new(@config.server, @config.port)
|
51
63
|
connection.read_timeout = 60
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Braintree
|
2
|
+
module SHA256Digest # :nodoc:
|
3
|
+
def self.hexdigest(private_key, string)
|
4
|
+
_hmac(private_key, string)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self._hmac(key, message)
|
8
|
+
key_digest = ::Digest::SHA256.digest(key)
|
9
|
+
sha256 = OpenSSL::Digest::Digest.new("sha256")
|
10
|
+
OpenSSL::HMAC.hexdigest(sha256, key_digest, message.to_s)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Braintree
|
2
|
+
class SignatureService
|
3
|
+
attr_reader :key
|
4
|
+
|
5
|
+
def initialize(key, digest=Braintree::Digest)
|
6
|
+
@key = key
|
7
|
+
@digest = digest
|
8
|
+
end
|
9
|
+
|
10
|
+
def sign(data)
|
11
|
+
query_string = Util.hash_to_query_string(data)
|
12
|
+
"#{hash(query_string)}|#{query_string}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def hash(data)
|
16
|
+
@digest.hexdigest(@key, data)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -60,6 +60,7 @@ module Braintree
|
|
60
60
|
:never_expires,
|
61
61
|
:number_of_billing_cycles,
|
62
62
|
:payment_method_token,
|
63
|
+
:payment_method_nonce,
|
63
64
|
:plan_id,
|
64
65
|
:price,
|
65
66
|
:trial_duration,
|
@@ -77,6 +78,7 @@ module Braintree
|
|
77
78
|
:never_expires,
|
78
79
|
:number_of_billing_cycles,
|
79
80
|
:payment_method_token,
|
81
|
+
:payment_method_nonce,
|
80
82
|
:plan_id,
|
81
83
|
:price,
|
82
84
|
{:options => [
|
@@ -3,14 +3,12 @@ module Braintree
|
|
3
3
|
class SuccessfulResult
|
4
4
|
include BaseModule
|
5
5
|
|
6
|
+
attr_reader :address, :credit_card, :customer, :merchant_account, :settlement_batch_summary, :subscription, :new_transaction, :transaction
|
7
|
+
|
6
8
|
def initialize(attributes = {}) # :nodoc:
|
7
9
|
@attrs = attributes.keys
|
8
|
-
|
9
|
-
|
10
|
-
define_method key do
|
11
|
-
value
|
12
|
-
end
|
13
|
-
end
|
10
|
+
attributes.each do |key, value|
|
11
|
+
instance_variable_set("@#{key}", value)
|
14
12
|
end
|
15
13
|
end
|
16
14
|
|
@@ -117,7 +117,7 @@ module Braintree
|
|
117
117
|
:amount, :customer_id, :merchant_account_id, :order_id, :channel, :payment_method_token,
|
118
118
|
:purchase_order_number, :recurring, :shipping_address_id, :type, :tax_amount, :tax_exempt,
|
119
119
|
:venmo_sdk_payment_method_code, :device_session_id, :service_fee_amount, :device_data, :fraud_merchant_id,
|
120
|
-
:billing_address_id,
|
120
|
+
:billing_address_id, :payment_method_nonce,
|
121
121
|
{:credit_card => [:token, :cardholder_name, :cvv, :expiration_date, :expiration_month, :expiration_year, :number]},
|
122
122
|
{:customer => [:id, :company, :email, :fax, :first_name, :last_name, :phone, :website]},
|
123
123
|
{
|
@@ -51,7 +51,7 @@ module Braintree
|
|
51
51
|
|
52
52
|
query_strings_without_hash = [query_string_without_hash, encoded_query_string_without_hash, decoded_query_string_without_hash]
|
53
53
|
|
54
|
-
if query_strings_without_hash.any? { |query_string|
|
54
|
+
if query_strings_without_hash.any? { |query_string| @config.signature_service.hash(query_string) == params[:hash] }
|
55
55
|
params
|
56
56
|
else
|
57
57
|
raise ForgedQueryString
|
@@ -92,17 +92,12 @@ module Braintree
|
|
92
92
|
|
93
93
|
def _data(params) # :nodoc:
|
94
94
|
raise ArgumentError, "expected params to contain :redirect_url" unless params[:redirect_url]
|
95
|
-
|
95
|
+
|
96
|
+
@config.signature_service.sign(params.merge(
|
96
97
|
:api_version => @config.api_version,
|
97
98
|
:time => Time.now.utc.strftime("%Y%m%d%H%M%S"),
|
98
99
|
:public_key => @config.public_key
|
99
100
|
))
|
100
|
-
tr_data_hash = _hash(tr_data_segment)
|
101
|
-
"#{tr_data_hash}|#{tr_data_segment}"
|
102
|
-
end
|
103
|
-
|
104
|
-
def _hash(string) # :nodoc:
|
105
|
-
::Braintree::Digest.hexdigest(@config.private_key, string)
|
106
101
|
end
|
107
102
|
end
|
108
103
|
end
|
data/lib/braintree/version.rb
CHANGED
@@ -6,6 +6,9 @@ module Braintree
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def parse(signature_string, payload)
|
9
|
+
if payload =~ /[^A-Za-z0-9+=\/\n]/
|
10
|
+
raise InvalidSignature, "payload contains illegal characters"
|
11
|
+
end
|
9
12
|
_verify_signature(signature_string, payload)
|
10
13
|
attributes = Xml.hash_from_xml(Base64.decode64(payload))
|
11
14
|
WebhookNotification._new(@gateway, attributes[:notification])
|
@@ -25,12 +28,15 @@ module Braintree
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
28
|
-
def _verify_signature(
|
29
|
-
public_key, signature = _matching_signature_pair(
|
30
|
-
|
31
|
+
def _verify_signature(signature_string, payload)
|
32
|
+
public_key, signature = _matching_signature_pair(signature_string)
|
33
|
+
raise InvalidSignature, 'no matching public key' if public_key.nil?
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
signature_matches = [payload, payload + "\n"].any? do |payload|
|
36
|
+
payload_signature = Braintree::Digest.hexdigest(@config.private_key, payload)
|
37
|
+
Braintree::Digest.secure_compare(signature, payload_signature)
|
38
|
+
end
|
39
|
+
raise InvalidSignature, 'signature does not match payload - one has been modified' unless signature_matches
|
34
40
|
end
|
35
41
|
end
|
36
42
|
end
|
@@ -65,31 +65,31 @@ module Braintree
|
|
65
65
|
def _partner_merchant_connected_sample_xml(data)
|
66
66
|
|
67
67
|
<<-XML
|
68
|
-
<
|
69
|
-
<
|
70
|
-
<
|
71
|
-
<
|
72
|
-
<
|
73
|
-
<
|
74
|
-
</
|
68
|
+
<partner-merchant>
|
69
|
+
<merchant-public-id>public_id</merchant-public-id>
|
70
|
+
<public-key>public_key</public-key>
|
71
|
+
<private-key>private_key</private-key>
|
72
|
+
<partner-merchant-id>abc123</partner-merchant-id>
|
73
|
+
<client-side-encryption-key>cse_key</client-side-encryption-key>
|
74
|
+
</partner-merchant>
|
75
75
|
XML
|
76
76
|
end
|
77
77
|
|
78
78
|
def _partner_merchant_disconnected_sample_xml(data)
|
79
79
|
|
80
80
|
<<-XML
|
81
|
-
<
|
82
|
-
<
|
83
|
-
</
|
81
|
+
<partner-merchant>
|
82
|
+
<partner-merchant-id>abc123</partner-merchant-id>
|
83
|
+
</partner-merchant>
|
84
84
|
XML
|
85
85
|
end
|
86
86
|
|
87
87
|
def _partner_merchant_declined_sample_xml(data)
|
88
88
|
|
89
89
|
<<-XML
|
90
|
-
<
|
91
|
-
<
|
92
|
-
</
|
90
|
+
<partner-merchant>
|
91
|
+
<partner-merchant-id>abc123</partner-merchant-id>
|
92
|
+
</partner-merchant>
|
93
93
|
XML
|
94
94
|
end
|
95
95
|
|
data/spec/httpsd.pid
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
5195
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
3
|
+
|
4
|
+
describe Braintree::ClientToken do
|
5
|
+
|
6
|
+
describe "self.generate" do
|
7
|
+
it "generates a fingerprint that the gateway accepts" do
|
8
|
+
config = Braintree::Configuration.instantiate
|
9
|
+
client_token = Braintree::ClientToken.generate
|
10
|
+
http = ClientApiHttp.new(
|
11
|
+
config,
|
12
|
+
:authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
|
13
|
+
:shared_customer_identifier => "fake_identifier",
|
14
|
+
:shared_customer_identifier_type => "testing"
|
15
|
+
)
|
16
|
+
|
17
|
+
response = http.get_cards
|
18
|
+
|
19
|
+
response.code.should == "200"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises ArgumentError on invalid parameters (422)" do
|
23
|
+
expect do
|
24
|
+
Braintree::ClientToken.generate(:options => {:make_default => true})
|
25
|
+
end.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can pass verify_card" do
|
29
|
+
config = Braintree::Configuration.instantiate
|
30
|
+
result = Braintree::Customer.create
|
31
|
+
client_token = Braintree::ClientToken.generate(
|
32
|
+
:customer_id => result.customer.id,
|
33
|
+
:options => {
|
34
|
+
:verify_card => true
|
35
|
+
}
|
36
|
+
)
|
37
|
+
|
38
|
+
http = ClientApiHttp.new(
|
39
|
+
config,
|
40
|
+
:authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
|
41
|
+
:shared_customer_identifier => "fake_identifier",
|
42
|
+
:shared_customer_identifier_type => "testing"
|
43
|
+
)
|
44
|
+
|
45
|
+
response = http.add_card(
|
46
|
+
:credit_card => {
|
47
|
+
:number => "4000111111111115",
|
48
|
+
:expiration_month => "11",
|
49
|
+
:expiration_year => "2099"
|
50
|
+
}
|
51
|
+
)
|
52
|
+
|
53
|
+
response.code.should == "422"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can pass make_default" do
|
57
|
+
config = Braintree::Configuration.instantiate
|
58
|
+
result = Braintree::Customer.create
|
59
|
+
customer_id = result.customer.id
|
60
|
+
client_token = Braintree::ClientToken.generate(
|
61
|
+
:customer_id => customer_id,
|
62
|
+
:options => {
|
63
|
+
:make_default => true
|
64
|
+
}
|
65
|
+
)
|
66
|
+
|
67
|
+
http = ClientApiHttp.new(
|
68
|
+
config,
|
69
|
+
:authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
|
70
|
+
:shared_customer_identifier => "fake_identifier",
|
71
|
+
:shared_customer_identifier_type => "testing"
|
72
|
+
)
|
73
|
+
|
74
|
+
response = http.add_card(
|
75
|
+
:credit_card => {
|
76
|
+
:number => "4111111111111111",
|
77
|
+
:expiration_month => "11",
|
78
|
+
:expiration_year => "2099"
|
79
|
+
}
|
80
|
+
)
|
81
|
+
|
82
|
+
response.code.should == "201"
|
83
|
+
|
84
|
+
response = http.add_card(
|
85
|
+
:credit_card => {
|
86
|
+
:number => "4005519200000004",
|
87
|
+
:expiration_month => "11",
|
88
|
+
:expiration_year => "2099"
|
89
|
+
}
|
90
|
+
)
|
91
|
+
|
92
|
+
response.code.should == "201"
|
93
|
+
|
94
|
+
customer = Braintree::Customer.find(customer_id)
|
95
|
+
customer.credit_cards.select { |c| c.bin == "400551" }[0].should be_default
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can pass fail_on_duplicate_payment_method" do
|
99
|
+
config = Braintree::Configuration.instantiate
|
100
|
+
result = Braintree::Customer.create
|
101
|
+
customer_id = result.customer.id
|
102
|
+
client_token = Braintree::ClientToken.generate(
|
103
|
+
:customer_id => customer_id
|
104
|
+
)
|
105
|
+
|
106
|
+
http = ClientApiHttp.new(
|
107
|
+
config,
|
108
|
+
:authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
|
109
|
+
:shared_customer_identifier => "fake_identifier",
|
110
|
+
:shared_customer_identifier_type => "testing"
|
111
|
+
)
|
112
|
+
|
113
|
+
response = http.add_card(
|
114
|
+
:credit_card => {
|
115
|
+
:number => "4111111111111111",
|
116
|
+
:expiration_month => "11",
|
117
|
+
:expiration_year => "2099"
|
118
|
+
}
|
119
|
+
)
|
120
|
+
|
121
|
+
response.code.should == "201"
|
122
|
+
|
123
|
+
client_token = Braintree::ClientToken.generate(
|
124
|
+
:customer_id => customer_id,
|
125
|
+
:options => {
|
126
|
+
:fail_on_duplicate_payment_method => true
|
127
|
+
}
|
128
|
+
)
|
129
|
+
|
130
|
+
http.fingerprint = JSON.parse(client_token)["authorizationFingerprint"]
|
131
|
+
|
132
|
+
response = http.add_card(
|
133
|
+
:credit_card => {
|
134
|
+
:number => "4111111111111111",
|
135
|
+
:expiration_month => "11",
|
136
|
+
:expiration_year => "2099"
|
137
|
+
}
|
138
|
+
)
|
139
|
+
|
140
|
+
response.code.should == "422"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|