spree_gateway 3.7.4 → 3.9.3

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 +36 -37
  3. data/Appraisals +3 -8
  4. data/app/models/spree/check.rb +41 -0
  5. data/app/models/spree/credit_card_decorator.rb +10 -0
  6. data/app/models/spree/gateway/stripe_ach_gateway.rb +60 -0
  7. data/app/models/spree/gateway/stripe_elements_gateway.rb +50 -0
  8. data/app/models/spree/gateway/stripe_gateway.rb +1 -0
  9. data/app/models/spree/order_decorator.rb +28 -0
  10. data/app/models/spree/payment_decorator.rb +36 -0
  11. data/app/views/spree/checkout/_payment_confirm.html.erb +39 -0
  12. data/config/initializers/spree_permitted_attributes.rb +5 -0
  13. data/config/locales/en.yml +23 -0
  14. data/config/routes.rb +12 -0
  15. data/db/migrate/20200317135551_add_spree_check_payment_source.rb +22 -0
  16. data/db/migrate/20200422114908_add_intent_key_to_payment.rb +5 -0
  17. data/gemfiles/{spree_3_5.gemfile → spree_4_1.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/engine.rb +8 -0
  22. data/lib/spree_gateway/version.rb +1 -1
  23. data/lib/views/backend/spree/admin/payments/source_forms/_stripe_ach.html.erb +86 -0
  24. data/lib/views/backend/spree/admin/payments/source_forms/_stripe_apple_pay.html.erb +0 -0
  25. data/lib/views/backend/spree/admin/payments/source_views/_stripe_ach.html.erb +21 -0
  26. data/lib/views/backend/spree/admin/payments/source_views/_stripe_apple_pay.html.erb +1 -0
  27. data/lib/views/frontend/spree/checkout/payment/_stripe_ach.html.erb +81 -0
  28. data/lib/views/frontend/spree/checkout/payment/_stripe_ach_verify.html.erb +16 -0
  29. data/lib/views/frontend/spree/checkout/payment/_stripe_apple_pay.html.erb +10 -1
  30. data/lib/views/frontend/spree/checkout/payment/_stripe_elements.html.erb +2 -1
  31. data/spec/factories/check_factory.rb +10 -0
  32. data/spec/features/admin/stripe_elements_payment_spec.rb +32 -32
  33. data/spec/features/stripe_checkout_spec.rb +30 -19
  34. data/spec/features/stripe_elements_3ds_checkout_spec.rb +224 -0
  35. data/spec/models/gateway/stripe_ach_gateway_spec.rb +186 -0
  36. data/spec/models/gateway/stripe_gateway_spec.rb +1 -0
  37. data/spec/spec_helper.rb +7 -65
  38. data/spec/support/order_walktrough.rb +73 -0
  39. data/spec/support/within_stripe_3ds_popup.rb +10 -0
  40. data/spree_gateway.gemspec +10 -22
  41. metadata +44 -282
  42. data/gemfiles/spree_4_0.gemfile +0 -8
  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: 82e672befb08ae277659eec1f7686002af0b6f66b1f8ecb467649ab29667aee2
4
+ data.tar.gz: 47d5a54c20d7987569af842d50f471e6f3eea69647f6807865191c55e97f61cc
5
5
  SHA512:
6
- metadata.gz: 0ce78bd6a8b62af2de0ac1e73abb6844ea16385d3ff121c033070b6c965f1e87e52d7a3eced0d35ced01d127eef3057dd8132f561555ac89780363b179e2e40f
7
- data.tar.gz: 9a0ac382a6aa612965f0ad22950ec96a907c069c71a06e8f319987582ad64691cb718bbda349ddd7b7d6accb9bc23f548e43908fb157b692c13395d468c8f96c
6
+ metadata.gz: 5efb8b8ce887c92a222bf0b4c4b53b0ea16ee45f6efff3ad2908acdc5e3383139b778362be57ed858d15a0497df4d6aa37fa2c8f56ddee81f334945bcb67fe9f
7
+ data.tar.gz: fb61b3afbfc998e819bd11cf5d1f53c782e0e486c5b0fea6c43710fba74664398384badff68298a593b715185da3eaa68727e18b3d575dc5f14511d232d7f437
@@ -1,51 +1,50 @@
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.7
19
+ - 3.0
9
20
 
10
21
  env:
11
- - DB=postgres
12
22
  - DB=mysql
23
+ - DB=postgres
13
24
 
14
25
  gemfile:
15
- - gemfiles/spree_3_5.gemfile
16
26
  - gemfiles/spree_3_7.gemfile
17
- - gemfiles/spree_4_0.gemfile
27
+ - gemfiles/spree_4_1.gemfile
18
28
  - gemfiles/spree_master.gemfile
19
29
 
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
30
+ jobs:
31
+ allow_failures:
32
+ - gemfile: gemfiles/spree_master.gemfile
33
+ exclude:
34
+ - rvm: 3.0
35
+ gemfile: gemfiles/spree_4_1.gemfile
36
+ - rvm: 3.0
37
+ gemfile: gemfiles/spree_3_7.gemfile
43
38
 
44
39
  before_install:
45
40
  - 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
41
+
42
+ before_script:
43
+ - CHROME_MAIN_VERSION=`google-chrome-stable --version | sed -E 's/(^Google Chrome |\.[0-9]+ )//g'`
44
+ - CHROMEDRIVER_VERSION=`curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_MAIN_VERSION"`
45
+ - curl "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip" -O
46
+ - unzip chromedriver_linux64.zip -d ~/bin
47
+
48
+ script:
49
+ - bundle exec rake test_app
50
+ - bundle exec rake spec
data/Appraisals CHANGED
@@ -1,16 +1,11 @@
1
- appraise 'spree-3-5' do
2
- gem 'spree', '~> 3.5.0'
3
- gem 'rails-controller-testing'
4
- end
5
-
6
1
  appraise 'spree-3-7' do
7
2
  gem 'spree', '~> 3.7.0'
8
- gem 'sass-rails'
9
3
  gem 'rails-controller-testing'
4
+ gem 'sass-rails'
10
5
  end
11
6
 
12
- appraise 'spree-4-0' do
13
- gem 'spree', '~> 4.0.0.rc2'
7
+ appraise 'spree-4-1' do
8
+ gem 'spree', '~> 4.1.0'
14
9
  gem 'rails-controller-testing'
15
10
  end
16
11
 
@@ -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,10 @@
1
+ module Spree
2
+ module CreditCardDecorator
3
+ def set_last_digits
4
+ self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
5
+ end
6
+
7
+ end
8
+ end
9
+
10
+ ::Spree::CreditCard.prepend(::Spree::CreditCardDecorator)
@@ -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,36 @@
1
+ module Spree
2
+ module PaymentDecorator
3
+ def handle_response(response, success_state, failure_state)
4
+ if response.success? && response.respond_to?(:params)
5
+ self.intent_client_key = response.params['client_secret'] if response.params['client_secret']
6
+ end
7
+ super
8
+ end
9
+
10
+ def verify!(**options)
11
+ process_verification(options)
12
+ end
13
+
14
+ private
15
+
16
+ def process_verification(**options)
17
+ protect_from_connection_error do
18
+ response = payment_method.verify(source, options)
19
+
20
+ record_response(response)
21
+
22
+ if response.success?
23
+ unless response.authorization.nil?
24
+ self.response_code = response.authorization
25
+
26
+ source.update(status: response.params['status'])
27
+ end
28
+ else
29
+ gateway_error(response)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Spree::Payment.prepend(Spree::PaymentDecorator)
@@ -0,0 +1,39 @@
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
+ // conditional needs for spree 3.7
13
+ if(form.elements["commit"]) {
14
+ form.elements["commit"].disabled = false;
15
+ }
16
+ $('#successBox').html(result.message);
17
+ $('#successBox').show();
18
+ form.submit();
19
+ }).fail(function(result) {
20
+ if(form.elements["commit"]) {
21
+ form.elements["commit"].disabled = false;
22
+ }
23
+ $('#errorBox').html(result.responseJSON.error);
24
+ $('#errorBox').show();
25
+ });
26
+ }
27
+
28
+ var stripeElements = Stripe("<%= @pk_key %>");
29
+ stripeElements.confirmCardPayment("<%= @client_secret %>").then(function(result) {
30
+ $('#infoBox').hide();
31
+ confirmCardPaymentResponseHandler(result);
32
+ });
33
+
34
+ document.addEventListener('DOMContentLoaded', function(){
35
+ form.elements["commit"].value = "continue"
36
+ form.elements["commit"].disabled = true
37
+ });
38
+ </script>
39
+ <% 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