braintree 2.49.0 → 2.50.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -72,6 +72,7 @@ require "braintree/paypal_account_gateway"
72
72
  require "braintree/plan"
73
73
  require "braintree/plan_gateway"
74
74
  require "braintree/risk_data"
75
+ require "braintree/facilitator_details"
75
76
  require "braintree/three_d_secure_info"
76
77
  require "braintree/settlement_batch_summary"
77
78
  require "braintree/settlement_batch_summary_gateway"
@@ -87,6 +87,10 @@ module Braintree
87
87
  Configuration.gateway.credit_card.from_nonce(nonce)
88
88
  end
89
89
 
90
+ def self.grant(token, allow_vaulting)
91
+ Configuration.gateway.credit_card.grant(token, allow_vaulting)
92
+ end
93
+
90
94
  def self.sale(token, transaction_attributes)
91
95
  Configuration.gateway.transaction.sale(transaction_attributes.merge(:payment_method_token => token))
92
96
  end
@@ -203,7 +207,7 @@ module Braintree
203
207
  end
204
208
 
205
209
  def nonce
206
- @nonce ||= PaymentMethodNonce.create(self)
210
+ @nonce ||= PaymentMethodNonce.create(token)
207
211
  end
208
212
 
209
213
  # Returns true if the card is associated with Venmo SDK
@@ -57,6 +57,20 @@ module Braintree
57
57
  raise NotFoundError, "nonce #{nonce.inspect} locked, consumed, or not found"
58
58
  end
59
59
 
60
+ def grant(token, allow_vaulting)
61
+ raise ArgumentError if token.nil? || token.to_s.strip == ""
62
+ response = @config.http.post(
63
+ "#{@config.base_merchant_path}/payment_methods/grant",
64
+ :payment_method => {
65
+ :shared_payment_method_token => token,
66
+ :allow_vaulting => allow_vaulting
67
+ }
68
+ )
69
+ PaymentMethodNonce._new(@gateway, response[:payment_method_nonce])
70
+ rescue NotFoundError
71
+ raise NotFoundError, "payment method with token #{token.inspect} not found"
72
+ end
73
+
60
74
  def update(token, attributes)
61
75
  Util.verify_keys(CreditCardGateway._update_signature, attributes)
62
76
  _do_update(:put, "/payment_methods/credit_card/#{token}", :credit_card => attributes)
@@ -294,6 +294,7 @@ module Braintree
294
294
  PayPalNotEnabled = "91576"
295
295
  PayPalVaultRecordMissingData = "91583"
296
296
  PaymentInstrumentNotSupportedByMerchantAccount = "91577"
297
+ PaymentInstrumentTypeIsNotAccepted = "915101"
297
298
  PaymentMethodConflict = "91515"
298
299
  PaymentMethodConflictWithVenmoSDK = "91549"
299
300
  PaymentMethodDoesNotBelongToCustomer = "91516"
@@ -309,6 +310,7 @@ module Braintree
309
310
  ProcessorAuthorizationCodeIsInvalid = "81520"
310
311
  ProcessorDoesNotSupportAuths = "915104"
311
312
  ProcessorDoesNotSupportCredits = "91546"
313
+ ProcessorDoesNotSupportPartialSettlement = "915102"
312
314
  ProcessorDoesNotSupportVoiceAuthorizations = "91545"
313
315
  PurchaseOrderNumberIsInvalid = "91548"
314
316
  PurchaseOrderNumberIsTooLong = "91537"
@@ -0,0 +1,19 @@
1
+ module Braintree
2
+ class FacilitatorDetails # :nodoc:
3
+ include BaseModule
4
+
5
+ attr_reader :oauth_application_client_id, :oauth_application_name
6
+
7
+ def initialize(attributes)
8
+ set_instance_variables_from_hash attributes unless attributes.nil?
9
+ end
10
+
11
+ def inspect
12
+ attr_order = [:oauth_application_client_id, :oauth_application_name]
13
+ formatted_attrs = attr_order.map do |attr|
14
+ "#{attr}: #{send(attr).inspect}"
15
+ end
16
+ "#<FacilitatorDetails #{formatted_attrs.join(", ")}>"
17
+ end
18
+ end
19
+ end
@@ -36,10 +36,6 @@ module Braintree
36
36
  DiscountGateway.new(self)
37
37
  end
38
38
 
39
- def merchant
40
- MerchantGateway.new(self)
41
- end
42
-
43
39
  def oauth
44
40
  OAuthGateway.new(self)
45
41
  end
@@ -60,6 +56,10 @@ module Braintree
60
56
  PayPalAccountGateway.new(self)
61
57
  end
62
58
 
59
+ def merchant
60
+ MerchantGateway.new(self)
61
+ end
62
+
63
63
  def merchant_account
64
64
  MerchantAccountGateway.new(self)
65
65
  end
@@ -15,5 +15,9 @@ module Braintree
15
15
  def self._new(*args) # :nodoc:
16
16
  self.new *args
17
17
  end
18
+
19
+ def self.provision_raw_apple_pay
20
+ Configuration.gateway.merchant.provision_raw_apple_pay
21
+ end
18
22
  end
19
23
  end
@@ -5,6 +5,17 @@ module Braintree
5
5
  @config = gateway.config
6
6
  end
7
7
 
8
+ def provision_raw_apple_pay
9
+ response = @config.http.put("#{@config.base_merchant_path}/provision_raw_apple_pay")
10
+ if response[:apple_pay]
11
+ SuccessfulResult.new(response[:apple_pay])
12
+ elsif response[:api_error_response]
13
+ ErrorResult.new(@gateway, response[:api_error_response])
14
+ else
15
+ raise UnexpectedError, "expected :apple_pay or :api_error_response"
16
+ end
17
+ end
18
+
8
19
  def create(params)
9
20
  _create_merchant(params)
10
21
  end
@@ -36,6 +36,7 @@ module Braintree
36
36
 
37
37
  def delete(token)
38
38
  @config.http.delete("#{@config.base_merchant_path}/payment_methods/any/#{token}")
39
+ SuccessfulResult.new
39
40
  end
40
41
 
41
42
  def find(token)
@@ -2,8 +2,8 @@ module Braintree
2
2
  class PaymentMethodNonce
3
3
  include BaseModule # :nodoc:
4
4
 
5
- def self.create(payment_method)
6
- Configuration.gateway.payment_method_nonce.create(payment_method)
5
+ def self.create(payment_method_token)
6
+ Configuration.gateway.payment_method_nonce.create(payment_method_token)
7
7
  end
8
8
 
9
9
  def self.find(payment_method_nonce)
@@ -2,7 +2,7 @@ module Braintree
2
2
  class SuccessfulResult
3
3
  include BaseModule
4
4
 
5
- attr_reader :address, :credit_card, :customer, :merchant_account, :payment_method, :settlement_batch_summary, :subscription, :new_transaction, :transaction, :payment_method_nonce, :credentials, :merchant
5
+ attr_reader :address, :credit_card, :customer, :merchant_account, :payment_method, :settlement_batch_summary, :subscription, :new_transaction, :transaction, :payment_method_nonce, :credentials, :merchant, :supported_networks
6
6
 
7
7
  def initialize(attributes = {}) # :nodoc:
8
8
  @attrs = attributes.keys
@@ -104,6 +104,8 @@ module Braintree
104
104
  attr_reader :recurring
105
105
  attr_reader :refund_ids, :refunded_transaction_id
106
106
  attr_reader :settlement_batch_id
107
+ attr_reader :authorized_transaction_id
108
+ attr_reader :partial_settlement_transaction_ids
107
109
  # See Transaction::Status
108
110
  attr_reader :status
109
111
  attr_reader :status_history
@@ -116,6 +118,7 @@ module Braintree
116
118
  attr_reader :add_ons, :discounts
117
119
  attr_reader :payment_instrument_type
118
120
  attr_reader :risk_data
121
+ attr_reader :facilitator_details
119
122
  attr_reader :three_d_secure_info
120
123
 
121
124
  def self.create(attributes)
@@ -210,6 +213,10 @@ module Braintree
210
213
  return_object_or_raise(:transaction) { submit_for_settlement(transaction_id, amount) }
211
214
  end
212
215
 
216
+ def self.submit_for_partial_settlement(authorized_transaction_id, amount = nil)
217
+ Configuration.gateway.transaction.submit_for_partial_settlement(authorized_transaction_id, amount)
218
+ end
219
+
213
220
  def self.void(transaction_id)
214
221
  Configuration.gateway.transaction.void(transaction_id)
215
222
  end
@@ -242,6 +249,7 @@ module Braintree
242
249
  discounts.map! { |attrs| Discount._new(attrs) } if discounts
243
250
  @payment_instrument_type = attributes[:payment_instrument_type]
244
251
  @risk_data = RiskData.new(attributes[:risk_data]) if attributes[:risk_data]
252
+ @facilitator_details = FacilitatorDetails.new(attributes[:facilitator_details]) if attributes[:facilitator_details]
245
253
  @three_d_secure_info = ThreeDSecureInfo.new(attributes[:three_d_secure_info]) if attributes[:three_d_secure_info]
246
254
  end
247
255
 
@@ -104,6 +104,12 @@ module Braintree
104
104
  _handle_transaction_response(response)
105
105
  end
106
106
 
107
+ def submit_for_partial_settlement(authorized_transaction_id, amount = nil)
108
+ raise ArgumentError, "authorized_transaction_id is invalid" unless authorized_transaction_id =~ /\A[0-9a-z]+\z/
109
+ response = @config.http.post("#{@config.base_merchant_path}/transactions/#{authorized_transaction_id}/submit_for_partial_settlement", :transaction => {:amount => amount})
110
+ _handle_transaction_response(response)
111
+ end
112
+
107
113
  def void(transaction_id)
108
114
  response = @config.http.put("#{@config.base_merchant_path}/transactions/#{transaction_id}/void")
109
115
  _handle_transaction_response(response)
@@ -120,6 +126,7 @@ module Braintree
120
126
  :purchase_order_number, :recurring, :shipping_address_id, :type, :tax_amount, :tax_exempt,
121
127
  :venmo_sdk_payment_method_code, :device_session_id, :service_fee_amount, :device_data, :fraud_merchant_id,
122
128
  :billing_address_id, :payment_method_nonce, :three_d_secure_token,
129
+ :shared_payment_method_token, :shared_billing_address_id, :shared_customer_id, :shared_shipping_address_id,
123
130
  {:credit_card => [:token, :cardholder_name, :cvv, :expiration_date, :expiration_month, :expiration_year, :number]},
124
131
  {:customer => [:id, :company, :email, :fax, :first_name, :last_name, :phone, :website]},
125
132
  {
@@ -144,7 +151,8 @@ module Braintree
144
151
  {:custom_fields => :_any_key_},
145
152
  {:descriptor => [:name, :phone, :url]},
146
153
  {:paypal_account => [:email, :token, :paypal_data, :payee_email]},
147
- {:industry => [:industry_type, {:data => [:folio_number, :check_in_date, :check_out_date, :travel_package, :lodging_check_in_date, :lodging_check_out_date, :departure_date, :lodging_name, :room_rate]}]}
154
+ {:industry => [:industry_type, {:data => [:folio_number, :check_in_date, :check_out_date, :travel_package, :lodging_check_in_date, :lodging_check_out_date, :departure_date, :lodging_name, :room_rate]}]},
155
+ {:apple_pay_card => [:number, :cardholder_name, :cryptogram, :expiration_month, :expiration_year]}
148
156
  ]
149
157
  end
150
158
 
@@ -58,11 +58,7 @@ module Braintree
58
58
  multiple_value_field :user
59
59
  multiple_value_field :merchant_account_id
60
60
  multiple_value_field :status, :allows => Transaction::Status::All
61
- multiple_value_field :source, :allows => [
62
- Transaction::Source::Api,
63
- Transaction::Source::ControlPanel,
64
- Transaction::Source::Recurring
65
- ]
61
+ multiple_value_field :source
66
62
  multiple_value_field :type, :allows => Transaction::Type::All
67
63
 
68
64
  key_value_fields :refund
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 2
4
- Minor = 49
4
+ Minor = 50
5
5
  Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
@@ -5,6 +5,8 @@ module Braintree
5
5
  include BaseModule
6
6
 
7
7
  module Kind
8
+ Check = "check"
9
+
8
10
  Disbursement = "disbursement"
9
11
  DisbursementException = "disbursement_exception"
10
12
 
@@ -62,6 +64,10 @@ module Braintree
62
64
  @error_result.message if @error_result
63
65
  end
64
66
 
67
+ def check?
68
+ !!@subject[:check]
69
+ end
70
+
65
71
  class << self
66
72
  protected :new
67
73
  def _new(*args) # :nodoc:
@@ -27,6 +27,8 @@ module Braintree
27
27
 
28
28
  def _subject_sample_xml(kind, id)
29
29
  case kind
30
+ when Braintree::WebhookNotification::Kind::Check
31
+ _check
30
32
  when Braintree::WebhookNotification::Kind::DisputeOpened
31
33
  _dispute_opened_sample_xml(id)
32
34
  when Braintree::WebhookNotification::Kind::DisputeLost
@@ -56,6 +58,13 @@ module Braintree
56
58
  end
57
59
  end
58
60
 
61
+ def _check
62
+
63
+ <<-XML
64
+ <check type="boolean">true</check>
65
+ XML
66
+ end
67
+
59
68
  def _subscription_charged_successfully(id)
60
69
 
61
70
  <<-XML
@@ -0,0 +1 @@
1
+ 10834
@@ -512,6 +512,94 @@ describe Braintree::CreditCard do
512
512
  end
513
513
  end
514
514
 
515
+ describe "self.grant" do
516
+ before(:each) do
517
+ partner_merchant_gateway = Braintree::Gateway.new(
518
+ :merchant_id => "integration_merchant_public_id",
519
+ :public_key => "oauth_app_partner_user_public_key",
520
+ :private_key => "oauth_app_partner_user_private_key",
521
+ :environment => :development,
522
+ :logger => Logger.new("/dev/null")
523
+ )
524
+ customer = partner_merchant_gateway.customer.create(
525
+ :first_name => "Joe",
526
+ :last_name => "Brown",
527
+ :company => "ExampleCo",
528
+ :email => "joe@example.com",
529
+ :phone => "312.555.1234",
530
+ :fax => "614.555.5678",
531
+ :website => "www.example.com"
532
+ ).customer
533
+ @credit_card = partner_merchant_gateway.credit_card.create(
534
+ :customer_id => customer.id,
535
+ :cardholder_name => "Adam Davis",
536
+ :number => Braintree::Test::CreditCardNumbers::Visa,
537
+ :expiration_date => "05/2009"
538
+ ).credit_card
539
+
540
+ oauth_gateway = Braintree::Gateway.new(
541
+ :client_id => "client_id$development$integration_client_id",
542
+ :client_secret => "client_secret$development$integration_client_secret",
543
+ :logger => Logger.new("/dev/null")
544
+ )
545
+ access_token = Braintree::OAuthTestHelper.create_token(oauth_gateway, {
546
+ :merchant_public_id => "integration_merchant_id",
547
+ :scope => "grant_payment_method"
548
+ }).credentials.access_token
549
+
550
+ @granting_gateway = Braintree::Gateway.new(
551
+ :access_token => access_token,
552
+ :logger => Logger.new("/dev/null")
553
+ )
554
+ end
555
+
556
+ it "returns a nonce that is transactable by a partner merchant exactly once" do
557
+ grant_result = @granting_gateway.credit_card.grant(@credit_card.token, false)
558
+
559
+ result = Braintree::Transaction.sale(
560
+ :payment_method_nonce => grant_result.nonce,
561
+ :amount => Braintree::Test::TransactionAmounts::Authorize
562
+ )
563
+ result.success?.should == true
564
+
565
+ result2 = Braintree::Transaction.sale(
566
+ :payment_method_nonce => grant_result.nonce,
567
+ :amount => Braintree::Test::TransactionAmounts::Authorize
568
+ )
569
+ result2.success?.should == false
570
+ end
571
+
572
+ it "returns a nonce that is not vaultable" do
573
+ grant_result = @granting_gateway.credit_card.grant(@credit_card.token, false)
574
+
575
+ customer_result = Braintree::Customer.create()
576
+
577
+ result = Braintree::PaymentMethod.create(
578
+ :customer_id => customer_result.customer.id,
579
+ :payment_method_nonce => grant_result.nonce
580
+ )
581
+ result.success?.should == false
582
+ end
583
+
584
+ it "returns a nonce that is vaultable" do
585
+ grant_result = @granting_gateway.credit_card.grant(@credit_card.token, true)
586
+
587
+ customer_result = Braintree::Customer.create()
588
+
589
+ result = Braintree::PaymentMethod.create(
590
+ :customer_id => customer_result.customer.id,
591
+ :payment_method_nonce => grant_result.nonce
592
+ )
593
+ result.success?.should == true
594
+ end
595
+
596
+ it "raises an error if the token isn't found" do
597
+ expect do
598
+ @granting_gateway.credit_card.grant("not_a_real_token", false)
599
+ end.to raise_error
600
+ end
601
+ end
602
+
515
603
  describe "self.create!" do
516
604
  it "returns the credit card if successful" do
517
605
  customer = Braintree::Customer.create!
@@ -1554,4 +1642,18 @@ describe Braintree::CreditCard do
1554
1642
  end.to raise_error(Braintree::ValidationsFailed)
1555
1643
  end
1556
1644
  end
1645
+
1646
+ describe "nonce" do
1647
+ it "returns the credit card nonce" do
1648
+ customer = Braintree::Customer.create!
1649
+ credit_card = Braintree::CreditCard.create!(
1650
+ :cardholder_name => "Original Holder",
1651
+ :customer_id => customer.id,
1652
+ :number => Braintree::Test::CreditCardNumbers::Visa,
1653
+ :expiration_date => "05/2012"
1654
+ )
1655
+
1656
+ credit_card.nonce.should_not be_nil
1657
+ end
1658
+ end
1557
1659
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
2
 
3
3
  describe Braintree::MerchantGateway do
4
4
  describe "create" do
@@ -52,4 +52,59 @@ describe Braintree::MerchantGateway do
52
52
  errors[0].code.should == Braintree::ErrorCodes::Merchant::PaymentMethodsAreInvalid
53
53
  end
54
54
  end
55
+
56
+ describe "provision_raw_apple_pay" do
57
+ before { _save_config }
58
+ after { _restore_config }
59
+
60
+ context "merchant has processor connection supporting apple pay" do
61
+ before do
62
+ Braintree::Configuration.merchant_id = "integration_merchant_id"
63
+ Braintree::Configuration.public_key = "integration_public_key"
64
+ Braintree::Configuration.private_key = "integration_private_key"
65
+ end
66
+
67
+ it "succeeds" do
68
+ result = Braintree::Merchant.provision_raw_apple_pay
69
+ result.should be_success
70
+ result.supported_networks.should == ["visa", "mastercard", "amex"]
71
+ end
72
+
73
+ it "is repeatable" do
74
+ result = Braintree::Merchant.provision_raw_apple_pay
75
+ result.should be_success
76
+ result = Braintree::Merchant.provision_raw_apple_pay
77
+ result.should be_success
78
+ result.supported_networks.should == ["visa", "mastercard", "amex"]
79
+ end
80
+ end
81
+
82
+ context "merchant has no processor connection supporting apple pay" do
83
+ before do
84
+ Braintree::Configuration.merchant_id = "forward_payment_method_merchant_id"
85
+ Braintree::Configuration.public_key = "forward_payment_method_public_key"
86
+ Braintree::Configuration.private_key = "forward_payment_method_private_key"
87
+ end
88
+
89
+ it "returns a validation error" do
90
+ result = Braintree::Merchant.provision_raw_apple_pay
91
+ result.should_not be_success
92
+ result.errors.for(:apple_pay).first.code.should == Braintree::ErrorCodes::ApplePay::ApplePayCardsAreNotAccepted
93
+ end
94
+ end
95
+
96
+ def _save_config
97
+ @original_config = {
98
+ :merchant_id => Braintree::Configuration.merchant_id,
99
+ :public_key => Braintree::Configuration.public_key,
100
+ :private_key => Braintree::Configuration.private_key,
101
+ }
102
+ end
103
+
104
+ def _restore_config
105
+ Braintree::Configuration.merchant_id = @original_config[:merchant_id]
106
+ Braintree::Configuration.public_key = @original_config[:public_key]
107
+ Braintree::Configuration.private_key = @original_config[:private_key]
108
+ end
109
+ end
55
110
  end