diff --git a/backend/apps/account_payment/webhooks.py b/backend/apps/account_payment/webhooks.py index 8f2b611b..51bb8d4b 100644 --- a/backend/apps/account_payment/webhooks.py +++ b/backend/apps/account_payment/webhooks.py @@ -513,6 +513,38 @@ def remove_user(email: str, group_key: str = None, event_context: str = None) -> raise +def chatbot_trial_just_ended(event: Event) -> bool: + """True if the subscription ended while it was still in trial.""" + data = event.data or {} + previous_status = (data.get("previous_attributes") or {}).get("status") + if previous_status == "trialing": + return True + + obj = data.get("object") or {} + trial_end = obj.get("trial_end") + ended_at = obj.get("canceled_at") or obj.get("ended_at") + if not trial_end or not ended_at: + return False + return int(ended_at) <= int(trial_end) + 3600 + + +def maybe_send_chatbot_trial_ended_email( + wc: WebhookContext, + subscription: Subscription | None, + account: Account | None, + *, + had_chatbot_access: bool, +) -> None: + """Send the trial-ended email if chatbot access was just revoked for a trial.""" + if not account or not chatbot_trial_just_ended(wc.event): + return + if not had_chatbot_access or account.has_chatbot_access: + return + if not subscription_product_is_chatbot(subscription, wc.event, wc.event_context): + return + send_chatbot_trial_ended_email(account, event_context=wc.event_context) + + def send_chatbot_trial_ended_email(account: Account, event_context: str = None) -> None: """Send the "your chatbot trial ended" email. @@ -763,8 +795,7 @@ def handle_subscription(event: Event): subscription = get_subscription(event, event_context=wc.event_context) account = get_account_for_stripe_customer(event) - status = event.data.get("object", {}).get("status") - previous_status = event.data.get("previous_attributes", {}).get("status") + status = (event.data.get("object") or {}).get("status") if status in ["trialing", "active"]: if subscription: @@ -791,18 +822,11 @@ def handle_subscription(event: Event): subscription.is_active = False subscription.save() - trial_just_ended = previous_status == "trialing" and account had_chatbot_access = bool(account and account.has_chatbot_access) - apply_inactive_subscription_entitlements(wc, subscription, account) - - if ( - trial_just_ended - and had_chatbot_access - and not account.has_chatbot_access - and subscription_product_is_chatbot(subscription, wc.event, wc.event_context) - ): - send_chatbot_trial_ended_email(account, event_context=wc.event_context) + maybe_send_chatbot_trial_ended_email( + wc, subscription, account, had_chatbot_access=had_chatbot_access + ) @webhooks.handler("customer.subscription.updated") @@ -833,6 +857,8 @@ def subscribe(event: Event, **kwargs): def unsubscribe(event: Event, **kwargs): """Revoke entitlements for a deleted Stripe subscription. + Also sends the chatbot trial-ended email if the subscription was still in trial. + Args: event: The `customer.subscription.deleted` Stripe webhook event. **kwargs: Unused; accepted for compatibility with the djstripe @@ -849,7 +875,11 @@ def unsubscribe(event: Event, **kwargs): subscription.save() account = get_account_for_stripe_customer(event) + had_chatbot_access = bool(account and account.has_chatbot_access) apply_inactive_subscription_entitlements(wc, subscription, account) + maybe_send_chatbot_trial_ended_email( + wc, subscription, account, had_chatbot_access=had_chatbot_access + ) @webhooks.handler("customer.subscription.paused")