diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dd65c63d2d..f58abc8f206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ## Solidus 1.4.0 (master, unreleased) +* Make some 'wallet' behavior configurable + + NOTE: `Order#persist_user_credit_card` has been renamed to + `Order#add_payment_sources_to_wallet`. If you are overriding + `persist_user_credit_card` you need to update your code. + + The following extension points have been added for customizing 'wallet' + behavior. + + * Spree::Config.add_payment_sources_to_wallet_class + * Spree::Config.default_payment_builder_class + + https://github.com/solidusio/solidus/pull/1086 + * Backend: UI, Remove icons from buttons and tabs * Backend: Deprecate args/options that add icons to buttons diff --git a/core/app/models/spree/app_configuration.rb b/core/app/models/spree/app_configuration.rb index 3a962e1ab92..d2d8fbb895e 100644 --- a/core/app/models/spree/app_configuration.rb +++ b/core/app/models/spree/app_configuration.rb @@ -349,6 +349,28 @@ def order_merger_class @order_merger_class ||= Spree::OrderMerger end + # Allows providing your own class for adding default payments to a user's + # order from their "wallet". + # + # @!attribute [rw] default_payment_builder_class + # @return [Class] a class with the same public interfaces as + # Spree::Wallet::DefaultPaymentBuilder. + attr_writer :default_payment_builder_class + def default_payment_builder_class + @default_payment_builder_class ||= Spree::Wallet::DefaultPaymentBuilder + end + + # Allows providing your own class for adding payment sources to a user's + # "wallet" after an order moves to the complete state. + # + # @!attribute [rw] add_payment_sources_to_wallet_class + # @return [Class] a class with the same public interfaces + # as Spree::Wallet::AddPaymentSourcesToWallet. + attr_writer :add_payment_sources_to_wallet_class + def add_payment_sources_to_wallet_class + @add_payment_sources_to_wallet_class ||= Spree::Wallet::AddPaymentSourcesToWallet + end + def static_model_preferences @static_model_preferences ||= Spree::Preferences::StaticModelPreferences.new end diff --git a/core/app/models/spree/order/checkout.rb b/core/app/models/spree/order/checkout.rb index f229382c18e..0737d2652da 100644 --- a/core/app/models/spree/order/checkout.rb +++ b/core/app/models/spree/order/checkout.rb @@ -76,7 +76,7 @@ def self.define_state_machine! transition to: :payment, from: :confirm end - after_transition to: :complete, do: :persist_user_credit_card + after_transition to: :complete, do: :add_payment_sources_to_wallet before_transition to: :payment, do: :set_shipments_cost before_transition to: :payment, do: :create_tax_charge! before_transition to: :payment, do: :assign_default_credit_card @@ -309,22 +309,25 @@ def persist_user_address! end end - def persist_user_credit_card - if !temporary_credit_card && user_id && valid_credit_cards.present? - default_cc = valid_credit_cards.first - # TODO: target for refactoring -- why is order checkout responsible for the user -> credit_card relationship? - default_cc.user_id = user_id - default_cc.default = true - default_cc.save - end + def add_payment_sources_to_wallet + Spree::Config. + add_payment_sources_to_wallet_class.new(self). + add_to_wallet end + alias_method :persist_user_credit_card, :add_payment_sources_to_wallet + deprecate :persist_user_credit_card def assign_default_credit_card - if payments.from_credit_card.count == 0 && user && user.default_credit_card.try(:valid?) - cc = user.default_credit_card - payments.create!(payment_method_id: cc.payment_method_id, source: cc) - # this is one of 2 places still using User#bill_address - self.bill_address ||= user.default_credit_card.address || user.bill_address + builder = Spree::Config.default_payment_builder_class.new(self) + + if payment = builder.build + payments << payment + + if bill_address.nil? + # this is one of 2 places still using User#bill_address + self.bill_address = payment.source.try(:address) || + user.bill_address + end end end diff --git a/core/app/models/spree/wallet/add_payment_sources_to_wallet.rb b/core/app/models/spree/wallet/add_payment_sources_to_wallet.rb new file mode 100644 index 00000000000..6e1d359be1f --- /dev/null +++ b/core/app/models/spree/wallet/add_payment_sources_to_wallet.rb @@ -0,0 +1,29 @@ +# This class is responsible for saving payment sources in the user's "wallet" +# for future use. You can substitute your own class via +# `Spree::Config.add_payment_sources_to_wallet_class`. +class Spree::Wallet::AddPaymentSourcesToWallet + def initialize(order) + @order = order + end + + # This is called after an order transistions to complete and should save the + # order's payment source/s in the user's "wallet" for future use. + # + # @return [undefined] + def add_to_wallet + if !order.temporary_credit_card && + order.user_id && + order.valid_credit_cards.present? + # arbitrarily pick the first one for the default + default_cc = order.valid_credit_cards.first + # TODO: target for refactoring -- why is order checkout responsible for the user -> credit_card relationship? + default_cc.user_id = order.user_id + default_cc.default = true + default_cc.save + end + end + + private + + attr_reader :order +end diff --git a/core/app/models/spree/wallet/default_payment_builder.rb b/core/app/models/spree/wallet/default_payment_builder.rb new file mode 100644 index 00000000000..50ed1d5fa3f --- /dev/null +++ b/core/app/models/spree/wallet/default_payment_builder.rb @@ -0,0 +1,26 @@ +# This class is responsible for building a default payment on an order, using a +# payment source that is already in the user's "wallet". +class Spree::Wallet::DefaultPaymentBuilder + def initialize(order) + @order = order + end + + # Build a payment to be added to an order prior to moving into the "payment" + # state. + # + # @return [Payment] the unsaved payment to be added, or nil if none. + def build + credit_card = order.user.try!(:default_credit_card) + + if credit_card.try!(:valid?) && order.payments.from_credit_card.count == 0 + Spree::Payment.new( + payment_method_id: credit_card.payment_method_id, + source: credit_card, + ) + end + end + + private + + attr_reader :order +end