From c06a4e7cc77ac22f876e560f4174281ad1bb5fee Mon Sep 17 00:00:00 2001 From: Chris Oliver Date: Fri, 20 Dec 2024 14:40:25 -0600 Subject: [PATCH] Ignore Stripe failed payment webhooks for incomplete subscriptions (#1121) Since incomplete subscriptions are just pending and are currently being created by a customer in the browser, the JavaScript will handle these events. We can safely ignore them server-side and prevent sending duplicates. Stripe Checkout's payment intents are also only usable inside Checkout, so if they were to be used to complete the charge, they may raise an error. --- .../stripe/webhooks/payment_action_required.rb | 3 ++- lib/pay/stripe/webhooks/payment_failed.rb | 3 ++- .../webhooks/payment_action_required_test.rb | 16 ++++++---------- test/pay/stripe/webhooks/payment_failed_test.rb | 7 +++++++ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/pay/stripe/webhooks/payment_action_required.rb b/lib/pay/stripe/webhooks/payment_action_required.rb index d94ce09d9..66c623639 100644 --- a/lib/pay/stripe/webhooks/payment_action_required.rb +++ b/lib/pay/stripe/webhooks/payment_action_required.rb @@ -8,8 +8,9 @@ def call(event) object = event.data.object + # Don't send email on incomplete Stripe subscriptions since they're just getting created and the JavaScript will handle SCA pay_subscription = Pay::Subscription.find_by_processor_and_id(:stripe, object.subscription) - return if pay_subscription.nil? + return if pay_subscription.nil? || pay_subscription.status == "incomplete" if Pay.send_email?(:payment_action_required, pay_subscription) Pay.mailer.with( diff --git a/lib/pay/stripe/webhooks/payment_failed.rb b/lib/pay/stripe/webhooks/payment_failed.rb index 6d915ecc8..d8138b4cf 100644 --- a/lib/pay/stripe/webhooks/payment_failed.rb +++ b/lib/pay/stripe/webhooks/payment_failed.rb @@ -8,8 +8,9 @@ def call(event) object = event.data.object + # Don't send email on incomplete Stripe subscriptions since they're just getting created and the JavaScript will handle SCA pay_subscription = Pay::Subscription.find_by_processor_and_id(:stripe, object.subscription) - return if pay_subscription.nil? + return if pay_subscription.nil? || pay_subscription.status == "incomplete" if Pay.send_email?(:payment_failed, pay_subscription) Pay.mailer.with( diff --git a/test/pay/stripe/webhooks/payment_action_required_test.rb b/test/pay/stripe/webhooks/payment_action_required_test.rb index 12498d23d..a4b18f3f6 100644 --- a/test/pay/stripe/webhooks/payment_action_required_test.rb +++ b/test/pay/stripe/webhooks/payment_action_required_test.rb @@ -5,24 +5,20 @@ class Pay::Stripe::Webhooks::PaymentActionRequiredTest < ActiveSupport::TestCase @event = stripe_event("invoice.payment_action_required") # Create user and subscription - @pay_customer = pay_customers(:stripe) - @pay_customer.update(processor_id: @event.data.object.customer) - @subscription = @pay_customer.subscriptions.create!( - processor_id: @event.data.object.subscription, - name: "default", - processor_plan: "some-plan", - status: "requires_action" - ) + pay_customers(:stripe).update!(processor_id: @event.data.object.customer) end test "it sends an email" do + Pay::Stripe::Subscription.sync @event.data.object.subscription, object: fake_stripe_subscription(id: @event.data.object.subscription, customer: @event.data.object.customer, status: :past_due) + assert_enqueued_jobs 1 do Pay::Stripe::Webhooks::PaymentActionRequired.new.call(@event) end end - test "ignores if subscription doesn't exist" do - @subscription.destroy! + test "skips email if subscription is incomplete" do + Pay::Stripe::Subscription.sync @event.data.object.subscription, object: fake_stripe_subscription(id: @event.data.object.subscription, customer: @event.data.object.customer, status: :incomplete) + assert_no_enqueued_jobs do Pay::Stripe::Webhooks::PaymentActionRequired.new.call(@event) end diff --git a/test/pay/stripe/webhooks/payment_failed_test.rb b/test/pay/stripe/webhooks/payment_failed_test.rb index 2d96b1fd2..42f286240 100644 --- a/test/pay/stripe/webhooks/payment_failed_test.rb +++ b/test/pay/stripe/webhooks/payment_failed_test.rb @@ -16,6 +16,13 @@ class Pay::Stripe::Webhooks::PaymentFailedTest < ActiveSupport::TestCase end end + test "skips email if subscription is incomplete" do + create_subscription(processor_id: @payment_failed_event.data.object.subscription) + assert_no_enqueued_jobs do + Pay::Stripe::Webhooks::PaymentFailed.new.call(@payment_failed_event) + end + end + private def create_subscription(processor_id:)