1use crate::db::{BillingCustomerId, BillingSubscriptionId};
2use sea_orm::entity::prelude::*;
3use serde::Serialize;
4
5/// A billing subscription.
6#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
7#[sea_orm(table_name = "billing_subscriptions")]
8pub struct Model {
9 #[sea_orm(primary_key)]
10 pub id: BillingSubscriptionId,
11 pub billing_customer_id: BillingCustomerId,
12 pub kind: Option<SubscriptionKind>,
13 pub stripe_subscription_id: String,
14 pub stripe_subscription_status: StripeSubscriptionStatus,
15 pub stripe_cancel_at: Option<DateTime>,
16 pub stripe_cancellation_reason: Option<StripeCancellationReason>,
17 pub stripe_current_period_start: Option<i64>,
18 pub stripe_current_period_end: Option<i64>,
19 pub created_at: DateTime,
20}
21
22impl Model {
23 pub fn current_period_start_at(&self) -> Option<DateTimeUtc> {
24 let period_start = self.stripe_current_period_start?;
25 chrono::DateTime::from_timestamp(period_start, 0)
26 }
27
28 pub fn current_period_end_at(&self) -> Option<DateTimeUtc> {
29 let period_end = self.stripe_current_period_end?;
30 chrono::DateTime::from_timestamp(period_end, 0)
31 }
32}
33
34#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
35pub enum Relation {
36 #[sea_orm(
37 belongs_to = "super::billing_customer::Entity",
38 from = "Column::BillingCustomerId",
39 to = "super::billing_customer::Column::Id"
40 )]
41 BillingCustomer,
42}
43
44impl Related<super::billing_customer::Entity> for Entity {
45 fn to() -> RelationDef {
46 Relation::BillingCustomer.def()
47 }
48}
49
50impl ActiveModelBehavior for ActiveModel {}
51
52#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Hash, Serialize)]
53#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)")]
54#[serde(rename_all = "snake_case")]
55pub enum SubscriptionKind {
56 #[sea_orm(string_value = "zed_pro")]
57 ZedPro,
58 #[sea_orm(string_value = "zed_pro_trial")]
59 ZedProTrial,
60 #[sea_orm(string_value = "zed_free")]
61 ZedFree,
62}
63
64impl From<SubscriptionKind> for zed_llm_client::Plan {
65 fn from(value: SubscriptionKind) -> Self {
66 match value {
67 SubscriptionKind::ZedPro => Self::ZedPro,
68 SubscriptionKind::ZedProTrial => Self::ZedProTrial,
69 SubscriptionKind::ZedFree => Self::Free,
70 }
71 }
72}
73
74/// The status of a Stripe subscription.
75///
76/// [Stripe docs](https://docs.stripe.com/api/subscriptions/object#subscription_object-status)
77#[derive(
78 Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default, Hash, Serialize,
79)]
80#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)")]
81#[serde(rename_all = "snake_case")]
82pub enum StripeSubscriptionStatus {
83 #[default]
84 #[sea_orm(string_value = "incomplete")]
85 Incomplete,
86 #[sea_orm(string_value = "incomplete_expired")]
87 IncompleteExpired,
88 #[sea_orm(string_value = "trialing")]
89 Trialing,
90 #[sea_orm(string_value = "active")]
91 Active,
92 #[sea_orm(string_value = "past_due")]
93 PastDue,
94 #[sea_orm(string_value = "canceled")]
95 Canceled,
96 #[sea_orm(string_value = "unpaid")]
97 Unpaid,
98 #[sea_orm(string_value = "paused")]
99 Paused,
100}
101
102impl StripeSubscriptionStatus {
103 pub fn is_cancelable(&self) -> bool {
104 match self {
105 Self::Trialing | Self::Active | Self::PastDue => true,
106 Self::Incomplete
107 | Self::IncompleteExpired
108 | Self::Canceled
109 | Self::Unpaid
110 | Self::Paused => false,
111 }
112 }
113}
114
115/// The cancellation reason for a Stripe subscription.
116///
117/// [Stripe docs](https://docs.stripe.com/api/subscriptions/object#subscription_object-cancellation_details-reason)
118#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Hash, Serialize)]
119#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)")]
120#[serde(rename_all = "snake_case")]
121pub enum StripeCancellationReason {
122 #[sea_orm(string_value = "cancellation_requested")]
123 CancellationRequested,
124 #[sea_orm(string_value = "payment_disputed")]
125 PaymentDisputed,
126 #[sea_orm(string_value = "payment_failed")]
127 PaymentFailed,
128}