Add overdue invoices check (#31290)
- Rename current_user_account_too_young to account_too_young for consistency - Add has_overdue_invoices field to track billing status - Block edit predictions when user has overdue invoices - Add overdue invoice warning to inline completion menu Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
parent
cb112a4012
commit
ca72efe701
7 changed files with 65 additions and 7 deletions
|
@ -1973,7 +1973,7 @@ impl AgentPanel {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.user_store.read(cx).current_user_account_too_young() {
|
if self.user_store.read(cx).account_too_young() {
|
||||||
Some(self.render_young_account_upsell(cx).into_any_element())
|
Some(self.render_young_account_upsell(cx).into_any_element())
|
||||||
} else {
|
} else {
|
||||||
Some(self.render_trial_upsell(cx).into_any_element())
|
Some(self.render_trial_upsell(cx).into_any_element())
|
||||||
|
|
|
@ -109,6 +109,7 @@ pub struct UserStore {
|
||||||
edit_predictions_usage_limit: Option<proto::UsageLimit>,
|
edit_predictions_usage_limit: Option<proto::UsageLimit>,
|
||||||
is_usage_based_billing_enabled: Option<bool>,
|
is_usage_based_billing_enabled: Option<bool>,
|
||||||
account_too_young: Option<bool>,
|
account_too_young: Option<bool>,
|
||||||
|
has_overdue_invoices: Option<bool>,
|
||||||
current_user: watch::Receiver<Option<Arc<User>>>,
|
current_user: watch::Receiver<Option<Arc<User>>>,
|
||||||
accepted_tos_at: Option<Option<DateTime<Utc>>>,
|
accepted_tos_at: Option<Option<DateTime<Utc>>>,
|
||||||
contacts: Vec<Arc<Contact>>,
|
contacts: Vec<Arc<Contact>>,
|
||||||
|
@ -176,6 +177,7 @@ impl UserStore {
|
||||||
edit_predictions_usage_limit: None,
|
edit_predictions_usage_limit: None,
|
||||||
is_usage_based_billing_enabled: None,
|
is_usage_based_billing_enabled: None,
|
||||||
account_too_young: None,
|
account_too_young: None,
|
||||||
|
has_overdue_invoices: None,
|
||||||
accepted_tos_at: None,
|
accepted_tos_at: None,
|
||||||
contacts: Default::default(),
|
contacts: Default::default(),
|
||||||
incoming_contact_requests: Default::default(),
|
incoming_contact_requests: Default::default(),
|
||||||
|
@ -350,6 +352,7 @@ impl UserStore {
|
||||||
.and_then(|trial_started_at| DateTime::from_timestamp(trial_started_at as i64, 0));
|
.and_then(|trial_started_at| DateTime::from_timestamp(trial_started_at as i64, 0));
|
||||||
this.is_usage_based_billing_enabled = message.payload.is_usage_based_billing_enabled;
|
this.is_usage_based_billing_enabled = message.payload.is_usage_based_billing_enabled;
|
||||||
this.account_too_young = message.payload.account_too_young;
|
this.account_too_young = message.payload.account_too_young;
|
||||||
|
this.has_overdue_invoices = message.payload.has_overdue_invoices;
|
||||||
|
|
||||||
if let Some(usage) = message.payload.usage {
|
if let Some(usage) = message.payload.usage {
|
||||||
this.model_request_usage_amount = Some(usage.model_requests_usage_amount);
|
this.model_request_usage_amount = Some(usage.model_requests_usage_amount);
|
||||||
|
@ -755,11 +758,16 @@ impl UserStore {
|
||||||
self.current_user.clone()
|
self.current_user.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the current user's account is too new to use the service
|
/// Returns whether the user's account is too new to use the service.
|
||||||
pub fn current_user_account_too_young(&self) -> bool {
|
pub fn account_too_young(&self) -> bool {
|
||||||
self.account_too_young.unwrap_or(false)
|
self.account_too_young.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether the current user has overdue invoices and usage should be blocked.
|
||||||
|
pub fn has_overdue_invoices(&self) -> bool {
|
||||||
|
self.has_overdue_invoices.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn current_user_has_accepted_terms(&self) -> Option<bool> {
|
pub fn current_user_has_accepted_terms(&self) -> Option<bool> {
|
||||||
self.accepted_tos_at
|
self.accepted_tos_at
|
||||||
.map(|accepted_tos_at| accepted_tos_at.is_some())
|
.map(|accepted_tos_at| accepted_tos_at.is_some())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::db::billing_subscription::SubscriptionKind;
|
use crate::db::billing_subscription::SubscriptionKind;
|
||||||
use crate::db::{billing_subscription, user};
|
use crate::db::{billing_customer, billing_subscription, user};
|
||||||
use crate::llm::AGENT_EXTENDED_TRIAL_FEATURE_FLAG;
|
use crate::llm::AGENT_EXTENDED_TRIAL_FEATURE_FLAG;
|
||||||
use crate::{Config, db::billing_preference};
|
use crate::{Config, db::billing_preference};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
|
@ -32,6 +32,8 @@ pub struct LlmTokenClaims {
|
||||||
pub enable_model_request_overages: bool,
|
pub enable_model_request_overages: bool,
|
||||||
pub model_request_overages_spend_limit_in_cents: u32,
|
pub model_request_overages_spend_limit_in_cents: u32,
|
||||||
pub can_use_web_search_tool: bool,
|
pub can_use_web_search_tool: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_overdue_invoices: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const LLM_TOKEN_LIFETIME: Duration = Duration::from_secs(60 * 60);
|
const LLM_TOKEN_LIFETIME: Duration = Duration::from_secs(60 * 60);
|
||||||
|
@ -40,6 +42,7 @@ impl LlmTokenClaims {
|
||||||
pub fn create(
|
pub fn create(
|
||||||
user: &user::Model,
|
user: &user::Model,
|
||||||
is_staff: bool,
|
is_staff: bool,
|
||||||
|
billing_customer: billing_customer::Model,
|
||||||
billing_preferences: Option<billing_preference::Model>,
|
billing_preferences: Option<billing_preference::Model>,
|
||||||
feature_flags: &Vec<String>,
|
feature_flags: &Vec<String>,
|
||||||
subscription: billing_subscription::Model,
|
subscription: billing_subscription::Model,
|
||||||
|
@ -99,6 +102,7 @@ impl LlmTokenClaims {
|
||||||
.map_or(0, |preferences| {
|
.map_or(0, |preferences| {
|
||||||
preferences.model_request_overages_spend_limit_in_cents as u32
|
preferences.model_request_overages_spend_limit_in_cents as u32
|
||||||
}),
|
}),
|
||||||
|
has_overdue_invoices: billing_customer.has_overdue_invoices,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(jsonwebtoken::encode(
|
Ok(jsonwebtoken::encode(
|
||||||
|
|
|
@ -2748,6 +2748,7 @@ async fn make_update_user_plan_message(
|
||||||
Ok(proto::UpdateUserPlan {
|
Ok(proto::UpdateUserPlan {
|
||||||
plan: plan.into(),
|
plan: plan.into(),
|
||||||
trial_started_at: billing_customer
|
trial_started_at: billing_customer
|
||||||
|
.as_ref()
|
||||||
.and_then(|billing_customer| billing_customer.trial_started_at)
|
.and_then(|billing_customer| billing_customer.trial_started_at)
|
||||||
.map(|trial_started_at| trial_started_at.and_utc().timestamp() as u64),
|
.map(|trial_started_at| trial_started_at.and_utc().timestamp() as u64),
|
||||||
is_usage_based_billing_enabled: if is_staff {
|
is_usage_based_billing_enabled: if is_staff {
|
||||||
|
@ -2762,6 +2763,8 @@ async fn make_update_user_plan_message(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
account_too_young: Some(account_too_young),
|
account_too_young: Some(account_too_young),
|
||||||
|
has_overdue_invoices: billing_customer
|
||||||
|
.map(|billing_customer| billing_customer.has_overdue_invoices),
|
||||||
usage: usage.map(|usage| {
|
usage: usage.map(|usage| {
|
||||||
let plan = match plan {
|
let plan = match plan {
|
||||||
proto::Plan::Free => zed_llm_client::Plan::ZedFree,
|
proto::Plan::Free => zed_llm_client::Plan::ZedFree,
|
||||||
|
@ -4077,6 +4080,7 @@ async fn get_llm_api_token(
|
||||||
let token = LlmTokenClaims::create(
|
let token = LlmTokenClaims::create(
|
||||||
&user,
|
&user,
|
||||||
session.is_staff(),
|
session.is_staff(),
|
||||||
|
billing_customer,
|
||||||
billing_preferences,
|
billing_preferences,
|
||||||
&flags,
|
&flags,
|
||||||
billing_subscription,
|
billing_subscription,
|
||||||
|
|
|
@ -745,7 +745,7 @@ impl InlineCompletionButton {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.separator();
|
.separator();
|
||||||
} else if self.user_store.read(cx).current_user_account_too_young() {
|
} else if self.user_store.read(cx).account_too_young() {
|
||||||
menu = menu
|
menu = menu
|
||||||
.custom_entry(
|
.custom_entry(
|
||||||
|_window, _cx| {
|
|_window, _cx| {
|
||||||
|
@ -785,6 +785,46 @@ impl InlineCompletionButton {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.separator();
|
.separator();
|
||||||
|
} else if self.user_store.read(cx).has_overdue_invoices() {
|
||||||
|
menu = menu
|
||||||
|
.custom_entry(
|
||||||
|
|_window, _cx| {
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::Warning)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.color(Color::Warning),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Label::new("You have an outstanding invoice")
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Warning),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
},
|
||||||
|
|window, cx| {
|
||||||
|
window.dispatch_action(
|
||||||
|
Box::new(OpenZedUrl {
|
||||||
|
url: zed_urls::account_url(cx),
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.entry(
|
||||||
|
"Check your payment status or contact us at billing-support@zed.dev to continue using this feature.",
|
||||||
|
None,
|
||||||
|
|window, cx| {
|
||||||
|
window.dispatch_action(
|
||||||
|
Box::new(OpenZedUrl {
|
||||||
|
url: zed_urls::account_url(cx),
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.build_language_settings_menu(menu, window, cx).when(
|
self.build_language_settings_menu(menu, window, cx).when(
|
||||||
|
|
|
@ -28,6 +28,7 @@ message UpdateUserPlan {
|
||||||
optional SubscriptionUsage usage = 4;
|
optional SubscriptionUsage usage = 4;
|
||||||
optional SubscriptionPeriod subscription_period = 5;
|
optional SubscriptionPeriod subscription_period = 5;
|
||||||
optional bool account_too_young = 6;
|
optional bool account_too_young = 6;
|
||||||
|
optional bool has_overdue_invoices = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SubscriptionPeriod {
|
message SubscriptionPeriod {
|
||||||
|
|
|
@ -1578,8 +1578,9 @@ impl inline_completion::EditPredictionProvider for ZetaInlineCompletionProvider
|
||||||
.zeta
|
.zeta
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.user_store
|
.user_store
|
||||||
.read(cx)
|
.read_with(cx, |user_store, _| {
|
||||||
.current_user_account_too_young()
|
user_store.account_too_young() || user_store.has_overdue_invoices()
|
||||||
|
})
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue