solidus_stripe 3.1.0 → 4.2.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +5 -0
  4. data/CHANGELOG.md +58 -2
  5. data/Gemfile +7 -0
  6. data/LICENSE +2 -2
  7. data/README.md +26 -8
  8. data/Rakefile +1 -1
  9. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-cart-page-checkout.js +42 -9
  10. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +3 -2
  11. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +56 -20
  12. data/app/controllers/solidus_stripe/intents_controller.rb +23 -18
  13. data/app/decorators/models/spree/refund_decorator.rb +9 -0
  14. data/app/models/solidus_stripe/address_from_params_service.rb +5 -2
  15. data/app/models/solidus_stripe/create_intents_payment_service.rb +113 -0
  16. data/app/models/spree/payment_method/stripe_credit_card.rb +16 -1
  17. data/bin/r +13 -0
  18. data/bin/rake +7 -0
  19. data/bin/sandbox +84 -0
  20. data/bin/sandbox_rails +18 -0
  21. data/bin/setup +1 -1
  22. data/config/routes.rb +4 -1
  23. data/lib/generators/solidus_stripe/install/install_generator.rb +7 -3
  24. data/lib/solidus_stripe/engine.rb +2 -2
  25. data/lib/solidus_stripe/factories.rb +4 -0
  26. data/lib/solidus_stripe/version.rb +1 -1
  27. data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +0 -1
  28. data/solidus_stripe.gemspec +34 -37
  29. data/spec/features/stripe_checkout_spec.rb +101 -48
  30. data/spec/models/solidus_stripe/address_from_params_service_spec.rb +19 -5
  31. data/spec/models/solidus_stripe/create_intents_payment_service_spec.rb +127 -0
  32. data/spec/models/spree/payment_method/stripe_credit_card_spec.rb +44 -1
  33. data/spec/spec_helper.rb +4 -1
  34. metadata +23 -16
  35. data/LICENSE.md +0 -26
  36. data/app/models/solidus_stripe/create_intents_order_service.rb +0 -70
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module RefundDecorator
5
+ attr_reader :response
6
+
7
+ Spree::Refund.prepend(self)
8
+ end
9
+ end
@@ -4,7 +4,7 @@ module SolidusStripe
4
4
  class AddressFromParamsService
5
5
  attr_reader :address_params, :user
6
6
 
7
- def initialize(address_params, user)
7
+ def initialize(address_params, user = nil)
8
8
  @address_params, @user = address_params, user
9
9
  end
10
10
 
@@ -43,7 +43,10 @@ module SolidusStripe
43
43
  end
44
44
 
45
45
  def state
46
- @state ||= country.states.find_by_abbr(address_params[:region])
46
+ @state ||= begin
47
+ region = address_params[:region]
48
+ country.states.find_by(abbr: region) || country.states.find_by(name: region)
49
+ end
47
50
  end
48
51
 
49
52
  def default_attributes
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusStripe
4
+ class CreateIntentsPaymentService
5
+ attr_reader :intent_id, :stripe, :controller
6
+
7
+ delegate :request, :current_order, :params, to: :controller
8
+
9
+ def initialize(intent_id, stripe, controller)
10
+ @intent_id, @stripe, @controller = intent_id, stripe, controller
11
+ end
12
+
13
+ def call
14
+ invalidate_previous_payment_intents_payments
15
+ if (payment = create_payment)
16
+ description = "Solidus Order ID: #{payment.gateway_order_identifier}"
17
+ stripe.update_intent(nil, intent_id, nil, description: description)
18
+ true
19
+ else
20
+ invalidate_current_payment_intent
21
+ false
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def intent
28
+ @intent ||= stripe.show_intent(intent_id, {})
29
+ end
30
+
31
+ def invalidate_current_payment_intent
32
+ stripe.cancel(intent_id)
33
+ end
34
+
35
+ def invalidate_previous_payment_intents_payments
36
+ if stripe.v3_intents?
37
+ current_order.payments.pending.where(payment_method: stripe).each(&:void_transaction!)
38
+ end
39
+ end
40
+
41
+ def create_payment
42
+ Spree::OrderUpdateAttributes.new(
43
+ current_order,
44
+ payment_params,
45
+ request_env: request.headers.env
46
+ ).apply
47
+
48
+ created_payment = Spree::Payment.find_by(response_code: intent_id)
49
+ created_payment&.tap { |payment| payment.update!(state: :pending) }
50
+ end
51
+
52
+ def payment_params
53
+ {
54
+ payments_attributes: [{
55
+ payment_method_id: stripe.id,
56
+ amount: current_order.total,
57
+ response_code: intent_id,
58
+ source_attributes: {
59
+ month: intent_card['exp_month'],
60
+ year: intent_card['exp_year'],
61
+ cc_type: intent_card['brand'],
62
+ last_digits: intent_card['last4'],
63
+ gateway_payment_profile_id: intent_customer_profile,
64
+ name: card_holder_name || address_full_name,
65
+ address_attributes: address_attributes
66
+ }
67
+ }]
68
+ }
69
+ end
70
+
71
+ def intent_card
72
+ intent_data['payment_method_details']['card']
73
+ end
74
+
75
+ def intent_customer_profile
76
+ intent.params['payment_method']
77
+ end
78
+
79
+ def card_holder_name
80
+ (html_payment_source_data['name'] || intent_data['billing_details']['name']).presence
81
+ end
82
+
83
+ def intent_data
84
+ intent.params['charges']['data'][0]
85
+ end
86
+
87
+ def form_data
88
+ params[:form_data]
89
+ end
90
+
91
+ def html_payment_source_data
92
+ if form_data.is_a?(String)
93
+ data = Rack::Utils.parse_nested_query(form_data)
94
+ data['payment_source'][stripe.id.to_s]
95
+ else
96
+ {}
97
+ end
98
+ end
99
+
100
+ def address_attributes
101
+ html_payment_source_data['address_attributes'] || SolidusStripe::AddressFromParamsService.new(form_data).call.attributes
102
+ end
103
+
104
+ def address_full_name
105
+ current_order.bill_address&.full_name || form_data[:recipient]
106
+ end
107
+
108
+ def update_stripe_payment_description
109
+ description = "Solidus Order ID: #{payment.gateway_order_identifier}"
110
+ stripe.update_intent(nil, intent_id, nil, description: description)
111
+ end
112
+ end
113
+ end
@@ -15,7 +15,7 @@ module Spree
15
15
  'Visa' => 'visa'
16
16
  }
17
17
 
18
- delegate :create_intent, :update_intent, :confirm_intent, to: :gateway
18
+ delegate :create_intent, :update_intent, :confirm_intent, :show_intent, to: :gateway
19
19
 
20
20
  def stripe_config(order)
21
21
  {
@@ -81,6 +81,21 @@ module Spree
81
81
  gateway.void(response_code, {})
82
82
  end
83
83
 
84
+ def payment_intents_refund_reason
85
+ Spree::RefundReason.where(name: Spree::Payment::Cancellation::DEFAULT_REASON).first_or_create
86
+ end
87
+
88
+ def try_void(payment)
89
+ if v3_intents? && payment.completed?
90
+ payment.refunds.create!(
91
+ amount: payment.credit_allowed,
92
+ reason: payment_intents_refund_reason
93
+ ).response
94
+ else
95
+ void(payment.response_code, nil, nil)
96
+ end
97
+ end
98
+
84
99
  def cancel(response_code)
85
100
  gateway.void(response_code, {})
86
101
  end
data/bin/r ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/solidus_stripe/engine', __dir__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'rails/all'
13
+ require 'rails/engine/commands'
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ case "$DB" in
6
+ postgres|postgresql)
7
+ RAILSDB="postgresql"
8
+ ;;
9
+ mysql)
10
+ RAILSDB="mysql"
11
+ ;;
12
+ sqlite|'')
13
+ RAILSDB="sqlite3"
14
+ ;;
15
+ *)
16
+ echo "Invalid DB specified: $DB"
17
+ exit 1
18
+ ;;
19
+ esac
20
+
21
+ if [ ! -z $SOLIDUS_BRANCH ]
22
+ then
23
+ BRANCH=$SOLIDUS_BRANCH
24
+ else
25
+ BRANCH="master"
26
+ fi
27
+
28
+ extension_name="solidus_stripe"
29
+
30
+ # Stay away from the bundler env of the containing extension.
31
+ function unbundled {
32
+ ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@
33
+ }
34
+
35
+ rm -rf ./sandbox
36
+ unbundled bundle exec rails new sandbox --database="$RAILSDB" \
37
+ --skip-bundle \
38
+ --skip-git \
39
+ --skip-keeps \
40
+ --skip-rc \
41
+ --skip-spring \
42
+ --skip-test \
43
+ --skip-javascript
44
+
45
+ if [ ! -d "sandbox" ]; then
46
+ echo 'sandbox rails application failed'
47
+ exit 1
48
+ fi
49
+
50
+ cd ./sandbox
51
+ cat <<RUBY >> Gemfile
52
+ gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH'
53
+ gem 'solidus_auth_devise', '>= 2.1.0'
54
+ gem 'rails-i18n'
55
+ gem 'solidus_i18n'
56
+
57
+ gem '$extension_name', path: '..'
58
+
59
+ group :test, :development do
60
+ platforms :mri do
61
+ gem 'pry-byebug'
62
+ end
63
+ end
64
+ RUBY
65
+
66
+ unbundled bundle install --gemfile Gemfile
67
+
68
+ unbundled bundle exec rake db:drop db:create
69
+
70
+ unbundled bundle exec rails generate spree:install \
71
+ --auto-accept \
72
+ --user_class=Spree::User \
73
+ --enforce_available_locales=true \
74
+ --with-authentication=false \
75
+ $@
76
+
77
+ unbundled bundle exec rails generate solidus:auth:install
78
+
79
+ echo
80
+ echo "🚀 Sandbox app successfully created for $extension_name!"
81
+ echo "🚀 Using $RAILSDB and Solidus $BRANCH"
82
+ echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
83
+ echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
84
+ echo "🚀 This app is intended for test purposes."
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ app_root = 'sandbox'
6
+
7
+ unless File.exist? "#{app_root}/bin/rails"
8
+ warn 'Creating the sandbox app...'
9
+ Dir.chdir "#{__dir__}/.." do
10
+ system "#{__dir__}/sandbox" or begin # rubocop:disable Style/AndOr
11
+ warn 'Automatic creation of the sandbox app failed'
12
+ exit 1
13
+ end
14
+ end
15
+ end
16
+
17
+ Dir.chdir app_root
18
+ exec 'bin/rails', *ARGV
data/bin/setup CHANGED
@@ -5,4 +5,4 @@ set -vx
5
5
 
6
6
  gem install bundler --conservative
7
7
  bundle update
8
- bundle exec rake extension:test_app
8
+ bin/rake clobber
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Spree::Core::Engine.routes.draw do
2
4
  # route to a deprecated controller, will be removed in the future:
3
5
  post '/stripe/confirm_payment', to: 'stripe#confirm_payment'
4
6
 
5
7
  # payment intents routes:
6
- post '/stripe/confirm_intents', to: '/solidus_stripe/intents#confirm'
8
+ post '/stripe/create_intent', to: '/solidus_stripe/intents#create_intent'
9
+ post '/stripe/create_payment', to: '/solidus_stripe/intents#create_payment'
7
10
 
8
11
  # payment request routes:
9
12
  post '/stripe/shipping_rates', to: '/solidus_stripe/payment_request#shipping_rates'
@@ -13,12 +13,16 @@ module SolidusStripe
13
13
  run 'bundle exec rake railties:install:migrations FROM=solidus_stripe'
14
14
  end
15
15
 
16
+ def add_migrations
17
+ run 'bin/rails railties:install:migrations FROM=solidus_stripe'
18
+ end
19
+
16
20
  def run_migrations
17
- run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
21
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Metrics/LineLength
18
22
  if run_migrations
19
- run 'bundle exec rake db:migrate'
23
+ run 'bin/rails db:migrate'
20
24
  else
21
- puts 'Skipping rake db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
25
+ puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
22
26
  end
23
27
  end
24
28
 
@@ -4,9 +4,9 @@ require 'spree/core'
4
4
 
5
5
  module SolidusStripe
6
6
  class Engine < Rails::Engine
7
- include SolidusSupport::EngineExtensions::Decorators
7
+ include SolidusSupport::EngineExtensions
8
8
 
9
- isolate_namespace Spree
9
+ isolate_namespace ::Spree
10
10
 
11
11
  engine_name 'solidus_stripe'
12
12
 
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusStripe
4
- VERSION = "3.1.0"
4
+ VERSION = "4.2.0"
5
5
  end
@@ -1,6 +1,5 @@
1
1
  <div id="payment-request-button" data-stripe-config="<%= payment_method.stripe_config(current_order).to_json %>" data-v3-api="<%= stripe_v3_api %>"></div>
2
2
 
3
- <%= image_tag 'credit_cards/credit_card.gif', id: 'credit-card-image' %>
4
3
  <% param_prefix = "payment_source[#{payment_method.id}]" %>
5
4
 
6
5
  <div class="field field-required">
@@ -1,40 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $:.push File.expand_path('lib', __dir__)
4
- require 'solidus_stripe/version'
5
-
6
- Gem::Specification.new do |s|
7
- s.name = 'solidus_stripe'
8
- s.version = SolidusStripe::VERSION
9
- s.summary = "Stripe Payment Method for Solidus"
10
- s.description = s.summary
11
- s.required_ruby_version = ">= 2.2"
12
-
13
- s.author = "Solidus Team"
14
- s.email = "contact@solidus.io"
15
- s.homepage = "https://solidus.io"
16
- s.license = 'BSD-3'
17
-
18
- if s.respond_to?(:metadata)
19
- s.metadata["homepage_uri"] = s.homepage if s.homepage
20
- s.metadata["source_code_uri"] = s.homepage if s.homepage
21
- end
22
-
23
- s.files = `git ls-files`.split("\n")
24
- s.test_files = `git ls-files -- spec/*`.split("\n")
25
- s.require_path = "lib"
26
- s.requirements << "none"
27
-
28
- s.bindir = "exe"
29
- s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
-
31
- s.add_dependency 'solidus_core', ['>= 2.3', '< 3']
32
- s.add_dependency 'solidus_support', '~> 0.4.0'
33
- # ActiveMerchant v1.58 through v1.59 introduced a breaking change
34
- # to the stripe gateway.
35
- #
36
- # This was resolved in v1.60, but we still need to skip 1.58 & 1.59.
37
- s.add_dependency "activemerchant", ">= 1.100" # includes "Stripe Payment Intents: Fix fallback for Store"
38
-
39
- s.add_development_dependency 'solidus_dev_support'
3
+ require_relative 'lib/solidus_stripe/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'solidus_stripe'
7
+ spec.version = SolidusStripe::VERSION
8
+ spec.authors = ['Solidus Team']
9
+ spec.email = 'contact@solidus.io'
10
+
11
+ spec.summary = 'Stripe Payment Method for Solidus'
12
+ spec.description = 'Stripe Payment Method for Solidus'
13
+ spec.homepage = 'https://github.com/solidusio/solidus_stripe#readme'
14
+ spec.license = 'BSD-3'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/solidusio/solidus_stripe'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/solidusio/solidus_stripe/blob/master/CHANGELOG.md'
19
+
20
+ spec.required_ruby_version = Gem::Requirement.new('~> 2.4')
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
25
+
26
+ spec.files = files.grep_v(%r{^(test|spec|features)/})
27
+ spec.test_files = files.grep(%r{^(test|spec|features)/})
28
+ spec.bindir = "exe"
29
+ spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency 'solidus_core', ['>= 2.3', '< 3']
33
+ spec.add_dependency 'solidus_support', '~> 0.5'
34
+ spec.add_dependency 'activemerchant', '>= 1.100'
35
+
36
+ spec.add_development_dependency 'solidus_dev_support'
40
37
  end