diff --git a/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_deleted_handler.rs b/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_deleted_handler.rs index 079f748806..bc520a22d0 100644 --- a/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_deleted_handler.rs +++ b/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_deleted_handler.rs @@ -4,6 +4,10 @@ use crate::stripe::webhook_event_handlers::stripe_webhook_summary::StripeWebhook use log::{error, warn}; use sqlx::MySqlPool; use stripe::Subscription; +use database_queries::queries::billing::subscriptions::get_subscription_by_stripe_id::get_subscription_by_stripe_id; +use database_queries::queries::billing::subscriptions::upsert_subscription_by_stripe_id::UpsertSubscriptionByStripeId; +use reusable_types::stripe::stripe_subscription_status::StripeSubscriptionStatus; +use crate::stripe::webhook_event_handlers::customer_subscription::calculate_subscription_end_date::calculate_subscription_end_date; /// Handle event type: 'customer.subscription.deleted' /// Sent when a customer’s subscription ends. @@ -17,11 +21,67 @@ pub async fn customer_subscription_deleted_handler( StripeWebhookError::ServerError // NB: This was probably *our* fault. })?; + let mut should_process_update = true; + + let mut action_was_taken = false; + let mut should_ignore_retry = false; + + // NB: It's possible to receive events out of order. + let maybe_existing_subscription = get_subscription_by_stripe_id(&summary.stripe_subscription_id, &mysql_pool) + .await + .map_err(|err| { + error!("Mysql error: {:?}", err); + StripeWebhookError::ServerError + })?; + + if let Some(existing_subscription) = maybe_existing_subscription { + match existing_subscription.maybe_stripe_subscription_status { + Some(StripeSubscriptionStatus::Canceled) => { + // NB: This is a terminal status and the subscription cannot be updated any further. + should_process_update = false; + should_ignore_retry = true; + } + _ => {} + } + } + + // NB: Even if we haven't received a record before, we should still be able to "tombstone" it + // once we detect the deletion. + if should_process_update { + let upsert = UpsertSubscriptionByStripeId { + stripe_subscription_id: &summary.stripe_subscription_id, + maybe_user_token: summary.user_token.as_deref(), + subscription_category: "todo", + subscription_product_key: "todo", + maybe_stripe_product_id: Some(&summary.stripe_product_id), + maybe_stripe_customer_id: Some(&summary.stripe_customer_id), + maybe_stripe_recurring_interval: Some(summary.subscription_interval), + maybe_stripe_subscription_status: Some(summary.stripe_subscription_status), + maybe_stripe_is_production: Some(summary.stripe_is_production), + subscription_start_at: summary.subscription_start_date, + current_billing_period_start_at: summary.current_billing_period_start, + current_billing_period_end_at: summary.current_billing_period_end, + subscription_expires_at: calculate_subscription_end_date(&summary), + maybe_cancel_at: summary.maybe_cancel_at, + maybe_canceled_at: summary.maybe_canceled_at, + }; + + let _r = upsert.upsert(mysql_pool) + .await + .map_err(|err| { + error!("Mysql error: {:?}", err); + StripeWebhookError::ServerError + })?; + + action_was_taken = true; + should_ignore_retry = true; + } + Ok(StripeWebhookSummary { maybe_user_token: summary.user_token, maybe_event_entity_id: Some(summary.stripe_subscription_id), maybe_stripe_customer_id: Some(summary.stripe_customer_id), - action_was_taken: false, - should_ignore_retry: false, + action_was_taken, + should_ignore_retry, }) } diff --git a/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_updated_handler.rs b/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_updated_handler.rs index 640950662a..36e21ad69c 100644 --- a/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_updated_handler.rs +++ b/crates/component_subsystem/billing/src/stripe/webhook_event_handlers/customer_subscription/customer_subscription_updated_handler.rs @@ -45,8 +45,6 @@ pub async fn customer_subscription_updated_handler( } } - // TODO: record cancel_at (future_cancel_at), canceled_at, ended_at (if subscription ended, when it ended), start_date - if should_process_update { let upsert = UpsertSubscriptionByStripeId { stripe_subscription_id: &summary.stripe_subscription_id,