collab: Add ability to initiate a checkout session for the Zed Free plan (#29767)

Marshall Bowers created

This PR adds the ability to initiate a checkout session for the Zed Free
plan.

Release Notes:

- N/A

Change summary

crates/collab/src/api/billing.rs    |  6 ++++++
crates/collab/src/stripe_billing.rs | 23 +++++++++++++++++++++++
2 files changed, 29 insertions(+)

Detailed changes

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

@@ -256,6 +256,7 @@ async fn list_billing_subscriptions(
 enum ProductCode {
     ZedPro,
     ZedProTrial,
+    ZedFree,
 }
 
 #[derive(Debug, Deserialize)]
@@ -386,6 +387,11 @@ async fn create_billing_subscription(
                 )
                 .await?
         }
+        Some(ProductCode::ZedFree) => {
+            stripe_billing
+                .checkout_with_zed_free(customer_id, &user.github_login, &success_url)
+                .await?
+        }
         None => {
             let default_model = llm_db.model(
                 zed_llm_client::LanguageModelProvider::Anthropic,

crates/collab/src/stripe_billing.rs 🔗

@@ -565,6 +565,29 @@ impl StripeBilling {
         let session = stripe::CheckoutSession::create(&self.client, params).await?;
         Ok(session.url.context("no checkout session URL")?)
     }
+
+    pub async fn checkout_with_zed_free(
+        &self,
+        customer_id: stripe::CustomerId,
+        github_login: &str,
+        success_url: &str,
+    ) -> Result<String> {
+        let zed_free_price_id = self.zed_free_price_id().await?;
+
+        let mut params = stripe::CreateCheckoutSession::new();
+        params.mode = Some(stripe::CheckoutSessionMode::Subscription);
+        params.customer = Some(customer_id);
+        params.client_reference_id = Some(github_login);
+        params.line_items = Some(vec![stripe::CreateCheckoutSessionLineItems {
+            price: Some(zed_free_price_id.to_string()),
+            quantity: Some(1),
+            ..Default::default()
+        }]);
+        params.success_url = Some(success_url);
+
+        let session = stripe::CheckoutSession::create(&self.client, params).await?;
+        Ok(session.url.context("no checkout session URL")?)
+    }
 }
 
 #[derive(Serialize)]