-
-
Notifications
You must be signed in to change notification settings - Fork 708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reset order state to cart in case the stripe SCA authorization step fails #5824
Merged
luisramos0
merged 21 commits into
openfoodfoundation:master
from
luisramos0:improve_checkout
Aug 1, 2020
Merged
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
5266d95
Move method closer to related/similar methods
luisramos0 1bf946d
Reused code in checkout controller, the reponse for the case when the…
luisramos0 b23b707
Notify bugsnag and execute post checkout actions (reset to cart state…
luisramos0 ec0d06a
Reuse update_failed method as the code needed is exactly the same
luisramos0 d8a96c9
Bring order checkout workflow and some of its specs from spree_core
luisramos0 e99f0dc
Rubocop autocorrect and easy rubocop issues
luisramos0 51de526
Fix specs in checkout_spec
luisramos0 e80337a
Transpec checkout_spec
luisramos0 734fce5
Add code to persist payments after failed payments. The state machine
luisramos0 26eee46
Rename AdvanceOrderService to OrderWorkflow
luisramos0 c3f9905
Move advance_order_state from checkout_controller to OrderWorkflow se…
luisramos0 9cbcf14
Move shipping method id setting code to OrderWorkflow service
luisramos0 ac5882e
Refactor OrderWorkflow
luisramos0 0700559
Move payments persistence code to order workflow service
luisramos0 fe0c04b
Complete renaming of AdvanceOrderService to OrderWorkflow
mkllnk ad00971
Improve readability and add bugsnag error (now in the checkout_failed…
luisramos0 da4abf6
Add a comment to explain the necessity of the first rescue in the upd…
luisramos0 9e9e0d0
Remove rescue_from and just add the rescue to the edit action, the up…
luisramos0 2136eec
Avoid reloading the payment every time, so that in-memory data is not…
luisramos0 e739c51
Add specs to verify that Spree::Core::Gateway exceptions are handled …
luisramos0 0359d10
Improve code comments on dodgy and/but critical checkout process method
luisramos0 File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
class Order < ActiveRecord::Base | ||
module Checkout | ||
def self.included(klass) | ||
klass.class_eval do | ||
class_attribute :next_event_transitions | ||
class_attribute :previous_states | ||
class_attribute :checkout_flow | ||
class_attribute :checkout_steps | ||
class_attribute :removed_transitions | ||
|
||
def self.checkout_flow(&block) | ||
if block_given? | ||
@checkout_flow = block | ||
define_state_machine! | ||
else | ||
@checkout_flow | ||
end | ||
end | ||
|
||
def self.define_state_machine! | ||
self.checkout_steps = {} | ||
self.next_event_transitions = [] | ||
self.previous_states = [:cart] | ||
self.removed_transitions = [] | ||
|
||
# Build the checkout flow using the checkout_flow defined either | ||
# within the Order class, or a decorator for that class. | ||
# | ||
# This method may be called multiple times depending on if the | ||
# checkout_flow is re-defined in a decorator or not. | ||
instance_eval(&checkout_flow) | ||
|
||
klass = self | ||
|
||
# To avoid a ton of warnings when the state machine is re-defined | ||
StateMachine::Machine.ignore_method_conflicts = true | ||
# To avoid multiple occurrences of the same transition being defined | ||
# On first definition, state_machines will not be defined | ||
state_machines.clear if respond_to?(:state_machines) | ||
state_machine :state, initial: :cart do | ||
klass.next_event_transitions.each { |t| transition(t.merge(on: :next)) } | ||
|
||
# Persist the state on the order | ||
after_transition do |order| | ||
order.state = order.state | ||
order.save | ||
end | ||
|
||
event :cancel do | ||
transition to: :canceled, if: :allow_cancel? | ||
end | ||
|
||
event :return do | ||
transition to: :returned, from: :awaiting_return, unless: :awaiting_returns? | ||
end | ||
|
||
event :resume do | ||
transition to: :resumed, from: :canceled, if: :allow_resume? | ||
end | ||
|
||
event :authorize_return do | ||
transition to: :awaiting_return | ||
end | ||
|
||
if states[:payment] | ||
before_transition to: :complete do |order| | ||
order.process_payments! if order.payment_required? | ||
end | ||
end | ||
|
||
before_transition from: :cart, do: :ensure_line_items_present | ||
|
||
before_transition to: :delivery, do: :create_proposed_shipments | ||
before_transition to: :delivery, do: :ensure_available_shipping_rates | ||
|
||
after_transition to: :complete, do: :finalize! | ||
after_transition to: :delivery, do: :create_tax_charge! | ||
after_transition to: :resumed, do: :after_resume | ||
after_transition to: :canceled, do: :after_cancel | ||
end | ||
end | ||
|
||
def self.go_to_state(name, options = {}) | ||
checkout_steps[name] = options | ||
previous_states.each do |state| | ||
add_transition({ from: state, to: name }.merge(options)) | ||
end | ||
if options[:if] | ||
previous_states << name | ||
else | ||
self.previous_states = [name] | ||
end | ||
end | ||
|
||
def self.insert_checkout_step(name, options = {}) | ||
before = options.delete(:before) | ||
after = options.delete(:after) unless before | ||
after = checkout_steps.keys.last unless before || after | ||
|
||
cloned_steps = checkout_steps.clone | ||
cloned_removed_transitions = removed_transitions.clone | ||
checkout_flow do | ||
cloned_steps.each_pair do |key, value| | ||
go_to_state(name, options) if key == before | ||
go_to_state(key, value) | ||
go_to_state(name, options) if key == after | ||
end | ||
cloned_removed_transitions.each do |transition| | ||
remove_transition(transition) | ||
end | ||
end | ||
end | ||
|
||
def self.remove_checkout_step(name) | ||
cloned_steps = checkout_steps.clone | ||
cloned_removed_transitions = removed_transitions.clone | ||
checkout_flow do | ||
cloned_steps.each_pair do |key, value| | ||
go_to_state(key, value) unless key == name | ||
end | ||
cloned_removed_transitions.each do |transition| | ||
remove_transition(transition) | ||
end | ||
end | ||
end | ||
|
||
def self.remove_transition(options = {}) | ||
removed_transitions << options | ||
next_event_transitions.delete(find_transition(options)) | ||
end | ||
|
||
def self.find_transition(options = {}) | ||
return nil if options.nil? || !options.include?(:from) || !options.include?(:to) | ||
|
||
next_event_transitions.detect do |transition| | ||
transition[options[:from].to_sym] == options[:to].to_sym | ||
end | ||
end | ||
|
||
def self.next_event_transitions | ||
@next_event_transitions ||= [] | ||
end | ||
|
||
def self.checkout_steps | ||
@checkout_steps ||= {} | ||
end | ||
|
||
def self.add_transition(options) | ||
next_event_transitions << { options.delete(:from) => options.delete(:to) }. | ||
merge(options) | ||
end | ||
|
||
def checkout_steps | ||
steps = self.class.checkout_steps. | ||
each_with_object([]) { |(step, options), checkout_steps| | ||
next if options.include?(:if) && !options[:if].call(self) | ||
|
||
checkout_steps << step | ||
}.map(&:to_s) | ||
# Ensure there is always a complete step | ||
steps << "complete" unless steps.include?("complete") | ||
steps | ||
end | ||
|
||
def checkout_step?(step) | ||
step.present? ? checkout_steps.include?(step) : false | ||
end | ||
|
||
def checkout_step_index(step) | ||
checkout_steps.index(step) | ||
end | ||
|
||
def self.removed_transitions | ||
@removed_transitions ||= [] | ||
end | ||
|
||
def can_go_to_state?(state) | ||
return false unless self.state.present? && | ||
checkout_step?(state) && | ||
checkout_step?(self.state) | ||
|
||
checkout_step_index(state) > checkout_step_index(self.state) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the key difference here is the call to checkout_failed in update_failed that executes PostCheckoutActions that will reset the order state to cart 👍