@@ -240,25 +240,31 @@ def subscription_updated(event: stripe.Event) -> None:
240240 )
241241
242242 # Handle trial subscription cancellation
243- # TODO: Check that status wasn't already canceled to avoid duplicate emails.
244- # This will be easier to implement once StripeEventSummary changes land,
245- # which will provide easier querying of prior subscription states.
246- if subscription .get ("status" ) == "canceled" :
247- trial_end = subscription .get ("trial_end" )
248- if trial_end :
249- logger .info (
250- f"Queuing trial cancellation email for subscription { subscription .id } , "
251- f"checkout_intent_id={ checkout_intent_id } "
252- )
253-
254- # Queue the async task to send the email
255- send_trial_cancellation_email_task .delay (
256- checkout_intent_id = checkout_intent .id ,
257- trial_end_timestamp = trial_end ,
258- )
243+ # Check if status changed to canceled to avoid duplicate emails
244+ current_status = subscription .get ("status" )
245+ if current_status == "canceled" :
246+ prior_status = getattr (checkout_intent .previous_summary (event ), 'subscription_status' , None )
247+
248+ # Only send email if status changed from non-canceled to canceled
249+ if prior_status != 'canceled' :
250+ trial_end = subscription .get ("trial_end" )
251+ if trial_end :
252+ logger .info (
253+ f"Subscription { subscription .id } status changed from '{ prior_status } ' to 'canceled'. "
254+ f"Queuing trial cancellation email for checkout_intent_id={ checkout_intent_id } "
255+ )
256+
257+ send_trial_cancellation_email_task .delay (
258+ checkout_intent_id = checkout_intent .id ,
259+ trial_end_timestamp = trial_end ,
260+ )
261+ else :
262+ logger .info (
263+ f"Subscription { subscription .id } canceled but has no trial_end, skipping cancellation email"
264+ )
259265 else :
260266 logger .info (
261- f"Subscription { subscription .id } canceled but has no trial_end , skipping cancellation email"
267+ f"Subscription { subscription .id } already canceled (status unchanged) , skipping cancellation email"
262268 )
263269
264270 @on_stripe_event ("customer.subscription.deleted" )
0 commit comments