collab: Limit customers to one free trial (#29232)
This PR makes it so customers can only subscribe to the trial once. Release Notes: - N/A
This commit is contained in:
parent
abf2b9d7d3
commit
a0eaede13d
5 changed files with 36 additions and 3 deletions
|
@ -492,7 +492,8 @@ CREATE TABLE IF NOT EXISTS billing_customers (
|
|||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id),
|
||||
has_overdue_invoices BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
stripe_customer_id TEXT NOT NULL
|
||||
stripe_customer_id TEXT NOT NULL,
|
||||
trial_started_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "uix_billing_customers_on_user_id" ON billing_customers (user_id);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
alter table billing_customers
|
||||
add column trial_started_at timestamp without time zone;
|
|
@ -287,7 +287,7 @@ async fn create_billing_subscription(
|
|||
}
|
||||
}
|
||||
|
||||
let customer_id = if let Some(existing_customer) = existing_billing_customer {
|
||||
let customer_id = if let Some(existing_customer) = &existing_billing_customer {
|
||||
CustomerId::from_str(&existing_customer.stripe_customer_id)
|
||||
.context("failed to parse customer ID")?
|
||||
} else {
|
||||
|
@ -320,6 +320,15 @@ async fn create_billing_subscription(
|
|||
.await?
|
||||
}
|
||||
Some(ProductCode::ZedProTrial) => {
|
||||
if let Some(existing_billing_customer) = &existing_billing_customer {
|
||||
if existing_billing_customer.trial_started_at.is_some() {
|
||||
return Err(Error::http(
|
||||
StatusCode::FORBIDDEN,
|
||||
"user already used free trial".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
stripe_billing
|
||||
.checkout_with_zed_pro_trial(
|
||||
app.config.zed_pro_price_id()?,
|
||||
|
@ -817,6 +826,24 @@ async fn handle_customer_subscription_event(
|
|||
.await?
|
||||
.ok_or_else(|| anyhow!("billing customer not found"))?;
|
||||
|
||||
if let Some(SubscriptionKind::ZedProTrial) = subscription_kind {
|
||||
if subscription.status == SubscriptionStatus::Trialing {
|
||||
let current_period_start =
|
||||
DateTime::from_timestamp(subscription.current_period_start, 0)
|
||||
.ok_or_else(|| anyhow!("No trial subscription period start"))?;
|
||||
|
||||
app.db
|
||||
.update_billing_customer(
|
||||
billing_customer.id,
|
||||
&UpdateBillingCustomerParams {
|
||||
trial_started_at: ActiveValue::set(Some(current_period_start.naive_utc())),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
let was_canceled_due_to_payment_failure = subscription.status == SubscriptionStatus::Canceled
|
||||
&& subscription
|
||||
.cancellation_details
|
||||
|
|
|
@ -11,6 +11,7 @@ pub struct UpdateBillingCustomerParams {
|
|||
pub user_id: ActiveValue<UserId>,
|
||||
pub stripe_customer_id: ActiveValue<String>,
|
||||
pub has_overdue_invoices: ActiveValue<bool>,
|
||||
pub trial_started_at: ActiveValue<Option<DateTime>>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
|
@ -45,7 +46,8 @@ impl Database {
|
|||
user_id: params.user_id.clone(),
|
||||
stripe_customer_id: params.stripe_customer_id.clone(),
|
||||
has_overdue_invoices: params.has_overdue_invoices.clone(),
|
||||
..Default::default()
|
||||
trial_started_at: params.trial_started_at.clone(),
|
||||
created_at: ActiveValue::not_set(),
|
||||
})
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
|
|
|
@ -10,6 +10,7 @@ pub struct Model {
|
|||
pub user_id: UserId,
|
||||
pub stripe_customer_id: String,
|
||||
pub has_overdue_invoices: bool,
|
||||
pub trial_started_at: Option<DateTime>,
|
||||
pub created_at: DateTime,
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue