spree_gateway 3.7.4 → 3.9.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +31 -38
  3. data/Appraisals +4 -10
  4. data/app/models/spree/check.rb +41 -0
  5. data/app/models/spree/gateway/stripe_ach_gateway.rb +60 -0
  6. data/app/models/spree/gateway/stripe_elements_gateway.rb +50 -0
  7. data/app/models/spree/gateway/stripe_gateway.rb +1 -0
  8. data/app/models/spree/order_decorator.rb +28 -0
  9. data/app/models/spree/payment_decorator.rb +34 -0
  10. data/app/views/spree/checkout/_payment_confirm.html.erb +34 -0
  11. data/config/initializers/spree_permitted_attributes.rb +5 -0
  12. data/config/locales/en.yml +23 -0
  13. data/config/routes.rb +12 -0
  14. data/db/migrate/20200317135551_add_spree_check_payment_source.rb +22 -0
  15. data/db/migrate/20200422114908_add_intent_key_to_payment.rb +5 -0
  16. data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +1 -1
  17. data/gemfiles/{spree_4_0.gemfile → spree_4_2.gemfile} +1 -1
  18. data/lib/active_merchant/billing/stripe_gateway_decorator.rb +13 -0
  19. data/lib/controllers/spree/api/v2/storefront/intents_controller.rb +27 -0
  20. data/lib/spree_frontend/controllers/spree/checkout_controller_decorator.rb +19 -0
  21. data/lib/spree_gateway.rb +1 -0
  22. data/lib/spree_gateway/engine.rb +8 -0
  23. data/lib/spree_gateway/version.rb +1 -1
  24. data/lib/views/backend/spree/admin/payments/source_forms/_stripe_ach.html.erb +86 -0
  25. data/lib/views/backend/spree/admin/payments/source_forms/_stripe_apple_pay.html.erb +0 -0
  26. data/lib/views/backend/spree/admin/payments/source_views/_stripe_ach.html.erb +21 -0
  27. data/lib/views/backend/spree/admin/payments/source_views/_stripe_apple_pay.html.erb +1 -0
  28. data/lib/views/frontend/spree/checkout/payment/_stripe_ach.html.erb +81 -0
  29. data/lib/views/frontend/spree/checkout/payment/_stripe_ach_verify.html.erb +16 -0
  30. data/lib/views/frontend/spree/checkout/payment/_stripe_apple_pay.html.erb +10 -1
  31. data/lib/views/frontend/spree/checkout/payment/_stripe_elements.html.erb +2 -1
  32. data/spec/factories/check_factory.rb +10 -0
  33. data/spec/features/admin/stripe_elements_payment_spec.rb +13 -3
  34. data/spec/features/stripe_checkout_spec.rb +3 -0
  35. data/spec/features/stripe_elements_3ds_checkout_spec.rb +106 -0
  36. data/spec/models/gateway/stripe_ach_gateway_spec.rb +186 -0
  37. data/spec/models/gateway/stripe_gateway_spec.rb +1 -0
  38. data/spec/spec_helper.rb +7 -65
  39. data/spec/support/within_stripe_3ds_popup.rb +10 -0
  40. data/spree_gateway.gemspec +11 -22
  41. metadata +44 -271
  42. data/gemfiles/spree_3_7.gemfile +0 -9
  43. data/spec/support/capybara_helper.rb +0 -15
@@ -91,7 +91,10 @@ describe "Stripe checkout", type: :feature do
91
91
  expect(page).to have_css('.has-error #card_code.error')
92
92
  end
93
93
 
94
+ # this scenario will not occur on Spree 4.2 due to swapping jquery.payment to cleave
95
+ # see https://github.com/spree/spree/pull/10363
94
96
  it "shows an error with invalid expiry month field", :js => true do
97
+ skip if Spree.version.to_f >= 4.2
95
98
  fill_in 'card_number', with: '4242 4242 4242 4242'
96
99
  fill_in 'card_expiry', :with => "00 / #{Time.now.year + 1}"
97
100
  fill_in 'card_code', with: '123'
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Stripe Elements 3ds checkout', type: :feature, js: true do
6
+ let!(:product) { create(:product, name: 'RoR Mug') }
7
+ let!(:stripe_payment_method) do
8
+ Spree::Gateway::StripeElementsGateway.create!(
9
+ name: 'Stripe',
10
+ preferred_secret_key: 'sk_test_VCZnDv3GLU15TRvn8i2EsaAN',
11
+ preferred_publishable_key: 'pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg',
12
+ preferred_intents: preferred_intents
13
+ )
14
+ end
15
+
16
+ before do
17
+ user = create(:user)
18
+ order = OrderWalkthrough.up_to(:confirm)
19
+ expect(order).to receive(:confirmation_required?).and_return(true).at_least(:once)
20
+
21
+ order.reload
22
+ order.user = user
23
+ payment = order.payments.first
24
+ payment.source = create(:credit_card, number: card_number)
25
+ payment.save!
26
+
27
+ allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order)
28
+ allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: user)
29
+ allow_any_instance_of(Spree::CheckoutController).to receive_messages(skip_state_validation?: true)
30
+ allow_any_instance_of(Spree::OrdersController).to receive_messages(try_spree_current_user: user)
31
+
32
+ add_to_cart(product)
33
+ click_link 'checkout'
34
+ click_button 'Place Order'
35
+ end
36
+
37
+ describe 'when intents are disabled' do
38
+ let(:preferred_intents) { false }
39
+
40
+ context 'and credit card does not require 3ds authentication' do
41
+ let(:card_number) { '4242424242424242' }
42
+
43
+ it 'should place order without 3ds authentication' do
44
+ expect(page).to have_content('Order placed successfully')
45
+ order = Spree::Order.complete.last
46
+ expect(page.current_url).to include("/orders/#{order.number}")
47
+ expect(page).to have_content(order.number)
48
+ end
49
+ end
50
+
51
+ context 'and credit card does require 3ds authentication' do
52
+ let(:card_number) { '4000000000003220' }
53
+
54
+ it 'should not place the order' do
55
+ expect(page).to have_content('Your card was declined. This transaction requires authentication.')
56
+ expect(Spree::Order.complete.last).to be_nil
57
+ end
58
+ end
59
+ end
60
+
61
+ describe 'when intents are enabled' do
62
+ let(:preferred_intents) { true }
63
+
64
+ context 'and credit card does not require 3ds authentication' do
65
+ let(:card_number) { '4242424242424242' }
66
+
67
+ it 'should successfully place order without 3ds authentication' do
68
+
69
+ expect(page).to have_content('Order placed successfully')
70
+ order = Spree::Order.complete.last
71
+ expect(page.current_url).to include("/orders/#{order.number}")
72
+ expect(page).to have_content(order.number)
73
+ end
74
+ end
75
+
76
+ context 'when credit card does require 3ds authentication' do
77
+ let(:card_number) { '4000000000003220' }
78
+
79
+ context 'and authentication is successful' do
80
+ it 'should place order after 3ds authentication' do
81
+ within_stripe_3ds_popup do
82
+ click_button('Complete')
83
+ end
84
+
85
+ expect(page).to have_content('Order placed successfully')
86
+ order = Spree::Order.complete.last
87
+ expect(page.current_url).to include("/orders/#{order.number}")
88
+ expect(page).to have_content(order.number)
89
+ end
90
+ end
91
+
92
+ context 'and authentication is unsuccessful' do
93
+ it 'should not place order after 3ds authentication' do
94
+ within_stripe_3ds_popup do
95
+ click_button('Fail')
96
+ end
97
+
98
+ expect(page).to_not have_content('Order placed successfully')
99
+ expect(page).to have_content('We are unable to authenticate your payment method.')
100
+ expect(page).to have_content('Please choose a different payment method and try again.')
101
+ expect(Spree::Order.complete.last).to be_nil
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::StripeAchGateway do
4
+ let(:secret_key) { 'key' }
5
+ let(:email) { 'customer@example.com' }
6
+ let(:source) { Spree::Check.new }
7
+ let(:payment) {
8
+ double('Spree::Payment',
9
+ source: source,
10
+ order: double('Spree::Order',
11
+ email: email,
12
+ bill_address: bill_address,
13
+ user: double('Spree::User',
14
+ email: email)
15
+ )
16
+ )
17
+ }
18
+ let(:provider) do
19
+ double('provider').tap do |p|
20
+ p.stub(:purchase)
21
+ p.stub(:authorize)
22
+ p.stub(:capture)
23
+ p.stub(:verify)
24
+ end
25
+ end
26
+
27
+ before do
28
+ subject.preferences = { secret_key: secret_key }
29
+ subject.stub(:options_for_purchase_or_auth).and_return(['money','check','opts'])
30
+ subject.stub(:provider).and_return provider
31
+ end
32
+
33
+ describe '#create_profile' do
34
+ before do
35
+ payment.source.stub(:update!)
36
+ end
37
+
38
+ context 'with an order that has a bill address' do
39
+ let(:bill_address) {
40
+ double('Spree::Address',
41
+ address1: '123 Happy Road',
42
+ address2: 'Apt 303',
43
+ city: 'Suzarac',
44
+ zipcode: '95671',
45
+ state: double('Spree::State', name: 'Oregon'),
46
+ country: double('Spree::Country', name: 'United States')
47
+ )
48
+ }
49
+
50
+ it 'stores the bill address with the provider' do
51
+ subject.provider.should_receive(:store).with(payment.source, {
52
+ email: email,
53
+ login: secret_key,
54
+
55
+ address: {
56
+ address1: '123 Happy Road',
57
+ address2: 'Apt 303',
58
+ city: 'Suzarac',
59
+ zip: '95671',
60
+ state: 'Oregon',
61
+ country: 'United States'
62
+ }
63
+ }).and_return double.as_null_object
64
+
65
+ subject.create_profile payment
66
+ end
67
+ end
68
+
69
+ context 'with an order that does not have a bill address' do
70
+ let(:bill_address) { nil }
71
+
72
+ it 'does not store a bill address with the provider' do
73
+ subject.provider.should_receive(:store).with(payment.source, {
74
+ email: email,
75
+ login: secret_key
76
+ }).and_return double.as_null_object
77
+
78
+ subject.create_profile payment
79
+ end
80
+
81
+ end
82
+
83
+ context 'with a check represents payment_profile' do
84
+ let(:source) { Spree::Check.new(gateway_payment_profile_id: 'tok_profileid') }
85
+ let(:bill_address) { nil }
86
+
87
+ it 'stores the profile_id as a check' do
88
+ subject.provider.should_receive(:store).with(source.gateway_payment_profile_id, anything).and_return double.as_null_object
89
+
90
+ subject.create_profile payment
91
+ end
92
+ end
93
+ end
94
+
95
+ context 'purchasing' do
96
+ after do
97
+ subject.purchase(19.99, 'check', {})
98
+ end
99
+
100
+ it 'send the payment to the provider' do
101
+ provider.should_receive(:purchase).with('money', 'check', 'opts')
102
+ end
103
+ end
104
+
105
+ context 'authorizing' do
106
+ after do
107
+ subject.authorize(19.99, 'check', {})
108
+ end
109
+
110
+ it 'send the authorization to the provider' do
111
+ provider.should_receive(:authorize).with('money', 'check', 'opts')
112
+ end
113
+ end
114
+
115
+ context 'verifying' do
116
+ after do
117
+ subject.verify(source, {})
118
+ end
119
+
120
+ it 'send the verify to the provider' do
121
+ provider.should_receive(:verify).with(source, anything)
122
+ end
123
+ end
124
+
125
+ context 'capturing' do
126
+
127
+ after do
128
+ subject.capture(1234, 'response_code', {})
129
+ end
130
+
131
+ it 'convert the amount to cents' do
132
+ provider.should_receive(:capture).with(1234, anything, anything)
133
+ end
134
+
135
+ it 'use the response code as the authorization' do
136
+ provider.should_receive(:capture).with(anything, 'response_code', anything)
137
+ end
138
+ end
139
+
140
+ context 'capture with payment class' do
141
+ let(:gateway) do
142
+ gateway = described_class.new(active: true)
143
+ gateway.set_preference :secret_key, secret_key
144
+ gateway.stub(:options_for_purchase_or_auth).and_return(['money', 'check', 'opts'])
145
+ gateway.stub(:provider).and_return provider
146
+ gateway.stub source_required: true
147
+ gateway
148
+ end
149
+
150
+ let(:order) { Spree::Order.create }
151
+
152
+ let(:check) do
153
+ # mock_model(Spree::Check, :gateway_customer_profile_id => 'cus_abcde',
154
+ # :imported => false)
155
+ create :check, gateway_customer_profile_id: 'cus_abcde', imported: false
156
+ end
157
+
158
+ let(:payment) do
159
+ payment = Spree::Payment.new
160
+ payment.source = check
161
+ payment.order = order
162
+ payment.payment_method = gateway
163
+ payment.amount = 98.55
164
+ payment.state = 'pending'
165
+ payment.response_code = '12345'
166
+ payment
167
+ end
168
+
169
+ after do
170
+ payment.capture!
171
+ end
172
+
173
+ let!(:success_response) do
174
+ double('success_response',
175
+ success?: true,
176
+ authorization: '123',
177
+ avs_result: { 'code' => 'avs-code' },
178
+ cvv_result: { 'code' => 'cvv-code', 'message' => 'CVV Result' },
179
+ params: {})
180
+ end
181
+
182
+ it 'gets correct amount' do
183
+ provider.should_receive(:capture).with(9855, '12345', anything).and_return(success_response)
184
+ end
185
+ end
186
+ end
@@ -182,6 +182,7 @@ describe Spree::Gateway::StripeGateway do
182
182
 
183
183
  let!(:success_response) do
184
184
  double('success_response', :success? => true,
185
+ :params => {},
185
186
  :authorization => '123',
186
187
  :avs_result => { 'code' => 'avs-code' },
187
188
  :cvv_result => { 'code' => 'cvv-code', 'message' => "CVV Result"})
@@ -1,68 +1,10 @@
1
- require 'simplecov'
2
- SimpleCov.start 'rails'
1
+ # Configure Rails Environment
2
+ ENV['RAILS_ENV'] = 'test'
3
3
 
4
- ENV["RAILS_ENV"] = "test"
4
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
5
5
 
6
- require File.expand_path("../dummy/config/environment.rb", __FILE__)
6
+ require 'spree_dev_tools/rspec/spec_helper'
7
7
 
8
- require 'rspec/rails'
9
- require 'rspec/active_model/mocks'
10
- require 'capybara/rspec'
11
- require 'capybara/rails'
12
- require 'capybara-screenshot/rspec'
13
- require "selenium-webdriver"
14
- require 'webdrivers'
15
- require 'database_cleaner'
16
- require 'ffaker'
17
- require 'rspec/active_model/mocks'
18
- require 'pry'
19
-
20
- Dir[File.join(File.dirname(__FILE__), "support", "**", "*.rb")].each { |f| require f }
21
-
22
- require 'spree/testing_support/factories'
23
- require 'spree/testing_support/order_walkthrough'
24
- require 'spree/testing_support/preferences'
25
- require 'spree/testing_support/capybara_ext'
26
- require 'spree/testing_support/authorization_helpers'
27
-
28
- FactoryBot.find_definitions
29
-
30
- RSpec.configure do |config|
31
- config.infer_spec_type_from_file_location!
32
- config.mock_with :rspec do |mock|
33
- mock.syntax = [:should, :expect]
34
- end
35
- config.raise_errors_for_deprecations!
36
- config.use_transactional_fixtures = false
37
- #config.filter_run focus: true
38
- #config.filter_run_excluding slow: true
39
-
40
- config.include FactoryBot::Syntax::Methods
41
- config.include Spree::TestingSupport::Preferences
42
-
43
- config.before :suite do
44
- DatabaseCleaner.strategy = :transaction
45
- DatabaseCleaner.clean_with :truncation
46
- end
47
-
48
- config.before do
49
- DatabaseCleaner.strategy = RSpec.current_example.metadata[:js] ? :truncation : :transaction
50
- DatabaseCleaner.start
51
- reset_spree_preferences
52
- create(:store)
53
- end
54
-
55
- config.after do
56
- DatabaseCleaner.clean
57
- end
58
-
59
- Capybara.register_driver :chrome do |app|
60
- Capybara::Selenium::Driver.new app,
61
- browser: :chrome,
62
- options: Selenium::WebDriver::Chrome::Options.new(
63
- args: %w[no-sandbox disable-dev-shm-usage disable-popup-blocking headless disable-gpu window-size=1920,1080 --enable-features=NetworkService,NetworkServiceInProcess --disable-features=VizDisplayCompositor],
64
- log_level: :error
65
- )
66
- end
67
- Capybara.javascript_driver = :chrome
68
- end
8
+ # Requires supporting ruby files with custom matchers and macros, etc,
9
+ # in spec/support/ and its subdirectories.
10
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].sort.each { |f| require f }
@@ -0,0 +1,10 @@
1
+ def within_stripe_3ds_popup
2
+ using_wait_time(10) do
3
+ within_frame 0 do
4
+ within_frame 0 do
5
+ expect(page).to have_text('3D Secure 2 Test Page', normalize_ws: true)
6
+ yield
7
+ end
8
+ end
9
+ end
10
+ end
@@ -16,35 +16,24 @@ Gem::Specification.new do |s|
16
16
  s.homepage = 'https://spreecommerce.org'
17
17
  s.license = 'BSD-3-Clause'
18
18
 
19
+ s.metadata = {
20
+ "bug_tracker_uri" => "https://github.com/spree/spree_gateway/issues",
21
+ "changelog_uri" => "https://github.com/spree/spree_gateway/releases/tag/v#{s.version}",
22
+ "documentation_uri" => "https://guides.spreecommerce.org/",
23
+ "source_code_uri" => "https://github.com/spree/spree_gateway/tree/v#{s.version}",
24
+ }
25
+
19
26
  s.files = `git ls-files`.split("\n")
20
27
  s.test_files = `git ls-files -- spec/*`.split("\n")
21
28
  s.require_path = 'lib'
22
29
  s.requirements << 'none'
23
30
 
24
- spree_version = '>= 3.1.0', '< 5.0'
31
+ spree_version = '>= 3.7.0', '< 5.0'
25
32
  s.add_dependency 'spree_core', spree_version
26
33
  s.add_dependency 'spree_extension'
34
+ s.add_dependency 'deface'
27
35
 
28
- s.add_development_dependency 'appraisal'
29
- s.add_development_dependency 'bootstrap-sass'
30
- s.add_development_dependency 'braintree'
31
- s.add_development_dependency 'capybara'
32
- s.add_development_dependency 'capybara-screenshot'
33
- s.add_development_dependency 'coffee-rails'
34
- s.add_development_dependency 'database_cleaner'
35
- s.add_development_dependency 'factory_bot', '~> 4.7'
36
- s.add_development_dependency 'ffaker'
37
- s.add_development_dependency 'guard-rspec'
38
- s.add_development_dependency 'launchy'
39
- s.add_development_dependency 'mysql2'
40
- s.add_development_dependency 'pg'
41
- s.add_development_dependency 'pry'
42
- s.add_development_dependency 'puma'
36
+ s.add_development_dependency 'braintree', '~>2.78'
43
37
  s.add_development_dependency 'rspec-activemodel-mocks'
44
- s.add_development_dependency 'rspec-rails'
45
- s.add_development_dependency 'sass-rails'
46
- s.add_development_dependency 'selenium-webdriver'
47
- s.add_development_dependency 'webdrivers'
48
- s.add_development_dependency 'simplecov'
49
- s.add_development_dependency 'sqlite3'
38
+ s.add_development_dependency 'spree_dev_tools'
50
39
  end