braintree 2.30.0 → 2.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|