solidus_paypal_braintree 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +40 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +18 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +68 -0
  8. data/CHANGELOG.md +258 -0
  9. data/Gemfile +43 -0
  10. data/LICENSE +2 -2
  11. data/README.md +77 -23
  12. data/Rakefile +4 -25
  13. data/app/assets/javascripts/solidus_paypal_braintree/checkout.js +32 -3
  14. data/app/assets/javascripts/solidus_paypal_braintree/client.js +23 -4
  15. data/app/assets/javascripts/solidus_paypal_braintree/constants.js +19 -0
  16. data/app/assets/javascripts/solidus_paypal_braintree/frontend.js +1 -0
  17. data/app/assets/javascripts/solidus_paypal_braintree/hosted_form.js +15 -5
  18. data/app/assets/javascripts/solidus_paypal_braintree/paypal_button.js +47 -20
  19. data/app/assets/javascripts/solidus_paypal_braintree/paypal_messaging.js +22 -0
  20. data/app/decorators/controllers/solidus_paypal_braintree/admin_payments_controller_decorator.rb +11 -0
  21. data/app/decorators/controllers/solidus_paypal_braintree/checkout_controller_decorator.rb +11 -0
  22. data/app/decorators/controllers/solidus_paypal_braintree/client_tokens_controller.rb +41 -0
  23. data/app/decorators/controllers/solidus_paypal_braintree/orders_controller_decorator.rb +11 -0
  24. data/app/decorators/models/solidus_paypal_braintree/spree/store_decorator.rb +20 -0
  25. data/app/decorators/models/solidus_paypal_braintree/spree/user_decorator.rb +13 -0
  26. data/app/helpers/solidus_paypal_braintree/braintree_admin_helper.rb +23 -0
  27. data/app/helpers/solidus_paypal_braintree/braintree_checkout_helper.rb +46 -0
  28. data/app/models/application_record.rb +2 -0
  29. data/app/models/solidus_paypal_braintree/address.rb +30 -0
  30. data/app/models/solidus_paypal_braintree/configuration.rb +26 -3
  31. data/app/models/solidus_paypal_braintree/customer.rb +7 -3
  32. data/app/models/solidus_paypal_braintree/gateway.rb +52 -20
  33. data/app/models/solidus_paypal_braintree/response.rb +3 -2
  34. data/app/models/solidus_paypal_braintree/source.rb +21 -7
  35. data/app/models/solidus_paypal_braintree/transaction.rb +2 -0
  36. data/app/models/solidus_paypal_braintree/transaction_address.rb +30 -12
  37. data/app/models/solidus_paypal_braintree/transaction_import.rb +13 -9
  38. data/app/views/spree/api/payments/source_views/_paypal_braintree.json.jbuilder +3 -0
  39. data/app/views/spree/checkout/existing_payment/_paypal_braintree.html.erb +10 -0
  40. data/app/views/spree/shared/_apple_pay_button.html.erb +2 -2
  41. data/app/views/spree/shared/_braintree_errors.html.erb +11 -17
  42. data/app/views/spree/shared/_braintree_hosted_fields.html.erb +24 -9
  43. data/app/views/spree/shared/_paypal_braintree_head_scripts.html.erb +9 -6
  44. data/app/views/spree/shared/_paypal_cart_button.html.erb +16 -2
  45. data/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
  46. data/bin/console +17 -0
  47. data/bin/rails +15 -0
  48. data/bin/setup +8 -0
  49. data/config/locales/en.yml +10 -0
  50. data/config/locales/it.yml +51 -8
  51. data/config/routes.rb +2 -0
  52. data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +3 -1
  53. data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +5 -7
  54. data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +3 -1
  55. data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
  56. data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
  57. data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +20 -5
  58. data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +25 -21
  59. data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +55 -51
  60. data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +7 -5
  61. data/lib/solidus_paypal_braintree.rb +4 -0
  62. data/lib/solidus_paypal_braintree/country_mapper.rb +4 -2
  63. data/lib/solidus_paypal_braintree/engine.rb +11 -11
  64. data/lib/solidus_paypal_braintree/factories.rb +8 -4
  65. data/lib/solidus_paypal_braintree/request_protection.rb +3 -0
  66. data/lib/solidus_paypal_braintree/version.rb +3 -1
  67. data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +30 -5
  68. data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  69. data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +2 -2
  70. data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  71. data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +4 -2
  72. data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +30 -0
  73. data/solidus_paypal_braintree.gemspec +42 -0
  74. data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +99 -0
  75. data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +55 -0
  76. data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +73 -0
  77. data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +183 -0
  78. data/spec/features/backend/configuration_spec.rb +23 -0
  79. data/spec/features/backend/new_payment_spec.rb +137 -0
  80. data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +187 -0
  81. data/spec/features/frontend/paypal_checkout_spec.rb +166 -0
  82. data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
  83. data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
  84. data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
  85. data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
  86. data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
  87. data/spec/fixtures/cassettes/braintree/token.yml +63 -0
  88. data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
  89. data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
  90. data/spec/fixtures/cassettes/checkout/update.yml +71 -0
  91. data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +156 -0
  92. data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
  93. data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
  94. data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
  95. data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
  96. data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
  97. data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
  98. data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
  99. data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
  100. data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
  101. data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
  102. data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
  103. data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
  104. data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
  105. data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
  106. data/spec/fixtures/cassettes/gateway/void.yml +137 -0
  107. data/spec/fixtures/cassettes/source/card_type.yml +267 -0
  108. data/spec/fixtures/cassettes/source/last4.yml +267 -0
  109. data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
  110. data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
  111. data/spec/fixtures/views/spree/orders/edit.html.erb +50 -0
  112. data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +17 -0
  113. data/spec/models/solidus_paypal_braintree/address_spec.rb +51 -0
  114. data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +317 -0
  115. data/spec/models/solidus_paypal_braintree/gateway_spec.rb +692 -0
  116. data/spec/models/solidus_paypal_braintree/response_spec.rb +280 -0
  117. data/spec/models/solidus_paypal_braintree/source_spec.rb +360 -0
  118. data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +253 -0
  119. data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +283 -0
  120. data/spec/models/solidus_paypal_braintree/transaction_spec.rb +85 -0
  121. data/spec/models/spree/store_spec.rb +14 -0
  122. data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
  123. data/spec/spec_helper.rb +26 -0
  124. data/spec/support/capybara.rb +7 -0
  125. data/spec/support/factories.rb +2 -0
  126. data/spec/support/gateway_helpers.rb +29 -0
  127. data/spec/support/order_ready_for_payment.rb +37 -0
  128. data/spec/support/vcr.rb +42 -0
  129. data/spec/support/views.rb +1 -0
  130. metadata +182 -194
  131. data/app/controllers/solidus_paypal_braintree/client_tokens_controller.rb +0 -22
  132. data/app/helpers/braintree_admin_helper.rb +0 -18
  133. data/app/models/spree/store_decorator.rb +0 -11
  134. data/app/views/spree/shared/_paypal_checkout_button.html.erb +0 -27
  135. data/config/initializers/braintree.rb +0 -1
@@ -0,0 +1,22 @@
1
+ //= require solidus_paypal_braintree/constants
2
+
3
+ SolidusPaypalBraintree.PaypalMessaging = function(paypalOptions) {
4
+ this._paypalOptions = paypalOptions || {};
5
+
6
+ this._client = null;
7
+ };
8
+
9
+ SolidusPaypalBraintree.PaypalMessaging.prototype.initialize = function() {
10
+ this._client = new SolidusPaypalBraintree.createClient({usePaypal: true});
11
+
12
+ return this._client.initialize().then(this.initializeCallback.bind(this));
13
+ };
14
+
15
+ SolidusPaypalBraintree.PaypalMessaging.prototype.initializeCallback = function() {
16
+ this._paymentMethodId = this._client.paymentMethodId;
17
+
18
+ this._client.getPaypalInstance().loadPayPalSDK({
19
+ currency: this._paypalOptions.currency,
20
+ components: "messages"
21
+ })
22
+ };
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module AdminPaymentsControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusPaypalBraintree::BraintreeAdminHelper
7
+ end
8
+
9
+ ::Spree::Admin::PaymentsController.prepend(self)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module CheckoutControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusPaypalBraintree::BraintreeCheckoutHelper
7
+ end
8
+
9
+ ::Spree::CheckoutController.prepend(self)
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ class ClientTokensController < ::Spree::Api::BaseController
5
+ skip_before_action :authenticate_user
6
+
7
+ before_action :load_gateway
8
+
9
+ def create
10
+ token = @gateway.generate_token
11
+ if token
12
+ render json: { client_token: token, payment_method_id: @gateway.id }
13
+ else
14
+ render json: { error: Gateway::TOKEN_GENERATION_DISABLED_MESSAGE }, status: :unprocessable_entity
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def load_gateway
21
+ if params[:payment_method_id]
22
+ @gateway = ::SolidusPaypalBraintree::Gateway.find(params[:payment_method_id])
23
+ else
24
+ store_payment_methods_scope =
25
+ if current_store.payment_methods.empty?
26
+ ::SolidusPaypalBraintree::Gateway.all
27
+ else
28
+ ::SolidusPaypalBraintree::Gateway.where(id: current_store.payment_method_ids)
29
+ end
30
+ @gateway = ::SolidusPaypalBraintree::Gateway.where(active: true).merge(store_payment_methods_scope).first!
31
+ end
32
+ end
33
+
34
+ def generate_token
35
+ @gateway.generate_token
36
+ rescue ::SolidusPaypalBraintree::Gateway::TokenGenerationDisabledError => e
37
+ Rails.logger.error e
38
+ nil
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module OrdersControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusPaypalBraintree::BraintreeCheckoutHelper
7
+ end
8
+
9
+ ::Spree::OrdersController.prepend(self)
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module Spree
5
+ module StoreDecorator
6
+ def self.prepended(base)
7
+ base.has_one :braintree_configuration, class_name: "SolidusPaypalBraintree::Configuration", dependent: :destroy
8
+ base.before_create :build_default_configuration
9
+ end
10
+
11
+ private
12
+
13
+ def build_default_configuration
14
+ build_braintree_configuration unless braintree_configuration
15
+ end
16
+
17
+ ::Spree::Store.prepend self
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module Spree
5
+ module UserDecorator
6
+ def self.prepended(base)
7
+ base.has_one :braintree_customer, class_name: 'SolidusPaypalBraintree::Customer', inverse_of: :user
8
+ end
9
+
10
+ ::Spree.user_class.prepend self
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module BraintreeAdminHelper
5
+ # Returns a link to the Braintree web UI for the given Braintree payment
6
+ def braintree_transaction_link(payment)
7
+ environment = payment.payment_method.preferred_environment == 'sandbox' ? 'sandbox' : 'www'
8
+ merchant_id = payment.payment_method.preferred_merchant_id
9
+ response_code = payment.response_code
10
+
11
+ return if response_code.blank?
12
+ return response_code if merchant_id.blank?
13
+
14
+ link_to(
15
+ response_code,
16
+ "https://#{environment}.braintreegateway.com/merchants/#{merchant_id}/transactions/#{response_code}",
17
+ title: 'Show payment on Braintree',
18
+ target: '_blank',
19
+ rel: 'noopener'
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ module BraintreeCheckoutHelper
5
+ def braintree_3ds_options_for(order)
6
+ ship_address = order.ship_address
7
+ bill_address = order.bill_address
8
+
9
+ {
10
+ nonce: nil, # populated after tokenization
11
+ bin: nil, # populated after tokenization
12
+ onLookupComplete: nil, # populated after tokenization
13
+ amount: order.total,
14
+ email: order.email,
15
+ billingAddress: {
16
+ givenName: bill_address.firstname,
17
+ surname: bill_address.lastname,
18
+ phoneNumber: bill_address.phone,
19
+ streetAddress: bill_address.address1,
20
+ extendedAddress: bill_address.address2,
21
+ locality: bill_address.city,
22
+ region: bill_address.state&.name,
23
+ postalCode: bill_address.zipcode,
24
+ countryCodeAlpha2: bill_address.country&.iso,
25
+ },
26
+ additionalInformation: {
27
+ shippingGivenName: ship_address.firstname,
28
+ shippingSurname: ship_address.lastname,
29
+ shippingPhone: ship_address.phone,
30
+ shippingAddress: {
31
+ streedAddress: ship_address.address1,
32
+ extendedAddress: ship_address.address2,
33
+ locality: ship_address.city,
34
+ region: ship_address.state&.name,
35
+ postalCode: ship_address.zipcode,
36
+ countryCodeAlpha2: ship_address.country&.iso,
37
+ }
38
+ }
39
+ }
40
+ end
41
+
42
+ def paypal_button_preference(key, store:)
43
+ store.braintree_configuration.preferences[key]
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationRecord < ActiveRecord::Base
2
4
  self.abstract_class = true
3
5
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ class Address
5
+ def initialize(spree_address)
6
+ @spree_address = spree_address
7
+ end
8
+
9
+ def to_json(*_args)
10
+ address_hash = {
11
+ line1: spree_address.address1,
12
+ line2: spree_address.address2,
13
+ city: spree_address.city,
14
+ postalCode: spree_address.zipcode,
15
+ countryCode: spree_address.country.iso,
16
+ phone: spree_address.phone,
17
+ recipientName: spree_address.full_name
18
+ }
19
+
20
+ if ::Spree::Config.address_requires_state && spree_address.country.states_required
21
+ address_hash[:state] = spree_address.state.name
22
+ end
23
+ address_hash.to_json
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :spree_address
29
+ end
30
+ end
@@ -1,5 +1,28 @@
1
- class SolidusPaypalBraintree::Configuration < ApplicationRecord
2
- belongs_to :store, class_name: 'Spree::Store'
1
+ # frozen_string_literal: true
3
2
 
4
- validates :store, presence: true
3
+ module SolidusPaypalBraintree
4
+ class Configuration < ::Spree::Base
5
+ PAYPAL_BUTTON_PREFERENCES = {
6
+ color: { availables: %w[gold blue silver white black], default: 'white' },
7
+ shape: { availables: %w[pill rect], default: 'rect' },
8
+ label: { availables: %w[checkout credit pay buynow paypal installment], default: 'checkout' },
9
+ tagline: { availables: %w[true false], default: 'false' },
10
+ layout: { availables: %w[horizontal vertical], default: 'horizontal' },
11
+ messaging: { availables: %w[true false], default: 'false' }
12
+ }.freeze
13
+
14
+ belongs_to :store, class_name: 'Spree::Store'
15
+
16
+ validates :store, presence: true
17
+
18
+ # Preferences for Paypal button
19
+ PAYPAL_BUTTON_PREFERENCES.each do |name, desc|
20
+ preference_name = "paypal_button_#{name}".to_sym
21
+ attribute_name = "preferred_#{preference_name}".to_sym
22
+
23
+ preference preference_name, :string, default: desc[:default]
24
+
25
+ validates attribute_name, inclusion: desc[:availables]
26
+ end
27
+ end
5
28
  end
@@ -1,4 +1,8 @@
1
- class SolidusPaypalBraintree::Customer < ApplicationRecord
2
- belongs_to :user, class_name: Spree::UserClassHandle.new
3
- has_many :sources, class_name: "SolidusPaypalBraintree::Source", inverse_of: :customer
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalBraintree
4
+ class Customer < ApplicationRecord
5
+ belongs_to :user, class_name: ::Spree::UserClassHandle.new, optional: true
6
+ has_many :sources, class_name: "SolidusPaypalBraintree::Source", inverse_of: :customer, dependent: :destroy
7
+ end
4
8
  end
@@ -1,22 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'braintree'
2
4
 
3
5
  module SolidusPaypalBraintree
4
6
  class Gateway < ::Spree::PaymentMethod
5
7
  include RequestProtection
6
8
 
9
+ class TokenGenerationDisabledError < StandardError; end
10
+
7
11
  # Error message from Braintree that gets returned by a non voidable transaction
8
- NON_VOIDABLE_STATUS_ERROR_REGEXP = /can only be voided if status is authorized/
12
+ NON_VOIDABLE_STATUS_ERROR_REGEXP = /can only be voided if status is authorized/.freeze
9
13
 
10
14
  TOKEN_GENERATION_DISABLED_MESSAGE = 'Token generation is disabled.' \
11
15
  ' To re-enable set the `token_generation_enabled` preference on the' \
12
- ' gateway to `true`.'.freeze
16
+ ' gateway to `true`.'
13
17
 
14
18
  ALLOWED_BRAINTREE_OPTIONS = [
15
19
  :device_data,
16
20
  :device_session_id,
17
21
  :merchant_account_id,
18
22
  :order_id
19
- ]
23
+ ].freeze
20
24
 
21
25
  VOIDABLE_STATUSES = [
22
26
  Braintree::Transaction::Status::SubmittedForSettlement,
@@ -35,9 +39,22 @@ module SolidusPaypalBraintree
35
39
  preference(:merchant_id, :string, default: nil)
36
40
  preference(:public_key, :string, default: nil)
37
41
  preference(:private_key, :string, default: nil)
42
+ preference(:http_open_timeout, :integer, default: 60)
43
+ preference(:http_read_timeout, :integer, default: 60)
38
44
  preference(:merchant_currency_map, :hash, default: {})
39
45
  preference(:paypal_payee_email_map, :hash, default: {})
40
46
 
47
+ # Which checkout flow to use (vault/checkout)
48
+ preference(:paypal_flow, :string, default: 'vault')
49
+
50
+ # A hash that gets passed to the `style` key when initializing the credit card fields.
51
+ # See https://developers.braintreepayments.com/guides/hosted-fields/styling/javascript/v3
52
+ preference(:credit_card_fields_style, :hash, default: {})
53
+
54
+ # A hash that gets its keys passed to the associated braintree field placeholder tag.
55
+ # Example: { number: "Enter card number", cvv: "Enter CVV", expirationDate: "mm/yy" }
56
+ preference(:placeholder_text, :hash, default: {})
57
+
41
58
  def partial_name
42
59
  "paypal_braintree"
43
60
  end
@@ -57,6 +74,8 @@ module SolidusPaypalBraintree
57
74
  merchant_id: preferred_merchant_id,
58
75
  public_key: preferred_public_key,
59
76
  private_key: preferred_private_key,
77
+ http_open_timeout: preferred_http_open_timeout,
78
+ http_read_timeout: preferred_http_read_timeout,
60
79
  logger: logger
61
80
  }
62
81
  end
@@ -73,7 +92,7 @@ module SolidusPaypalBraintree
73
92
  protected_request do
74
93
  result = braintree.transaction.sale(
75
94
  amount: dollars(money_cents),
76
- **transaction_options(source, gateway_options, true)
95
+ **transaction_options(source, gateway_options, submit_for_settlement: true)
77
96
  )
78
97
 
79
98
  Response.build(result)
@@ -200,7 +219,7 @@ module SolidusPaypalBraintree
200
219
  return if source.token.present? || source.customer.present? || source.nonce.nil?
201
220
 
202
221
  result = braintree.customer.create(customer_profile_params(payment))
203
- fail Spree::Core::GatewayError, result.message unless result.success?
222
+ fail ::Spree::Core::GatewayError, result.message unless result.success?
204
223
 
205
224
  customer = result.customer
206
225
 
@@ -213,12 +232,12 @@ module SolidusPaypalBraintree
213
232
  end
214
233
  end
215
234
 
235
+ # @raise [TokenGenerationDisabledError]
236
+ # If `preferred_token_generation_enabled` is false
237
+ #
216
238
  # @return [String]
217
239
  # The token that should be used along with the Braintree js-client sdk.
218
240
  #
219
- # returns an error message if `preferred_token_generation_enabled` is
220
- # set to false.
221
- #
222
241
  # @example
223
242
  # <script>
224
243
  # var token = #{Spree::Braintree::Gateway.first!.generate_token}
@@ -233,7 +252,10 @@ module SolidusPaypalBraintree
233
252
  # );
234
253
  # </script>
235
254
  def generate_token
236
- return TOKEN_GENERATION_DISABLED_MESSAGE unless preferred_token_generation_enabled
255
+ unless preferred_token_generation_enabled
256
+ raise TokenGenerationDisabledError, TOKEN_GENERATION_DISABLED_MESSAGE
257
+ end
258
+
237
259
  braintree.client_token.generate
238
260
  end
239
261
 
@@ -261,6 +283,12 @@ module SolidusPaypalBraintree
261
283
 
262
284
  private
263
285
 
286
+ # Whether to store this payment method in the PayPal Vault. This only works when the checkout
287
+ # flow is "vault", so make sure to call +super+ if you override it.
288
+ def store_in_vault
289
+ preferred_paypal_flow == 'vault'
290
+ end
291
+
264
292
  def logger
265
293
  Braintree::Configuration.logger.clone.tap do |logger|
266
294
  logger.level = Rails.logger.level
@@ -279,20 +307,24 @@ module SolidusPaypalBraintree
279
307
  JSON.parse(preference_string.gsub("=>", ":"))
280
308
  end
281
309
 
282
- def convert_preference_value(value, type)
310
+ def convert_preference_value(value, type, preference_encryptor = nil)
283
311
  if type == :hash && value.is_a?(String)
284
312
  value = to_hash(value)
285
313
  end
286
- super
314
+ if method(__method__).super_method.arity == 3
315
+ super
316
+ else
317
+ super(value, type)
318
+ end
287
319
  end
288
320
 
289
- def transaction_options(source, options, submit_for_settlement = false)
321
+ def transaction_options(source, options, submit_for_settlement: false)
290
322
  params = options.select do |key, _|
291
323
  ALLOWED_BRAINTREE_OPTIONS.include?(key)
292
324
  end
293
325
 
294
326
  params[:channel] = "Solidus"
295
- params[:options] = { store_in_vault_on_success: true }
327
+ params[:options] = { store_in_vault_on_success: store_in_vault }
296
328
 
297
329
  if submit_for_settlement
298
330
  params[:options][:submit_for_settlement] = true
@@ -349,21 +381,21 @@ module SolidusPaypalBraintree
349
381
  end
350
382
 
351
383
  def merchant_account_for(_source, options)
352
- if options[:currency]
353
- preferred_merchant_currency_map[options[:currency]]
354
- end
384
+ return unless options[:currency]
385
+
386
+ preferred_merchant_currency_map[options[:currency]]
355
387
  end
356
388
 
357
389
  def paypal_payee_email_for(source, options)
358
- if source.paypal?
359
- preferred_paypal_payee_email_map[options[:currency]]
360
- end
390
+ return unless source.paypal?
391
+
392
+ preferred_paypal_payee_email_map[options[:currency]]
361
393
  end
362
394
 
363
395
  def customer_profile_params(payment)
364
396
  params = {}
365
397
 
366
- if payment.source.try(:nonce)
398
+ if store_in_vault && payment.source.try(:nonce)
367
399
  params[:payment_method_nonce] = payment.source.nonce
368
400
  end
369
401