diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index 543ec7d707..566701ea66 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -174,15 +174,14 @@ module Spree def authorize_stripe_sca_payment return unless @payment.payment_method.instance_of?(Spree::Gateway::StripeSCA) - @payment.authorize!(full_order_path(@payment.order)) + OrderManagement::Order::StripeScaPaymentAuthorize. + new(@order, payment: @payment, off_session: true). + call!(full_order_path(@order)) - unless @payment.pending? || @payment.requires_authorization? - raise Spree::Core::GatewayError, I18n.t('authorization_failure') - end + raise Spree::Core::GatewayError, I18n.t('authorization_failure') if @order.errors.any? return unless @payment.requires_authorization? - PaymentMailer.authorize_payment(@payment).deliver_later raise Spree::Core::GatewayError, I18n.t('action_required') end diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index e492036220..0ad025d9f6 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -88,9 +88,11 @@ class SubscriptionConfirmJob < ActiveJob::Base def authorize_payment!(order) return if order.subscription.payment_method.class != Spree::Gateway::StripeSCA - OrderManagement::Order::StripeScaPaymentAuthorize.new(order). - extend(OrderManagement::Order::SendAuthorizationEmails). - call! + OrderManagement::Order::StripeScaPaymentAuthorize.new( + order, + off_session: true, + notify_hub: true + ).call! end def send_confirmation_email(order) diff --git a/engines/order_management/app/services/order_management/order/send_authorization_emails.rb b/engines/order_management/app/services/order_management/order/send_authorization_emails.rb deleted file mode 100644 index 5b4fc46924..0000000000 --- a/engines/order_management/app/services/order_management/order/send_authorization_emails.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module OrderManagement - module Order - module SendAuthorizationEmails - def call!(redirect_url = full_order_path(@order)) - super(redirect_url) - - return unless @payment.requires_authorization? - - PaymentMailer.authorize_payment(@payment).deliver_now - PaymentMailer.authorization_required(@payment).deliver_now - end - end - end -end diff --git a/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb b/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb index 80f5364490..4a7839f614 100644 --- a/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb +++ b/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb @@ -1,25 +1,48 @@ # frozen_string_literal: true +# Note: "off-session" processing happens when a payment is placed on behalf of a user when +# they are currently offline. This can happen with backoffice orders or subscriptions. +# In that case; if the payment requires authorization in Stripe, we send an email so the user +# can authorize it later (asynchronously). + module OrderManagement module Order class StripeScaPaymentAuthorize include FullUrlHelper - def initialize(order) + def initialize(order, payment: nil, off_session: false, notify_hub: false) @order = order - @payment = OrderPaymentFinder.new(@order).last_pending_payment + @payment = payment || OrderPaymentFinder.new(order).last_pending_payment + @off_session = off_session + @notify_hub = notify_hub end - def call!(redirect_url = full_order_path(@order)) - return unless @payment&.checkout? + def call!(redirect_url = full_order_path(order)) + return unless payment&.checkout? - @payment.authorize!(redirect_url) + payment.authorize!(redirect_url) - unless @payment.pending? || @payment.requires_authorization? - @order.errors.add(:base, I18n.t('authorization_failure')) - end + order.errors.add(:base, I18n.t('authorization_failure')) unless successfully_processed? + send_auth_emails if requires_authorization_emails? - @payment + payment + end + + private + + attr_reader :order, :payment, :off_session, :notify_hub + + def successfully_processed? + payment.pending? || payment.requires_authorization? + end + + def requires_authorization_emails? + payment.requires_authorization? && off_session + end + + def send_auth_emails + PaymentMailer.authorize_payment(payment).deliver_now + PaymentMailer.authorization_required(payment).deliver_now if notify_hub end end end diff --git a/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb b/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb index 00447c6d78..9dd15e8a8b 100644 --- a/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb +++ b/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb @@ -70,15 +70,6 @@ module OrderManagement } end - it "sends an email requesting authorization and an email notifying the shop owner when requested" do - payment_authorize.extend(OrderManagement::Order::SendAuthorizationEmails).call! - - expect(order.errors.size).to eq 0 - expect(PaymentMailer).to have_received(:authorize_payment) - expect(PaymentMailer).to have_received(:authorization_required) - expect(mail_mock).to have_received(:deliver_now).twice - end - it "doesn't send emails by default" do payment_authorize.call! @@ -87,6 +78,39 @@ module OrderManagement expect(PaymentMailer).to_not have_received(:authorization_required) expect(mail_mock).to_not have_received(:deliver_now) end + + context "when the processing is off-session (via backoffice/subscription)" do + let(:payment_authorize) { + OrderManagement::Order::StripeScaPaymentAuthorize.new(order, off_session: true) + } + + it "notifies the customer" do + payment_authorize.call! + + expect(order.errors.size).to eq 0 + expect(PaymentMailer).to have_received(:authorize_payment) + expect(mail_mock).to have_received(:deliver_now).once + end + + context "with an additional notification for the hub manager" do + let(:payment_authorize) { + OrderManagement::Order::StripeScaPaymentAuthorize.new( + order, + off_session: true, + notify_hub: true + ) + } + + it "notifies both the customer and the hub" do + payment_authorize.call! + + expect(order.errors.size).to eq 0 + expect(PaymentMailer).to have_received(:authorize_payment) + expect(PaymentMailer).to have_received(:authorization_required) + expect(mail_mock).to have_received(:deliver_now).twice + end + end + end end end end diff --git a/spec/jobs/subscription_confirm_job_spec.rb b/spec/jobs/subscription_confirm_job_spec.rb index 8681f5fc6d..7fe9cab1af 100644 --- a/spec/jobs/subscription_confirm_job_spec.rb +++ b/spec/jobs/subscription_confirm_job_spec.rb @@ -170,9 +170,8 @@ describe SubscriptionConfirmJob do let(:provider) { double } before do - allow_any_instance_of(Stripe::CreditCardCloner).to receive(:find_or_clone) { - ["cus_123", "pm_1234"] - } + allow_any_instance_of(Stripe::CreditCardCloner). + to receive(:find_or_clone) { ["cus_123", "pm_1234"] } allow(order).to receive(:pending_payments) { [stripe_sca_payment] } allow(stripe_sca_payment_method).to receive(:provider) { provider } allow(stripe_sca_payment_method.provider).to receive(:purchase) { true } @@ -189,6 +188,15 @@ describe SubscriptionConfirmJob do expect(stripe_sca_payment_method.provider).to receive(:capture) job.send(:confirm_order!, order) end + + it "authorizes the payment with Stripe" do + allow(order).to receive_message_chain(:subscription, :payment_method) { stripe_sca_payment_method } + + expect(OrderManagement::Order::StripeScaPaymentAuthorize). + to receive_message_chain(:new, :call!) { true } + + job.send(:confirm_order!, order) + end end end