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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ab0991265fe493eef93a0ee098892bd46682c56ab554ae773831ae2b154115b
4
- data.tar.gz: ea290e80beadd38e792c4218180bad4a65505ac038e7c5b21e19f0a18d63cb24
3
+ metadata.gz: b54b6ec2e6137ae258ac3d108c715f3a8bf2e534c8f3ee13946529831fdf6c8e
4
+ data.tar.gz: 95e413abf6077ee0ed9cf92d260be0ad44de0888334d94b6d60b8b68002e85fd
5
5
  SHA512:
6
- metadata.gz: 0ce78bd6a8b62af2de0ac1e73abb6844ea16385d3ff121c033070b6c965f1e87e52d7a3eced0d35ced01d127eef3057dd8132f561555ac89780363b179e2e40f
7
- data.tar.gz: 9a0ac382a6aa612965f0ad22950ec96a907c069c71a06e8f319987582ad64691cb718bbda349ddd7b7d6accb9bc23f548e43908fb157b692c13395d468c8f96c
6
+ metadata.gz: b94ebcae1a0004d780547e34f73764093a84ab49a36a6c31bbc5177b7094c6c609a545da6684d262eb65155550c8c6eb1eb528118a1258ef2b090512c862afa7
7
+ data.tar.gz: 2facdc1ebd849fe10fffb1b2ab0daebef9e90d7178bcad1a29fd3cd896935fc12c2f828dd48e01644557f3abf21320b76402c150ba8399b309fbbc5d9a83a668
@@ -1,51 +1,44 @@
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
 
6
- addons:
7
- chrome: stable
8
- postgresql: 9.4
17
+ rvm:
18
+ - 2.6
9
19
 
10
20
  env:
11
- - DB=postgres
12
21
  - DB=mysql
22
+ - DB=postgres
13
23
 
14
24
  gemfile:
15
- - gemfiles/spree_3_5.gemfile
16
- - gemfiles/spree_3_7.gemfile
17
- - gemfiles/spree_4_0.gemfile
25
+ # - gemfiles/spree_4_1.gemfile
26
+ - gemfiles/spree_4_2.gemfile
18
27
  - gemfiles/spree_master.gemfile
19
28
 
20
- script:
21
- - bundle exec rake test_app
22
- - bundle exec rake spec
23
-
24
- rvm:
25
- - 2.5.1
26
- - 2.4.4
27
- - 2.3.8
28
-
29
- matrix:
30
- allow_failures:
31
- - gemfile: gemfiles/spree_master.gemfile
32
- exclude:
33
- - rvm: 2.3.8
34
- gemfile: gemfiles/spree_master.gemfile
35
- - rvm: 2.4.4
36
- gemfile: gemfiles/spree_master.gemfile
37
- - rvm: 2.3.8
38
- gemfile: gemfiles/spree_4_0.gemfile
39
- - rvm: 2.4.4
40
- gemfile: gemfiles/spree_4_0.gemfile
41
- - rvm: 2.5.1
42
- gemfile: gemfiles/spree_3_5.gemfile
29
+ jobs:
30
+ allow_failures:
31
+ - gemfile: gemfiles/spree_master.gemfile
43
32
 
44
33
  before_install:
45
34
  - mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
46
- - wget -N https://chromedriver.storage.googleapis.com/2.35/chromedriver_linux64.zip -P ~/
47
- - unzip ~/chromedriver_linux64.zip -d ~/
48
- - rm ~/chromedriver_linux64.zip
49
- - sudo mv -f ~/chromedriver /usr/local/share/
50
- - sudo chmod +x /usr/local/share/chromedriver
51
- - sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
35
+
36
+ before_script:
37
+ - CHROME_MAIN_VERSION=`google-chrome-stable --version | sed -E 's/(^Google Chrome |\.[0-9]+ )//g'`
38
+ - CHROMEDRIVER_VERSION=`curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_MAIN_VERSION"`
39
+ - curl "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip" -O
40
+ - unzip chromedriver_linux64.zip -d ~/bin
41
+
42
+ script:
43
+ - bundle exec rake test_app
44
+ - bundle exec rake spec
data/Appraisals CHANGED
@@ -1,16 +1,10 @@
1
- appraise 'spree-3-5' do
2
- gem 'spree', '~> 3.5.0'
1
+ appraise 'spree-4-1' do
2
+ gem 'spree', '~> 4.1.0'
3
3
  gem 'rails-controller-testing'
4
4
  end
5
5
 
6
- appraise 'spree-3-7' do
7
- gem 'spree', '~> 3.7.0'
8
- gem 'sass-rails'
9
- gem 'rails-controller-testing'
10
- end
11
-
12
- appraise 'spree-4-0' do
13
- gem 'spree', '~> 4.0.0.rc2'
6
+ appraise 'spree-4-2' do
7
+ gem 'spree', '~> 4.2.0.beta'
14
8
  gem 'rails-controller-testing'
15
9
  end
16
10
 
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ class Check < Spree::Base
3
+
4
+ attr_accessor :imported
5
+
6
+ belongs_to :payment_method
7
+ belongs_to :user, class_name: Spree.user_class.to_s, foreign_key: 'user_id',
8
+ optional: true
9
+ has_many :payments, as: :source
10
+
11
+ scope :with_payment_profile, -> { where.not(gateway_customer_profile_id: nil) }
12
+
13
+ validates :account_holder_name, presence: true
14
+ validates :account_holder_type, presence: true, inclusion: { in: %w[Individual Company] }
15
+ validates :account_number, presence: true, numericality: { only_integer: true }
16
+ validates :routing_number, presence: true, numericality: { only_integer: true }
17
+
18
+ def has_payment_profile?
19
+ gateway_customer_profile_id.present? || gateway_payment_profile_id.present?
20
+ end
21
+
22
+ def actions
23
+ %w[capture void credit]
24
+ end
25
+
26
+ def can_capture?(payment)
27
+ payment.pending? || payment.checkout?
28
+ end
29
+
30
+ # Indicates whether its possible to void the payment.
31
+ def can_void?(payment)
32
+ !payment.failed? && !payment.void?
33
+ end
34
+
35
+ # Indicates whether its possible to credit the payment. Note that most gateways require that the
36
+ # payment be settled first which generally happens within 12-24 hours of the transaction.
37
+ def can_credit?(payment)
38
+ payment.completed? && payment.credit_allowed > 0
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ module Spree
2
+ class Gateway::StripeAchGateway < Gateway::StripeGateway
3
+
4
+ def method_type
5
+ 'stripe_ach'
6
+ end
7
+
8
+ def payment_source_class
9
+ Check
10
+ end
11
+
12
+ def verify(source, **gateway_options)
13
+ provider.verify(source, gateway_options)
14
+ end
15
+
16
+ def create_profile(payment)
17
+ return unless payment.source&.gateway_customer_profile_id.nil?
18
+
19
+ options = {
20
+ email: payment.order.user&.email || payment.order.email,
21
+ login: preferred_secret_key,
22
+ }.merge! address_for(payment)
23
+
24
+ source = payment.source
25
+ bank_account = if source.gateway_payment_profile_id.present?
26
+ source.gateway_payment_profile_id
27
+ else
28
+ source
29
+ end
30
+
31
+ response = provider.store(bank_account, options)
32
+
33
+ if response.success?
34
+ payment.source.update!({
35
+ gateway_customer_profile_id: response.params['id'],
36
+ gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
37
+ })
38
+
39
+ else
40
+ payment.send(:gateway_error, response.message)
41
+ end
42
+ end
43
+
44
+ def supports?(_source)
45
+ true
46
+ end
47
+
48
+ def available_for_order?(order)
49
+ # Stripe ACH payments are supported only for US customers
50
+ # Therefore we need to check order's addresses
51
+ return unless order.ship_address_id && order.bill_address_id
52
+ return unless order.ship_address && order.bill_address_id
53
+
54
+ usa_id = ::Spree::Country.find_by(iso: 'US')&.id
55
+ return false unless usa_id
56
+
57
+ order.ship_address.country_id == usa_id && order.bill_address.country_id == usa_id
58
+ end
59
+ end
60
+ end
@@ -1,7 +1,57 @@
1
1
  module Spree
2
2
  class Gateway::StripeElementsGateway < Gateway::StripeGateway
3
+ preference :intents, :boolean, default: true
4
+
3
5
  def method_type
4
6
  'stripe_elements'
5
7
  end
8
+
9
+ def provider_class
10
+ if get_preference(:intents)
11
+ ActiveMerchant::Billing::StripePaymentIntentsGateway
12
+ else
13
+ ActiveMerchant::Billing::StripeGateway
14
+ end
15
+ end
16
+
17
+ def create_profile(payment)
18
+ return unless payment.source.gateway_customer_profile_id.nil?
19
+
20
+ options = {
21
+ email: payment.order.email,
22
+ login: preferred_secret_key
23
+ }.merge! address_for(payment)
24
+
25
+ source = update_source!(payment.source)
26
+ creditcard = source.gateway_payment_profile_id.present? ? source.gateway_payment_profile_id : source
27
+ response = provider.store(creditcard, options)
28
+ if response.success?
29
+ if get_preference(:intents)
30
+ payment.source.update!(
31
+ cc_type: payment.source.cc_type,
32
+ gateway_customer_profile_id: response.params['id'],
33
+ gateway_payment_profile_id: response.params['sources']['data'].first['id']
34
+ )
35
+ else
36
+ response_cc_type = response.params['sources']['data'].first['brand']
37
+ cc_type = CARD_TYPE_MAPPING.include?(response_cc_type) ? CARD_TYPE_MAPPING[response_cc_type] : payment.source.cc_type
38
+ payment.source.update!({
39
+ cc_type: cc_type, # side-effect of update_source!
40
+ gateway_customer_profile_id: response.params['id'],
41
+ gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
42
+ })
43
+ end
44
+ else
45
+ payment.send(:gateway_error, response.message)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def options_for_purchase_or_auth(money, creditcard, gateway_options)
52
+ money, creditcard, options = super
53
+ options[:execute_threed] = get_preference(:intents)
54
+ return money, creditcard, options
55
+ end
6
56
  end
7
57
  end
@@ -54,6 +54,7 @@ module Spree
54
54
 
55
55
  def create_profile(payment)
56
56
  return unless payment.source.gateway_customer_profile_id.nil?
57
+
57
58
  options = {
58
59
  email: payment.order.email,
59
60
  login: preferred_secret_key,
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module OrderDecorator
3
+ def self.prepended(base)
4
+ return if base.checkout_steps.key?(:payment_confirm)
5
+
6
+ base.insert_checkout_step(
7
+ :payment_confirm,
8
+ before: :complete,
9
+ if: ->(order) { order.intents? }
10
+ )
11
+ end
12
+
13
+ def process_payments!
14
+ # Payments are processed in confirm_payment step where after successful
15
+ # 3D Secure authentication `intent_client_key` is saved for payment.
16
+ # In case authentication is unsuccessful, `intent_client_key` is removed.
17
+ return if intents? && payments.valid.last.intent_client_key.present?
18
+
19
+ super
20
+ end
21
+
22
+ def intents?
23
+ payments.valid.map { |p| p.payment_method&.has_preference?(:intents) && p.payment_method&.get_preference(:intents) }.any?
24
+ end
25
+ end
26
+ end
27
+
28
+ Spree::Order.prepend(Spree::OrderDecorator)
@@ -0,0 +1,34 @@
1
+ module Spree
2
+ module PaymentDecorator
3
+ def handle_response(response, success_state, failure_state)
4
+ self.intent_client_key = response.params['client_secret'] if response.params['client_secret'] && response.success?
5
+ super
6
+ end
7
+
8
+ def verify!(**options)
9
+ process_verification(options)
10
+ end
11
+
12
+ private
13
+
14
+ def process_verification(**options)
15
+ protect_from_connection_error do
16
+ response = payment_method.verify(source, options)
17
+
18
+ record_response(response)
19
+
20
+ if response.success?
21
+ unless response.authorization.nil?
22
+ self.response_code = response.authorization
23
+
24
+ source.update(status: response.params['status'])
25
+ end
26
+ else
27
+ gateway_error(response)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ Spree::Payment.prepend(Spree::PaymentDecorator)
@@ -0,0 +1,34 @@
1
+ <div id='errorBox' class='errorExplanation alert alert-danger' style='display:none'></div>
2
+ <div id='successBox' class='alert alert-success' style='display:none'></div>
3
+ <div id='infoBox' class='alert alert-info'><%= t('spree.please_wait_for_confirmation_popup') %></div>
4
+
5
+ <% if @client_secret.present? && @pk_key.present? %>
6
+ <script type="text/javascript" src="https://js.stripe.com/v3/"></script>
7
+ <script>
8
+ var form = document.getElementById('checkout_form_payment_confirm');
9
+
10
+ function confirmCardPaymentResponseHandler(response) {
11
+ $.post("/api/v2/storefront/intents/handle_response", { response: response, order_token: "<%= @order.token %>" }).done(function (result) {
12
+ form.elements["commit"].disabled = false;
13
+ $('#successBox').html(result.message);
14
+ $('#successBox').show();
15
+ form.submit();
16
+ }).fail(function(result) {
17
+ form.elements["commit"].disabled = false;
18
+ $('#errorBox').html(result.responseJSON.error);
19
+ $('#errorBox').show();
20
+ });
21
+ }
22
+
23
+ var stripeElements = Stripe("<%= @pk_key %>");
24
+ stripeElements.confirmCardPayment("<%= @client_secret %>").then(function(result) {
25
+ $('#infoBox').hide();
26
+ confirmCardPaymentResponseHandler(result);
27
+ });
28
+
29
+ document.addEventListener('DOMContentLoaded', function(){
30
+ form.elements["commit"].value = "continue"
31
+ form.elements["commit"].disabled = true
32
+ });
33
+ </script>
34
+ <% end %>
@@ -0,0 +1,5 @@
1
+ module Spree
2
+ module PermittedAttributes
3
+ @@source_attributes += %i[account_number routing_number account_holder_type account_holder_name status]
4
+ end
5
+ end
@@ -1,7 +1,19 @@
1
1
  ---
2
2
  en:
3
+ activerecord:
4
+ attributes:
5
+ spree/check:
6
+ account_holder_name: Account Holder Name
7
+ account_holder_type: Account Holder Type
8
+ account_number: Account Number
9
+ routing_number: Routing Number
3
10
  spree:
11
+ check: Check
4
12
  payment_has_been_cancelled: The payment has been cancelled.
13
+ please_wait_for_confirmation_popup: Please wait for payment confirmation popup to appear.
14
+ payment_successfully_authorized: The payment was successfully authorized.
15
+ order_state:
16
+ payment_confirm: Verify payment
5
17
  log_entry:
6
18
  braintree:
7
19
  message: Message
@@ -21,3 +33,14 @@ en:
21
33
  cvv_result: CVV Result
22
34
  cvc_check: CVC Check
23
35
  address_zip_check: Address ZIP check
36
+ stripe:
37
+ ach:
38
+ account_holder_name: Account Holder Name
39
+ account_holder_type: Account Holder Type
40
+ routing_number: Routing Number
41
+ account_number: Account Number
42
+ verify_account_number: Verify Account Number
43
+ verify_bank_account: Verify Bank Account
44
+ first_deposit: First Deposit
45
+ second_deposit: Second Deposit
46
+ bank_transfer: bank_transfer
@@ -4,3 +4,15 @@
4
4
  Rails.application.routes.draw do
5
5
  get '/.well-known/apple-developer-merchantid-domain-association' => 'spree/apple_pay_domain_verification#show'
6
6
  end
7
+
8
+ Spree::Core::Engine.add_routes do
9
+ namespace :api, defaults: { format: 'json' } do
10
+ namespace :v2 do
11
+ namespace :storefront do
12
+ namespace :intents do
13
+ post :handle_response
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end