braintree 2.49.0 → 2.50.0

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.
@@ -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