collab: Remove unused StripeBilling
methods (#35740)
This PR removes some unused methods from the `StripeBilling` object. Release Notes: - N/A
This commit is contained in:
parent
740597492b
commit
b08e26df60
2 changed files with 4 additions and 341 deletions
|
@ -1,21 +1,15 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::Utc;
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use stripe::SubscriptionStatus;
|
use stripe::SubscriptionStatus;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use crate::db::billing_subscription::SubscriptionKind;
|
|
||||||
use crate::stripe_client::{
|
use crate::stripe_client::{
|
||||||
RealStripeClient, StripeAutomaticTax, StripeClient, StripeCreateMeterEventParams,
|
RealStripeClient, StripeAutomaticTax, StripeClient, StripeCreateSubscriptionItems,
|
||||||
StripeCreateMeterEventPayload, StripeCreateSubscriptionItems, StripeCreateSubscriptionParams,
|
StripeCreateSubscriptionParams, StripeCustomerId, StripePrice, StripePriceId,
|
||||||
StripeCustomerId, StripePrice, StripePriceId, StripeSubscription, StripeSubscriptionId,
|
StripeSubscription,
|
||||||
StripeSubscriptionTrialSettings, StripeSubscriptionTrialSettingsEndBehavior,
|
|
||||||
StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod, UpdateSubscriptionItems,
|
|
||||||
UpdateSubscriptionParams,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct StripeBilling {
|
pub struct StripeBilling {
|
||||||
|
@ -94,30 +88,6 @@ impl StripeBilling {
|
||||||
.ok_or_else(|| crate::Error::Internal(anyhow!("no price found for {lookup_key:?}")))
|
.ok_or_else(|| crate::Error::Internal(anyhow!("no price found for {lookup_key:?}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn determine_subscription_kind(
|
|
||||||
&self,
|
|
||||||
subscription: &StripeSubscription,
|
|
||||||
) -> Option<SubscriptionKind> {
|
|
||||||
let zed_pro_price_id = self.zed_pro_price_id().await.ok()?;
|
|
||||||
let zed_free_price_id = self.zed_free_price_id().await.ok()?;
|
|
||||||
|
|
||||||
subscription.items.iter().find_map(|item| {
|
|
||||||
let price = item.price.as_ref()?;
|
|
||||||
|
|
||||||
if price.id == zed_pro_price_id {
|
|
||||||
Some(if subscription.status == SubscriptionStatus::Trialing {
|
|
||||||
SubscriptionKind::ZedProTrial
|
|
||||||
} else {
|
|
||||||
SubscriptionKind::ZedPro
|
|
||||||
})
|
|
||||||
} else if price.id == zed_free_price_id {
|
|
||||||
Some(SubscriptionKind::ZedFree)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the Stripe customer associated with the provided email address, or creates a new customer, if one does
|
/// Returns the Stripe customer associated with the provided email address, or creates a new customer, if one does
|
||||||
/// not already exist.
|
/// not already exist.
|
||||||
///
|
///
|
||||||
|
@ -150,65 +120,6 @@ impl StripeBilling {
|
||||||
Ok(customer_id)
|
Ok(customer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn subscribe_to_price(
|
|
||||||
&self,
|
|
||||||
subscription_id: &StripeSubscriptionId,
|
|
||||||
price: &StripePrice,
|
|
||||||
) -> Result<()> {
|
|
||||||
let subscription = self.client.get_subscription(subscription_id).await?;
|
|
||||||
|
|
||||||
if subscription_contains_price(&subscription, &price.id) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
const BILLING_THRESHOLD_IN_CENTS: i64 = 20 * 100;
|
|
||||||
|
|
||||||
let price_per_unit = price.unit_amount.unwrap_or_default();
|
|
||||||
let _units_for_billing_threshold = BILLING_THRESHOLD_IN_CENTS / price_per_unit;
|
|
||||||
|
|
||||||
self.client
|
|
||||||
.update_subscription(
|
|
||||||
subscription_id,
|
|
||||||
UpdateSubscriptionParams {
|
|
||||||
items: Some(vec![UpdateSubscriptionItems {
|
|
||||||
price: Some(price.id.clone()),
|
|
||||||
}]),
|
|
||||||
trial_settings: Some(StripeSubscriptionTrialSettings {
|
|
||||||
end_behavior: StripeSubscriptionTrialSettingsEndBehavior {
|
|
||||||
missing_payment_method: StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Cancel
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn bill_model_request_usage(
|
|
||||||
&self,
|
|
||||||
customer_id: &StripeCustomerId,
|
|
||||||
event_name: &str,
|
|
||||||
requests: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let timestamp = Utc::now().timestamp();
|
|
||||||
let idempotency_key = Uuid::new_v4();
|
|
||||||
|
|
||||||
self.client
|
|
||||||
.create_meter_event(StripeCreateMeterEventParams {
|
|
||||||
identifier: &format!("model_requests/{}", idempotency_key),
|
|
||||||
event_name,
|
|
||||||
payload: StripeCreateMeterEventPayload {
|
|
||||||
value: requests as u64,
|
|
||||||
stripe_customer_id: customer_id,
|
|
||||||
},
|
|
||||||
timestamp: Some(timestamp),
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn subscribe_to_zed_free(
|
pub async fn subscribe_to_zed_free(
|
||||||
&self,
|
&self,
|
||||||
customer_id: StripeCustomerId,
|
customer_id: StripeCustomerId,
|
||||||
|
@ -243,14 +154,3 @@ impl StripeBilling {
|
||||||
Ok(subscription)
|
Ok(subscription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription_contains_price(
|
|
||||||
subscription: &StripeSubscription,
|
|
||||||
price_id: &StripePriceId,
|
|
||||||
) -> bool {
|
|
||||||
subscription.items.iter().any(|item| {
|
|
||||||
item.price
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |price| price.id == *price_id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::stripe_billing::StripeBilling;
|
use crate::stripe_billing::StripeBilling;
|
||||||
use crate::stripe_client::{
|
use crate::stripe_client::{FakeStripeClient, StripePrice, StripePriceId, StripePriceRecurring};
|
||||||
FakeStripeClient, StripeCustomerId, StripeMeter, StripeMeterId, StripePrice, StripePriceId,
|
|
||||||
StripePriceRecurring, StripeSubscription, StripeSubscriptionId, StripeSubscriptionItem,
|
|
||||||
StripeSubscriptionItemId, UpdateSubscriptionItems,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn make_stripe_billing() -> (StripeBilling, Arc<FakeStripeClient>) {
|
fn make_stripe_billing() -> (StripeBilling, Arc<FakeStripeClient>) {
|
||||||
let stripe_client = Arc::new(FakeStripeClient::new());
|
let stripe_client = Arc::new(FakeStripeClient::new());
|
||||||
|
@ -21,24 +16,6 @@ fn make_stripe_billing() -> (StripeBilling, Arc<FakeStripeClient>) {
|
||||||
async fn test_initialize() {
|
async fn test_initialize() {
|
||||||
let (stripe_billing, stripe_client) = make_stripe_billing();
|
let (stripe_billing, stripe_client) = make_stripe_billing();
|
||||||
|
|
||||||
// Add test meters
|
|
||||||
let meter1 = StripeMeter {
|
|
||||||
id: StripeMeterId("meter_1".into()),
|
|
||||||
event_name: "event_1".to_string(),
|
|
||||||
};
|
|
||||||
let meter2 = StripeMeter {
|
|
||||||
id: StripeMeterId("meter_2".into()),
|
|
||||||
event_name: "event_2".to_string(),
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.meters
|
|
||||||
.lock()
|
|
||||||
.insert(meter1.id.clone(), meter1);
|
|
||||||
stripe_client
|
|
||||||
.meters
|
|
||||||
.lock()
|
|
||||||
.insert(meter2.id.clone(), meter2);
|
|
||||||
|
|
||||||
// Add test prices
|
// Add test prices
|
||||||
let price1 = StripePrice {
|
let price1 = StripePrice {
|
||||||
id: StripePriceId("price_1".into()),
|
id: StripePriceId("price_1".into()),
|
||||||
|
@ -144,217 +121,3 @@ async fn test_find_or_create_customer_by_email() {
|
||||||
assert_eq!(customer.email.as_deref(), Some(email));
|
assert_eq!(customer.email.as_deref(), Some(email));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_subscribe_to_price() {
|
|
||||||
let (stripe_billing, stripe_client) = make_stripe_billing();
|
|
||||||
|
|
||||||
let price = StripePrice {
|
|
||||||
id: StripePriceId("price_test".into()),
|
|
||||||
unit_amount: Some(2000),
|
|
||||||
lookup_key: Some("test-price".to_string()),
|
|
||||||
recurring: None,
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.prices
|
|
||||||
.lock()
|
|
||||||
.insert(price.id.clone(), price.clone());
|
|
||||||
|
|
||||||
let now = Utc::now();
|
|
||||||
let subscription = StripeSubscription {
|
|
||||||
id: StripeSubscriptionId("sub_test".into()),
|
|
||||||
customer: StripeCustomerId("cus_test".into()),
|
|
||||||
status: stripe::SubscriptionStatus::Active,
|
|
||||||
current_period_start: now.timestamp(),
|
|
||||||
current_period_end: (now + Duration::days(30)).timestamp(),
|
|
||||||
items: vec![],
|
|
||||||
cancel_at: None,
|
|
||||||
cancellation_details: None,
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.subscriptions
|
|
||||||
.lock()
|
|
||||||
.insert(subscription.id.clone(), subscription.clone());
|
|
||||||
|
|
||||||
stripe_billing
|
|
||||||
.subscribe_to_price(&subscription.id, &price)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let update_subscription_calls = stripe_client
|
|
||||||
.update_subscription_calls
|
|
||||||
.lock()
|
|
||||||
.iter()
|
|
||||||
.map(|(id, params)| (id.clone(), params.clone()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
assert_eq!(update_subscription_calls.len(), 1);
|
|
||||||
assert_eq!(update_subscription_calls[0].0, subscription.id);
|
|
||||||
assert_eq!(
|
|
||||||
update_subscription_calls[0].1.items,
|
|
||||||
Some(vec![UpdateSubscriptionItems {
|
|
||||||
price: Some(price.id.clone())
|
|
||||||
}])
|
|
||||||
);
|
|
||||||
|
|
||||||
// Subscribing to a price that is already on the subscription is a no-op.
|
|
||||||
{
|
|
||||||
let now = Utc::now();
|
|
||||||
let subscription = StripeSubscription {
|
|
||||||
id: StripeSubscriptionId("sub_test".into()),
|
|
||||||
customer: StripeCustomerId("cus_test".into()),
|
|
||||||
status: stripe::SubscriptionStatus::Active,
|
|
||||||
current_period_start: now.timestamp(),
|
|
||||||
current_period_end: (now + Duration::days(30)).timestamp(),
|
|
||||||
items: vec![StripeSubscriptionItem {
|
|
||||||
id: StripeSubscriptionItemId("si_test".into()),
|
|
||||||
price: Some(price.clone()),
|
|
||||||
}],
|
|
||||||
cancel_at: None,
|
|
||||||
cancellation_details: None,
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.subscriptions
|
|
||||||
.lock()
|
|
||||||
.insert(subscription.id.clone(), subscription.clone());
|
|
||||||
|
|
||||||
stripe_billing
|
|
||||||
.subscribe_to_price(&subscription.id, &price)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(stripe_client.update_subscription_calls.lock().len(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_subscribe_to_zed_free() {
|
|
||||||
let (stripe_billing, stripe_client) = make_stripe_billing();
|
|
||||||
|
|
||||||
let zed_pro_price = StripePrice {
|
|
||||||
id: StripePriceId("price_1".into()),
|
|
||||||
unit_amount: Some(0),
|
|
||||||
lookup_key: Some("zed-pro".to_string()),
|
|
||||||
recurring: None,
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.prices
|
|
||||||
.lock()
|
|
||||||
.insert(zed_pro_price.id.clone(), zed_pro_price.clone());
|
|
||||||
let zed_free_price = StripePrice {
|
|
||||||
id: StripePriceId("price_2".into()),
|
|
||||||
unit_amount: Some(0),
|
|
||||||
lookup_key: Some("zed-free".to_string()),
|
|
||||||
recurring: None,
|
|
||||||
};
|
|
||||||
stripe_client
|
|
||||||
.prices
|
|
||||||
.lock()
|
|
||||||
.insert(zed_free_price.id.clone(), zed_free_price.clone());
|
|
||||||
|
|
||||||
stripe_billing.initialize().await.unwrap();
|
|
||||||
|
|
||||||
// Customer is subscribed to Zed Free when not already subscribed to a plan.
|
|
||||||
{
|
|
||||||
let customer_id = StripeCustomerId("cus_no_plan".into());
|
|
||||||
|
|
||||||
let subscription = stripe_billing
|
|
||||||
.subscribe_to_zed_free(customer_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(subscription.items[0].price.as_ref(), Some(&zed_free_price));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Customer is not subscribed to Zed Free when they already have an active subscription.
|
|
||||||
{
|
|
||||||
let customer_id = StripeCustomerId("cus_active_subscription".into());
|
|
||||||
|
|
||||||
let now = Utc::now();
|
|
||||||
let existing_subscription = StripeSubscription {
|
|
||||||
id: StripeSubscriptionId("sub_existing_active".into()),
|
|
||||||
customer: customer_id.clone(),
|
|
||||||
status: stripe::SubscriptionStatus::Active,
|
|
||||||
current_period_start: now.timestamp(),
|
|
||||||
current_period_end: (now + Duration::days(30)).timestamp(),
|
|
||||||
items: vec![StripeSubscriptionItem {
|
|
||||||
id: StripeSubscriptionItemId("si_test".into()),
|
|
||||||
price: Some(zed_pro_price.clone()),
|
|
||||||
}],
|
|
||||||
cancel_at: None,
|
|
||||||
cancellation_details: None,
|
|
||||||
};
|
|
||||||
stripe_client.subscriptions.lock().insert(
|
|
||||||
existing_subscription.id.clone(),
|
|
||||||
existing_subscription.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let subscription = stripe_billing
|
|
||||||
.subscribe_to_zed_free(customer_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(subscription, existing_subscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Customer is not subscribed to Zed Free when they already have a trial subscription.
|
|
||||||
{
|
|
||||||
let customer_id = StripeCustomerId("cus_trial_subscription".into());
|
|
||||||
|
|
||||||
let now = Utc::now();
|
|
||||||
let existing_subscription = StripeSubscription {
|
|
||||||
id: StripeSubscriptionId("sub_existing_trial".into()),
|
|
||||||
customer: customer_id.clone(),
|
|
||||||
status: stripe::SubscriptionStatus::Trialing,
|
|
||||||
current_period_start: now.timestamp(),
|
|
||||||
current_period_end: (now + Duration::days(14)).timestamp(),
|
|
||||||
items: vec![StripeSubscriptionItem {
|
|
||||||
id: StripeSubscriptionItemId("si_test".into()),
|
|
||||||
price: Some(zed_pro_price.clone()),
|
|
||||||
}],
|
|
||||||
cancel_at: None,
|
|
||||||
cancellation_details: None,
|
|
||||||
};
|
|
||||||
stripe_client.subscriptions.lock().insert(
|
|
||||||
existing_subscription.id.clone(),
|
|
||||||
existing_subscription.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let subscription = stripe_billing
|
|
||||||
.subscribe_to_zed_free(customer_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(subscription, existing_subscription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_bill_model_request_usage() {
|
|
||||||
let (stripe_billing, stripe_client) = make_stripe_billing();
|
|
||||||
|
|
||||||
let customer_id = StripeCustomerId("cus_test".into());
|
|
||||||
|
|
||||||
stripe_billing
|
|
||||||
.bill_model_request_usage(&customer_id, "some_model/requests", 73)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let create_meter_event_calls = stripe_client
|
|
||||||
.create_meter_event_calls
|
|
||||||
.lock()
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
assert_eq!(create_meter_event_calls.len(), 1);
|
|
||||||
assert!(
|
|
||||||
create_meter_event_calls[0]
|
|
||||||
.identifier
|
|
||||||
.starts_with("model_requests/")
|
|
||||||
);
|
|
||||||
assert_eq!(create_meter_event_calls[0].stripe_customer_id, customer_id);
|
|
||||||
assert_eq!(
|
|
||||||
create_meter_event_calls[0].event_name.as_ref(),
|
|
||||||
"some_model/requests"
|
|
||||||
);
|
|
||||||
assert_eq!(create_meter_event_calls[0].value, 73);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue