Skip to content

Commit

Permalink
collab: Fix issues with syncing LLM usage to Stripe (zed-industries#1…
Browse files Browse the repository at this point in the history
…8970)

This PR fixes some issues with our previous approach to synching LLM
usage over to Stripe.

We now have a separate LLM access price in Stripe that is a marker price
to allow us to create the initial subscription with that as its
subscription item

We then dynamically set the LLM usage price during the reconciliation
sync based on the usage for the current month.

Release Notes:

- N/A

---------

Co-authored-by: Antonio <[email protected]>
Co-authored-by: Richard <[email protected]>
  • Loading branch information
3 people authored and noaccOS committed Oct 19, 2024
1 parent 65bfb22 commit c249c4e
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 21 deletions.
51 changes: 31 additions & 20 deletions crates/collab/src/api/billing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,10 @@ async fn create_billing_subscription(
.await?
.ok_or_else(|| anyhow!("user not found"))?;

let Some((stripe_client, stripe_price_id)) = app
let Some((stripe_client, stripe_access_price_id)) = app
.stripe_client
.clone()
.zip(app.config.stripe_llm_usage_price_id.clone())
.zip(app.config.stripe_llm_access_price_id.clone())
else {
log::error!("failed to retrieve Stripe client or price ID");
Err(Error::http(
Expand Down Expand Up @@ -232,8 +232,8 @@ async fn create_billing_subscription(
params.customer = Some(customer_id);
params.client_reference_id = Some(user.github_login.as_str());
params.line_items = Some(vec![CreateCheckoutSessionLineItems {
price: Some(stripe_price_id.to_string()),
quantity: Some(0),
price: Some(stripe_access_price_id.to_string()),
quantity: Some(1),
..Default::default()
}]);
let success_url = format!("{}/account", app.config.zed_dot_dev_url());
Expand Down Expand Up @@ -787,22 +787,33 @@ async fn update_stripe_subscription(
monthly_spending.saturating_sub(FREE_TIER_MONTHLY_SPENDING_LIMIT);

let new_quantity = (monthly_spending_over_free_tier.0 as f32 / 100.).ceil();
Subscription::update(
stripe_client,
&subscription_id,
stripe::UpdateSubscription {
items: Some(vec![stripe::UpdateSubscriptionItems {
// TODO: Do we need to send up the `id` if a subscription item
// with this price already exists, or will Stripe take care of
// it?
id: None,
price: Some(stripe_llm_usage_price_id.to_string()),
quantity: Some(new_quantity as u64),
..Default::default()
}]),
let current_subscription = Subscription::retrieve(stripe_client, &subscription_id, &[]).await?;

let mut update_params = stripe::UpdateSubscription {
proration_behavior: Some(
stripe::generated::billing::subscription::SubscriptionProrationBehavior::None,
),
..Default::default()
};

if let Some(existing_item) = current_subscription.items.data.iter().find(|item| {
item.price.as_ref().map_or(false, |price| {
price.id == stripe_llm_usage_price_id.as_ref()
})
}) {
update_params.items = Some(vec![stripe::UpdateSubscriptionItems {
id: Some(existing_item.id.to_string()),
quantity: Some(new_quantity as u64),
..Default::default()
},
)
.await?;
}]);
} else {
update_params.items = Some(vec![stripe::UpdateSubscriptionItems {
price: Some(stripe_llm_usage_price_id.to_string()),
quantity: Some(new_quantity as u64),
..Default::default()
}]);
}

Subscription::update(stripe_client, &subscription_id, update_params).await?;
Ok(())
}
2 changes: 2 additions & 0 deletions crates/collab/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ pub struct Config {
pub slack_panics_webhook: Option<String>,
pub auto_join_channel_id: Option<ChannelId>,
pub stripe_api_key: Option<String>,
pub stripe_llm_access_price_id: Option<Arc<str>>,
pub stripe_llm_usage_price_id: Option<Arc<str>>,
pub supermaven_admin_api_key: Option<Arc<str>>,
pub user_backfiller_github_access_token: Option<Arc<str>>,
Expand Down Expand Up @@ -237,6 +238,7 @@ impl Config {
migrations_path: None,
seed_path: None,
stripe_api_key: None,
stripe_llm_access_price_id: None,
stripe_llm_usage_price_id: None,
supermaven_admin_api_key: None,
user_backfiller_github_access_token: None,
Expand Down
3 changes: 2 additions & 1 deletion crates/collab/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ async fn main() -> Result<()> {
.await
.trace_err();

if let Some(llm_db) = llm_db {
if let Some(mut llm_db) = llm_db {
llm_db.initialize().await?;
sync_llm_usage_with_stripe_periodically(state.clone(), llm_db);
}

Expand Down
1 change: 1 addition & 0 deletions crates/collab/src/tests/test_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ impl TestServer {
migrations_path: None,
seed_path: None,
stripe_api_key: None,
stripe_llm_access_price_id: None,
stripe_llm_usage_price_id: None,
supermaven_admin_api_key: None,
user_backfiller_github_access_token: None,
Expand Down

0 comments on commit c249c4e

Please sign in to comment.