Skip to content

Commit

Permalink
Ignore Stripe failed payment webhooks for incomplete subscriptions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
excid3 committed Dec 20, 2024
1 parent cfec031 commit 1ca2797
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 12 deletions.
3 changes: 2 additions & 1 deletion lib/pay/stripe/webhooks/payment_action_required.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 2 additions & 1 deletion lib/pay/stripe/webhooks/payment_failed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
16 changes: 6 additions & 10 deletions test/pay/stripe/webhooks/payment_action_required_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions test/pay/stripe/webhooks/payment_failed_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:)
Expand Down

0 comments on commit 1ca2797

Please sign in to comment.