spree_braintree_vzero 3.5.1 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.travis.yml +39 -46
  4. data/Appraisals +19 -28
  5. data/README.md +6 -4
  6. data/app/assets/images/backend-settle.svg +3 -0
  7. data/app/assets/javascripts/spree/frontend/spree_braintree_vzero.js +3 -1
  8. data/app/controllers/spree/admin/payments_controller_decorator.rb +9 -4
  9. data/app/controllers/spree/orders_controller_decorator.rb +2 -0
  10. data/app/helpers/spree/admin/payment_methods_helper.rb +11 -5
  11. data/app/helpers/spree/braintree_helper.rb +2 -2
  12. data/app/models/concerns/spree/gateway/braintree_vzero/legacy_rails_patch.rb +20 -0
  13. data/app/models/spree/address_decorator.rb +2 -2
  14. data/app/models/spree/braintree_vzero/order_decorator.rb +155 -0
  15. data/app/models/spree/gateway/braintree_vzero_base/utils.rb +50 -3
  16. data/app/models/spree/gateway/braintree_vzero_base.rb +4 -0
  17. data/app/models/spree/gateway/braintree_vzero_drop_in_ui.rb +3 -1
  18. data/app/models/spree/gateway/braintree_vzero_hosted_fields.rb +3 -1
  19. data/app/models/spree/log_entry_decorator.rb +52 -0
  20. data/app/models/spree/payment_processing_decorator.rb +12 -0
  21. data/app/models/spree/preferences/preferable_decorator.rb +1 -1
  22. data/app/overrides/spree/orders/edit.rb +1 -1
  23. data/app/views/spree/braintree_vzero/_paypal_checkout.html.erb +77 -59
  24. data/app/views/spree/checkout/payment/braintree_vzero/_non_three_d_secure.html.erb +178 -0
  25. data/app/views/spree/checkout/payment/braintree_vzero/_payment.html.erb +32 -13
  26. data/app/views/spree/checkout/payment/braintree_vzero/_three_d_secure.html.erb +54 -14
  27. data/config/initializers/prepend_helpers.rb +3 -1
  28. data/db/migrate/20160112153422_add_admin_payment_to_spree_braintree_checkout.rb +1 -1
  29. data/gemfiles/spree_3_7.gemfile +2 -0
  30. data/gemfiles/spree_4_0.gemfile +1 -0
  31. data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +2 -5
  32. data/gemfiles/{spree_4_0_spree_auth_devise.gemfile → spree_4_2.gemfile} +2 -2
  33. data/gemfiles/spree_master.gemfile +1 -3
  34. data/lib/spree_braintree_vzero/engine.rb +3 -1
  35. data/lib/spree_braintree_vzero/version.rb +2 -2
  36. data/spec/controllers/spree/checkout_controller_spec.rb +1 -1
  37. data/spec/features/spree/admin/log_entries_spec.rb +18 -3
  38. data/spec/models/gateway/braintree_vzero_base_spec.rb +8 -5
  39. data/spec/models/spree/address_spec.rb +18 -0
  40. data/spec/support/vcr.rb +1 -1
  41. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/sends_address_data_when_address_is_new.yml +72 -0
  42. data/spree_braintree_vzero.gemspec +4 -5
  43. metadata +22 -49
  44. data/app/controllers/spree/user_sessions_controller_decorator.rb +0 -15
  45. data/app/models/spree/order_decorator.rb +0 -153
  46. data/gemfiles/spree_3_5_spree_auth_devise.gemfile +0 -13
  47. data/gemfiles/spree_3_7_spree_auth_devise.gemfile +0 -13
  48. data/gemfiles/spree_master_spree_auth_devise.gemfile +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 771cd46e87b5da70e1d9145ea38a084154a2329002598538b01da3f116409526
4
- data.tar.gz: 84ab1f12fd88cce62738419cc2de7cde03f73c1999d1b4b26c8160ce41fba489
3
+ metadata.gz: '09e02a4cf3c79e5be3e0964676effe42b4633d8aa4ed2b991b7f301c962f5545'
4
+ data.tar.gz: 646f04bc68b92b1347ab313ef428ab47171cab19d2db364f714ea57320750350
5
5
  SHA512:
6
- metadata.gz: 177d2a5421ae0e22794b8b8e5333b0ee0788f2f97654ee9f48c2571a6dcb07c3ee423be13f0ca84a624d8cc7edf0012fa2e6ac0ec5577632b2efacd4045ea63a
7
- data.tar.gz: e10a7db15dfa14c87648c1e8b26954442dd3471456bd25418f81d2fb5c0db72f7bc1557cd1c2e2e904eb997092958188182200e2148e54a4b2974e39a44114e5
6
+ metadata.gz: 1bb01d7427a967624664259f2cd13679b2c6678ef81b2265b81a8bc67af912dcdf3dea095764e1ad25f8c9646d89548b70c2b45aca8de31cd901749f64ed6eb3
7
+ data.tar.gz: d9ef517a4204b61b2eca65f4412ebd62e583c34e3a53517541ca4c8d48624203a96657ea571acca80fad2090793ddf188af6514d9e8d9323f7f7bda700126e7e
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
8
+ ignore:
9
+ - dependency-name: webmock
10
+ versions:
11
+ - 3.11.2
data/.travis.yml CHANGED
@@ -1,5 +1,16 @@
1
- sudo: required
2
- dist: trusty
1
+ os: linux
2
+ dist: bionic
3
+
4
+ addons:
5
+ apt:
6
+ sources:
7
+ - google-chrome
8
+ packages:
9
+ - google-chrome-stable
10
+
11
+ services:
12
+ - mysql
13
+ - postgresql
3
14
 
4
15
  language: ruby
5
16
 
@@ -7,57 +18,39 @@ env:
7
18
  - DB=postgres
8
19
  - DB=mysql
9
20
 
21
+ rvm:
22
+ - 2.7
23
+ - 3.0
24
+
10
25
  gemfile:
11
- - gemfiles/spree_3_5.gemfile
12
- - gemfiles/spree_3_5_spree_auth_devise.gemfile
13
26
  - gemfiles/spree_3_7.gemfile
14
- - gemfiles/spree_3_7_spree_auth_devise.gemfile
15
27
  - gemfiles/spree_4_0.gemfile
16
- - gemfiles/spree_4_0_spree_auth_devise.gemfile
28
+ - gemfiles/spree_4_1.gemfile
29
+ - gemfiles/spree_4_2.gemfile
17
30
  - gemfiles/spree_master.gemfile
18
- - gemfiles/spree_master_spree_auth_devise.gemfile
19
31
 
20
32
  script:
21
33
  - bundle exec rake test_app
22
34
  - bundle exec rake spec
23
35
  - bundle exec codeclimate-test-reporter
24
36
 
25
- rvm:
26
- - 2.3.8
27
- - 2.4.4
28
- - 2.5.1
29
-
30
- matrix:
31
- allow_failures:
32
- - gemfile: gemfiles/spree_master.gemfile
33
- - gemfile: gemfiles/spree_master_spree_auth_devise.gemfile
34
- exclude:
35
- - rvm: 2.3.8
36
- gemfile: gemfiles/spree_4_0.gemfile
37
- - rvm: 2.3.8
38
- gemfile: gemfiles/spree_4_0_spree_auth_devise.gemfile
39
- - rvm: 2.3.8
40
- gemfile: gemfiles/spree_master.gemfile
41
- - rvm: 2.3.8
42
- gemfile: gemfiles/spree_master_spree_auth_devise.gemfile
43
- - rvm: 2.4.4
44
- gemfile: gemfiles/spree_4_0.gemfile
45
- - rvm: 2.4.4
46
- gemfile: gemfiles/spree_4_0_spree_auth_devise.gemfile
47
- - rvm: 2.4.4
48
- gemfile: gemfiles/spree_master.gemfile
49
- - rvm: 2.4.4
50
- gemfile: gemfiles/spree_master_spree_auth_devise.gemfile
51
- - rvm: 2.5.1
52
- gemfile: gemfiles/spree_3_5.gemfile
53
- - rvm: 2.5.1
54
- gemfile: gemfiles/spree_3_5_spree_auth_devise.gemfile
55
-
56
-
57
- addons:
58
- apt:
59
- packages:
60
- - mysql-server-5.6
61
- - mysql-client-core-5.6
62
- - mysql-client-5.6
63
- postgresql: 9.4
37
+ jobs:
38
+ exclude:
39
+ - rvm: 3.0
40
+ gemfile: gemfiles/spree_3_7.gemfile
41
+ - rvm: 3.0
42
+ gemfile: gemfiles/spree_4_0.gemfile
43
+ - rvm: 3.0
44
+ gemfile: gemfiles/spree_4_1.gemfile
45
+ allow_failures:
46
+ - gemfile: gemfiles/spree_master.gemfile
47
+
48
+ before_install:
49
+ - mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
50
+
51
+ before_script:
52
+ - CHROME_MAIN_VERSION=`google-chrome-stable --version | sed -E 's/(^Google Chrome |\.[0-9]+ )//g'`
53
+ - CHROMEDRIVER_VERSION=`curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_MAIN_VERSION"`
54
+ - curl "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip" -O
55
+ - unzip chromedriver_linux64.zip -d ~/bin
56
+ - nvm install 14
data/Appraisals CHANGED
@@ -1,44 +1,35 @@
1
- appraise 'spree-3-5' do
2
- ENV['WITHOUT_SPREE_AUTH_DEVISE'] = 'true'
3
-
4
- gem 'spree', '~> 3.5.0'
5
- end
6
-
7
- appraise 'spree-3-5-spree-auth-devise' do
8
- gem 'spree', '~> 3.5.0'
9
- gem 'spree_auth_devise', '~> 3.3.0'
10
- end
11
-
12
1
  appraise 'spree-3-7' do
13
- ENV['WITHOUT_SPREE_AUTH_DEVISE'] = 'true'
14
-
2
+ gem 'sassc-rails'
15
3
  gem 'spree', '~> 3.7.1'
16
- end
17
-
18
- appraise 'spree-3-7-spree-auth-devise' do
19
- gem 'spree', '~> 3.7.1'
20
- gem 'spree_auth_devise', '~> 3.3.0'
4
+ gem 'pg'
5
+ gem 'mysql2'
6
+ gem 'webmock', '>= 2.3.1'
7
+ gem 'rails-controller-testing'
8
+ gem 'spree_auth_devise', '~> 3.5.2'
21
9
  end
22
10
 
23
11
  appraise 'spree-4-0' do
24
- ENV['WITHOUT_SPREE_AUTH_DEVISE'] = 'true'
25
-
26
- gem 'spree', '~> 4.0.0'
27
- end
28
-
29
- appraise 'spree-4-0-spree-auth-devise' do
30
12
  gem 'spree', '~> 4.0.0'
13
+ gem 'pg'
14
+ gem 'mysql2'
15
+ gem 'webmock', '>= 2.3.1'
16
+ gem 'rails-controller-testing'
31
17
  gem 'spree_auth_devise', '~> 4.0.0'
32
18
  end
33
19
 
34
- appraise 'spree-master' do
35
- ENV['WITHOUT_SPREE_AUTH_DEVISE'] = 'true'
20
+ appraise 'spree-4-1' do
21
+ gem 'spree', '~> 4.1.0'
22
+ gem 'spree_auth_devise', '~> 4.1.0'
23
+ gem 'rails-controller-testing'
24
+ end
36
25
 
37
- gem 'spree', github: 'spree/spree', branch: 'master'
26
+ appraise 'spree-4-2' do
27
+ gem 'spree', '~> 4.2.0'
28
+ gem 'spree_auth_devise', '~> 4.2.0'
38
29
  gem 'rails-controller-testing'
39
30
  end
40
31
 
41
- appraise 'spree-master-spree-auth-devise' do
32
+ appraise 'spree-master' do
42
33
  gem 'spree', github: 'spree/spree', branch: 'master'
43
34
  gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master'
44
35
  gem 'rails-controller-testing'
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Braintree v.zero extension for Spree Commerce
1
+ # Official Braintree + PayPal integration for Spree Commerce
2
2
 
3
- [![Build Status](https://travis-ci.org/spree-contrib/spree_braintree_vzero.svg?branch=master)](https://travis-ci.org/spree-contrib/spree_braintree_vzero) [![Code Climate](https://codeclimate.com/repos/560308aa6956801375000a4e/badges/9874199a656054d613cd/gpa.svg)](https://codeclimate.com/repos/560308aa6956801375000a4e/feed)
3
+ [![Build Status](https://travis-ci.com/spree-contrib/spree_braintree_vzero.svg?branch=master)](https://travis-ci.com/spree-contrib/spree_braintree_vzero) [![Code Climate](https://codeclimate.com/repos/560308aa6956801375000a4e/badges/9874199a656054d613cd/gpa.svg)](https://codeclimate.com/repos/560308aa6956801375000a4e/feed)
4
4
 
5
- This is the official Braintree v.zero extension for [Spree](https://spreecommerce.org). It supports:
5
+ This is the official Braintree + PayPal extension for [Spree Commerce](https://spreecommerce.org). It supports:
6
6
  * [Braintree Hosted Fields](https://github.com/spree-contrib/spree_braintree_vzero/wiki/1.-Hosted-Fields) - style the credit card form to match your UI with full PCI compliance
7
7
  * [PayPal Express Checkout](https://github.com/spree-contrib/spree_braintree_vzero/wiki/2.-PayPal-Express) - the fastest way for buyers to pay with PayPal - available on Spree for the first time
8
8
  * [Braintree Drop-in UI](http://github.com/spree-contrib/spree_braintree_vzero/wiki/3.-Drop-in-UI) - start accepting payments ASAP
@@ -43,7 +43,9 @@ Behind-the-scenes, this extension uses [Braintree Ruby SDK](https://github.com/b
43
43
 
44
44
  If your server was running, restart it so that it can find the assets properly.
45
45
 
46
+ ## Documentation
46
47
 
48
+ See [wiki](https://github.com/spree-contrib/spree_braintree_vzero/wiki) for more detailed documentation.
47
49
 
48
50
  ## Heroku installation (optional)
49
51
 
@@ -64,7 +66,7 @@ pull request.
64
66
 
65
67
  ## License
66
68
 
67
- Spree Braintree V.zero is copyright © 2015-2020
69
+ Spree Braintree V.zero is copyright © 2015-2021
68
70
  [Spark Solutions Sp. z o.o.][spark]. It is free software,
69
71
  and may be redistributed under the terms specified in the
70
72
  [LICENCE](LICENSE) file.
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">
2
+ <path fill="currentColor" d="M474.975 82.072c-5.79-5.77-15.123-5.77-20.874 0L173.92 362.589 58.76 247.27c-5.75-5.75-15.085-5.79-20.874 0-5.77 5.75-5.77 15.104 0 20.874l125.597 125.774c2.875 2.895 6.656 4.333 10.437 4.333 3.8 0 7.562-1.438 10.437-4.333l290.62-290.953c5.769-5.77 5.769-15.144 0-20.894z" fill-rule="nonzero" stroke="currentColor" stroke-width="24.024606"/>
3
+ </svg>
@@ -46,7 +46,8 @@ SpreeBraintreeVzero = {
46
46
  }
47
47
  }
48
48
 
49
- $(document).ready(function() {
49
+ $(document).ready(function () {
50
+ var event = new Event('spreebraintree:ready');
50
51
  paymentMethods = $('div[data-hook="checkout_payment_step"] input[type="radio"]').click(function (e) {
51
52
  SpreeBraintreeVzero.setSaveAndContinueVisibility();
52
53
  });
@@ -65,4 +66,5 @@ $(document).ready(function() {
65
66
  SpreeBraintreeVzero.addDeviceData();
66
67
  });
67
68
  SpreeBraintreeVzero.setSaveAndContinueVisibility();
69
+ window.dispatchEvent(event);
68
70
  })
@@ -1,6 +1,11 @@
1
1
  module Spree
2
2
  module Admin
3
3
  module PaymentsControllerDecorator
4
+ def self.prepended(base)
5
+ base.include Spree::BraintreeHelper
6
+ base.helper_method [:asset_available?, :options_from_braintree_payments]
7
+ end
8
+
4
9
  def create
5
10
  invoke_callbacks(:create, :before)
6
11
  @payment ||= @order.payments.build(object_params)
@@ -8,11 +13,11 @@ module Spree
8
13
  if @payment.payment_method.source_required?
9
14
  if params[:card].present? && params[:card] != 'new'
10
15
  @payment.source = @payment.payment_method.payment_source_class.find_by_id(params[:card])
11
- elsif @payment.payment_source.is_a?(Spree::Gateway::BraintreeVzeroBase)
12
- @payment.braintree_token = params[:payment_method_token]
13
- @payment.braintree_nonce = params[:payment_method_nonce]
14
- @payment.source = Spree::BraintreeCheckout.create!(admin_payment: true)
15
16
  end
17
+ elsif @payment.payment_source.is_a?(Spree::Gateway::BraintreeVzeroBase)
18
+ @payment.braintree_token = params[:payment_method_token]
19
+ @payment.braintree_nonce = params[:payment_method_nonce]
20
+ @payment.source = Spree::BraintreeCheckout.create!(admin_payment: true)
16
21
  end
17
22
 
18
23
  begin
@@ -7,6 +7,8 @@ module Spree
7
7
  end
8
8
 
9
9
  def process_paypal_express
10
+ return false if current_order.blank?
11
+
10
12
  if params[:paypal].blank? || params[:paypal][:payment_method_nonce].blank?
11
13
  # when user goes back from checkout, paypal express payments should be invalidated to ensure standard checkout flow
12
14
  current_order.invalidate_paypal_express_payments
@@ -84,11 +84,17 @@ module Spree
84
84
  keys.reject { |k| k == :currency_merchant_accounts }.map do |key|
85
85
  next unless object.has_preference?(key)
86
86
 
87
- content_tag(:div, class: 'form-group', 'data-hook' => "preferred_#{key}") do
88
- form.label("preferred_#{key}", Spree.t(key) + ': ') +
89
- preference_field_for(form, "preferred_#{key}", type: object.preference_type(key),
90
- values: object.send("preferred_#{key}_default").is_a?(Hash) ? object.send("preferred_#{key}_default")[:values] : nil,
91
- selected: object.preferences[key])
87
+ if object.preference_type(key) == :boolean
88
+ content_tag(:div, preference_field_for(form, "preferred_#{key}", type: object.preference_type(key)) +
89
+ form.label("preferred_#{key}", Spree.t(key), class: 'form-check-label'),
90
+ class: 'form-group form-check', id: [object.class.to_s.parameterize, 'preference', key].join('-'))
91
+ else
92
+ content_tag(:div, class: 'form-group', 'data-hook' => "preferred_#{key}") do
93
+ form.label("preferred_#{key}", Spree.t(key) + ': ') +
94
+ preference_field_for(form, "preferred_#{key}", type: object.preference_type(key),
95
+ values: object.send("preferred_#{key}_default").is_a?(Hash) ? object.send("preferred_#{key}_default")[:values] : nil,
96
+ selected: object.preferences[key])
97
+ end
92
98
  end
93
99
  end.join(' ').html_safe
94
100
  end
@@ -6,14 +6,14 @@ module Spree
6
6
  else
7
7
  []
8
8
  end
9
- additional_options + payment_methods.map do |method|
9
+ (additional_options + payment_methods.map do |method|
10
10
  text = if method.is_a?(Braintree::CreditCard)
11
11
  Spree.t('admin.vaulted_payments.credit_card', card_type: method.card_type, last_4: method.last_4)
12
12
  elsif method.is_a?(Braintree::PayPalAccount)
13
13
  Spree.t('admin.vaulted_payments.paypal', email: method.email)
14
14
  end
15
15
  "<option value='#{method.token}'>#{text}</option>"
16
- end.join.html_safe
16
+ end).join.html_safe
17
17
  end
18
18
 
19
19
  def asset_available?(logical_path)
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ class Gateway
3
+ module BraintreeVzero
4
+ module LegacyRailsPatch
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ # https://github.com/spree-contrib/spree_braintree_vzero/issues/216
10
+ def method_name_for_attributes_after_save
11
+ Rails::VERSION::STRING >= '5.1' ? :saved_changes : :changes
12
+ end
13
+
14
+ def attributes_after_save
15
+ method(method_name_for_attributes_after_save).call
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -2,9 +2,9 @@ module Spree
2
2
  module AddressDecorator
3
3
  def self.prepended(base)
4
4
  base.scope :vaulted_duplicates, ->(address) do
5
- where.not(id: address.id).
5
+ base.where.not(id: address.id).
6
6
  where.not(braintree_id: nil).
7
- where(address.attributes.except('id', 'updated_at', 'created_at', 'braintree_id'))
7
+ where(address.attributes.except('id', 'updated_at', 'created_at', 'braintree_id', 'preferences'))
8
8
  end
9
9
  end
10
10
 
@@ -0,0 +1,155 @@
1
+ module Spree
2
+ module BraintreeVzero
3
+ module OrderDecorator
4
+ def self.prepended(base)
5
+ base.state_machine.before_transition to: :complete, do: :process_paypal_express_payments
6
+ end
7
+
8
+ def save_paypal_address(type, address_hash)
9
+ return if address_hash.blank?
10
+ address = Spree::Address.new(prepare_address_hash(address_hash))
11
+ return unless address.save
12
+
13
+ update_column("#{type}_id", address.id)
14
+ end
15
+
16
+ def save_paypal_payment(options)
17
+ options[:source] = Spree::BraintreeCheckout.create!(options.slice(:paypal_email, :advanced_fraud_data))
18
+ payments.create(options.slice(:braintree_nonce, :payment_method_id, :source))
19
+ end
20
+
21
+ def set_billing_address
22
+ return if bill_address_id
23
+ return unless ship_address_id
24
+
25
+ address = Spree::Address.create(shipping_address.attributes.except('id', 'updated_at', 'created_at', 'braintree_id'))
26
+ update_column(:bill_address_id, address.try(:id))
27
+ end
28
+
29
+ # override needed to add braintree source attribute
30
+ def update_from_params(params, permitted_params, request_env = {})
31
+ success = false
32
+ @updating_params = params
33
+ run_callbacks :updating_from_params do
34
+ # Set existing card after setting permitted parameters because
35
+ # rails would slice parameters containg ruby objects, apparently
36
+ existing_card_id = @updating_params[:order] ? @updating_params[:order].delete(:existing_card) : nil
37
+
38
+ attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params).delete_if { |_k, v| v.nil? } : {}
39
+ payment_attributes = attributes[:payments_attributes].first if attributes[:payments_attributes].present?
40
+
41
+ if existing_card_id.present?
42
+ credit_card = Spree::CreditCard.find existing_card_id
43
+ if credit_card.user_id != user_id || credit_card.user_id.blank?
44
+ raise Spree::Core::GatewayError, Spree.t(:invalid_credit_card)
45
+ end
46
+
47
+ credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
48
+
49
+ attributes[:payments_attributes].first[:source] = credit_card
50
+ attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
51
+ attributes[:payments_attributes].first.delete :source_attributes
52
+ end
53
+
54
+ if payment_attributes.present?
55
+ payment_attributes[:request_env] = request_env
56
+
57
+ if (token = payment_attributes[:braintree_token]).present?
58
+ payment_attributes[:source] = Spree::BraintreeCheckout.create_from_token(token, payment_attributes[:payment_method_id])
59
+ elsif payment_attributes[:braintree_nonce].present?
60
+ payment_attributes[:source] = Spree::BraintreeCheckout.create_from_params(params)
61
+ end
62
+ end
63
+
64
+ success = update(attributes)
65
+ set_shipments_cost if shipments.any?
66
+ end
67
+
68
+ @updating_params = nil
69
+ success
70
+ end
71
+
72
+ def payment_required?
73
+ # default payment processing requires order to have state == payment
74
+ # so there is no need to divide this method for checkout steps and actual payment processing
75
+ return false if paid_with_paypal_express?
76
+ total.to_f > 0.0
77
+ end
78
+
79
+ def confirmation_required?
80
+ Spree::Config[:always_include_confirm_step] ||
81
+ payments.valid.map(&:payment_method).compact.any?(&:payment_profiles_supported?) ||
82
+ # setting payment_profiles_supported? for braintree gateways would require few additional changes in payments profiles system
83
+ braintree_confirmation_required? || state == 'confirm'
84
+ end
85
+
86
+ def paid_with_braintree?
87
+ payments.valid.map(&:payment_method).compact.any? { |p| p.is_a?(Spree::Gateway::BraintreeVzeroBase) }
88
+ end
89
+
90
+ def paid_with_paypal_express?
91
+ payments.valid.map(&:payment_method).compact.any? { |p| p.is_a?(Spree::Gateway::BraintreeVzeroPaypalExpress) }
92
+ end
93
+
94
+ def invalidate_paypal_express_payments
95
+ return unless paid_with_paypal_express?
96
+
97
+ payments.valid.each do |payment|
98
+ payment.invalidate! if payment.payment_method.is_a?(Spree::Gateway::BraintreeVzeroPaypalExpress)
99
+ end
100
+ end
101
+
102
+ def no_phone_number?
103
+ [ship_address, bill_address].each do |address|
104
+ return true if address.try(:phone).eql?(I18n.t('braintree.phone_number_placeholder'))
105
+ end
106
+ false
107
+ end
108
+
109
+ def remove_phone_number_placeholder
110
+ [ship_address, bill_address].each do |address|
111
+ address.update_column(:phone, nil) if address.try(:phone).eql?(I18n.t('braintree.phone_number_placeholder'))
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def braintree_confirmation_required?
118
+ paid_with_braintree? && state.eql?('payment')
119
+ end
120
+
121
+ def prepare_address_hash(hash)
122
+ hash.delete_if { |e| hash[e].eql?('undefined') }
123
+ country_id = Spree::Country.find_by(iso: hash.delete(:country)).try(:id)
124
+
125
+ hash[:country_id] = country_id
126
+ state_param = hash.delete(:state)
127
+ state = Spree::State.where('spree_states.abbr = :abbr OR upper(spree_states.name) = :name',
128
+ abbr: state_param, name: state_param.upcase).find_by(country_id: country_id)
129
+ hash[:state_id] = state.try(:id)
130
+ hash[:phone] ||= I18n.t('braintree.phone_number_placeholder')
131
+
132
+ return hash if hash[:full_name].blank?
133
+
134
+ full_name = hash.delete(:full_name).split(' ')
135
+ hash[:lastname] = full_name.slice!(-1)
136
+ hash[:firstname] = full_name.join(' ')
137
+ hash
138
+ end
139
+
140
+ def process_paypal_express_payments
141
+ return unless paid_with_paypal_express?
142
+
143
+ if payments.valid.empty?
144
+ order.errors.add(:base, Spree.t(:no_payment_found))
145
+ false
146
+ else
147
+ payments.valid.last.update(amount: total)
148
+ process_payments!
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ ::Spree::Order.prepend(Spree::BraintreeVzero::OrderDecorator)
@@ -39,7 +39,9 @@ module Spree
39
39
  def order_data(identifier, amount)
40
40
  identifier.merge(
41
41
  amount: amount,
42
- order_id: order.number
42
+ order_id: order.number,
43
+ line_items: collect_line_items,
44
+ shipping_amount: order_shipping
43
45
  )
44
46
  end
45
47
 
@@ -81,9 +83,11 @@ module Spree
81
83
 
82
84
  def payment_in_vault(data = {})
83
85
  store_ship_address = data['shipping_address_id'].blank?
84
- if gateway.preferred_store_payments_in_vault == 'store_only_on_success'
86
+
87
+ case gateway.preferred_store_payments_in_vault.to_s
88
+ when 'store_only_on_success'
85
89
  { store_in_vault_on_success: true, store_shipping_address_in_vault: store_ship_address }
86
- elsif gateway.preferred_store_payments_in_vault == 'store_all'
90
+ when 'store_all'
87
91
  { store_in_vault: true, store_shipping_address_in_vault: store_ship_address }
88
92
  else
89
93
  { store_in_vault: false }
@@ -102,6 +106,49 @@ module Spree
102
106
  'failed'
103
107
  end
104
108
  end
109
+
110
+ private
111
+
112
+ PAYPAL_MAX_LINEITEMS = 249
113
+
114
+ # Because of strange PayPal behaviour with accepting discounts
115
+ # the only solution to add needed discount (if it exist) to
116
+ # create a virtual Line Item with discount amount and with 'credit'
117
+ # and name it as 'discount' to be reflected in PayPal email to customer
118
+ #
119
+ def collect_line_items # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
120
+ items =
121
+ @order
122
+ .line_items
123
+ .reject { |li| li.price.zero? || li.quantity.zero? }
124
+ .map do |li|
125
+ {
126
+ name: li.name.truncate(127, omission: ''),
127
+ kind: 'debit',
128
+ quantity: li.quantity.to_s,
129
+ unit_amount: li.price.to_s,
130
+ unit_of_measure: 'unit',
131
+ product_code: li.sku,
132
+ total_amount: (li.price * li.quantity).to_s,
133
+ tax_amount: li.additional_tax_total.to_s
134
+ }
135
+ end.
136
+ take(PAYPAL_MAX_LINEITEMS - 1)
137
+ total = @order.adjustment_total.abs
138
+ return items if total.zero?
139
+
140
+ items.append({
141
+ name: 'discount',
142
+ kind: 'credit',
143
+ quantity: '1',
144
+ unit_amount: total.to_s,
145
+ total_amount: total.to_s
146
+ })
147
+ end
148
+
149
+ def order_shipping
150
+ @order.shipment_total.to_s
151
+ end
105
152
  end
106
153
  end
107
154
  end
@@ -87,6 +87,10 @@ module Spree
87
87
  provider::PaymentMethod.find(token)
88
88
  end
89
89
 
90
+ def source_required?
91
+ false
92
+ end
93
+
90
94
  private
91
95
 
92
96
  def sale(data, order, source = nil)
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class Gateway::BraintreeVzeroDropInUi < Spree::Gateway::BraintreeVzeroBase
3
+ include ::Spree::Gateway::BraintreeVzero::LegacyRailsPatch
4
+
3
5
  preference :dropin_container, :string, default: 'payment-form'
4
6
  preference :checkout_form_id, :string, default: 'checkout_form_payment'
5
7
  preference :error_messages_container_id, :string, default: 'content'
@@ -7,7 +9,7 @@ module Spree
7
9
  preference :'3dsecure', :boolean_select, default: false
8
10
 
9
11
  after_save :disable_hosted_gateways, if: proc {
10
- active? && (saved_changes.keys & %w[active id]).any?
12
+ active? && (attributes_after_save.keys & %w[active id]).any?
11
13
  }
12
14
 
13
15
  def method_type
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class Gateway::BraintreeVzeroHostedFields < Spree::Gateway::BraintreeVzeroBase
3
+ include ::Spree::Gateway::BraintreeVzero::LegacyRailsPatch
4
+
3
5
  preference :checkout_form_id, :string, default: 'checkout_form_payment'
4
6
  preference :error_messages_container_id, :string, default: 'content'
5
7
  preference :number_selector, :string, default: '#hosted-fields-number'
@@ -12,7 +14,7 @@ module Spree
12
14
  preference :'3dsecure', :boolean_select, default: false
13
15
 
14
16
  after_save :disable_dropin_gateways, if: proc {
15
- active? && (saved_changes.keys & %w[active id]).any?
17
+ active? && (attributes_after_save.keys & %w[active id]).any?
16
18
  }
17
19
 
18
20
  def method_type