collab: Subscribe to Zed Free when a subscription is canceled or paused (#30965)

Marshall Bowers created

This PR makes it so that when a Stripe subscription is canceled or
paused we'll subscribe the user to Zed Free.

Release Notes:

- N/A

Change summary

crates/collab/src/api/billing.rs    | 15 +++++++++++++++
crates/collab/src/stripe_billing.rs | 18 ++++++++++++++++++
2 files changed, 33 insertions(+)

Detailed changes

crates/collab/src/api/billing.rs 🔗

@@ -1185,6 +1185,21 @@ async fn sync_subscription(
             .await?;
     }
 
+    if let Some(stripe_billing) = app.stripe_billing.as_ref() {
+        if subscription.status == SubscriptionStatus::Canceled
+            || subscription.status == SubscriptionStatus::Paused
+        {
+            let stripe_customer_id = billing_customer
+                .stripe_customer_id
+                .parse::<stripe::CustomerId>()
+                .context("failed to parse Stripe customer ID from database")?;
+
+            stripe_billing
+                .subscribe_to_zed_free(stripe_customer_id)
+                .await?;
+        }
+    }
+
     Ok(billing_customer)
 }
 

crates/collab/src/stripe_billing.rs 🔗

@@ -263,6 +263,24 @@ impl StripeBilling {
         Ok(session.url.context("no checkout session URL")?)
     }
 
+    pub async fn subscribe_to_zed_free(
+        &self,
+        customer_id: stripe::CustomerId,
+    ) -> Result<stripe::Subscription> {
+        let zed_free_price_id = self.zed_free_price_id().await?;
+
+        let mut params = stripe::CreateSubscription::new(customer_id);
+        params.items = Some(vec![stripe::CreateSubscriptionItems {
+            price: Some(zed_free_price_id.to_string()),
+            quantity: Some(1),
+            ..Default::default()
+        }]);
+
+        let subscription = stripe::Subscription::create(&self.client, params).await?;
+
+        Ok(subscription)
+    }
+
     pub async fn checkout_with_zed_free(
         &self,
         customer_id: stripe::CustomerId,