1use crate::db::billing_subscription::{
2 StripeCancellationReason, StripeSubscriptionStatus, SubscriptionKind,
3};
4
5use super::*;
6
7#[derive(Debug)]
8pub struct CreateBillingSubscriptionParams {
9 pub billing_customer_id: BillingCustomerId,
10 pub kind: Option<SubscriptionKind>,
11 pub stripe_subscription_id: String,
12 pub stripe_subscription_status: StripeSubscriptionStatus,
13 pub stripe_cancellation_reason: Option<StripeCancellationReason>,
14 pub stripe_current_period_start: Option<i64>,
15 pub stripe_current_period_end: Option<i64>,
16}
17
18#[derive(Debug, Default)]
19pub struct UpdateBillingSubscriptionParams {
20 pub billing_customer_id: ActiveValue<BillingCustomerId>,
21 pub kind: ActiveValue<Option<SubscriptionKind>>,
22 pub stripe_subscription_id: ActiveValue<String>,
23 pub stripe_subscription_status: ActiveValue<StripeSubscriptionStatus>,
24 pub stripe_cancel_at: ActiveValue<Option<DateTime>>,
25 pub stripe_cancellation_reason: ActiveValue<Option<StripeCancellationReason>>,
26 pub stripe_current_period_start: ActiveValue<Option<i64>>,
27 pub stripe_current_period_end: ActiveValue<Option<i64>>,
28}
29
30impl Database {
31 /// Creates a new billing subscription.
32 pub async fn create_billing_subscription(
33 &self,
34 params: &CreateBillingSubscriptionParams,
35 ) -> Result<()> {
36 self.transaction(|tx| async move {
37 billing_subscription::Entity::insert(billing_subscription::ActiveModel {
38 billing_customer_id: ActiveValue::set(params.billing_customer_id),
39 kind: ActiveValue::set(params.kind),
40 stripe_subscription_id: ActiveValue::set(params.stripe_subscription_id.clone()),
41 stripe_subscription_status: ActiveValue::set(params.stripe_subscription_status),
42 stripe_cancellation_reason: ActiveValue::set(params.stripe_cancellation_reason),
43 stripe_current_period_start: ActiveValue::set(params.stripe_current_period_start),
44 stripe_current_period_end: ActiveValue::set(params.stripe_current_period_end),
45 ..Default::default()
46 })
47 .exec_without_returning(&*tx)
48 .await?;
49
50 Ok(())
51 })
52 .await
53 }
54
55 /// Updates the specified billing subscription.
56 pub async fn update_billing_subscription(
57 &self,
58 id: BillingSubscriptionId,
59 params: &UpdateBillingSubscriptionParams,
60 ) -> Result<()> {
61 self.transaction(|tx| async move {
62 billing_subscription::Entity::update(billing_subscription::ActiveModel {
63 id: ActiveValue::set(id),
64 billing_customer_id: params.billing_customer_id.clone(),
65 kind: params.kind.clone(),
66 stripe_subscription_id: params.stripe_subscription_id.clone(),
67 stripe_subscription_status: params.stripe_subscription_status.clone(),
68 stripe_cancel_at: params.stripe_cancel_at.clone(),
69 stripe_cancellation_reason: params.stripe_cancellation_reason.clone(),
70 stripe_current_period_start: params.stripe_current_period_start.clone(),
71 stripe_current_period_end: params.stripe_current_period_end.clone(),
72 created_at: ActiveValue::not_set(),
73 })
74 .exec(&*tx)
75 .await?;
76
77 Ok(())
78 })
79 .await
80 }
81
82 /// Returns the billing subscription with the specified ID.
83 pub async fn get_billing_subscription_by_id(
84 &self,
85 id: BillingSubscriptionId,
86 ) -> Result<Option<billing_subscription::Model>> {
87 self.transaction(|tx| async move {
88 Ok(billing_subscription::Entity::find_by_id(id)
89 .one(&*tx)
90 .await?)
91 })
92 .await
93 }
94
95 /// Returns the billing subscription with the specified Stripe subscription ID.
96 pub async fn get_billing_subscription_by_stripe_subscription_id(
97 &self,
98 stripe_subscription_id: &str,
99 ) -> Result<Option<billing_subscription::Model>> {
100 self.transaction(|tx| async move {
101 Ok(billing_subscription::Entity::find()
102 .filter(
103 billing_subscription::Column::StripeSubscriptionId.eq(stripe_subscription_id),
104 )
105 .one(&*tx)
106 .await?)
107 })
108 .await
109 }
110
111 /// Returns all of the billing subscriptions for the user with the specified ID.
112 ///
113 /// Note that this returns the subscriptions regardless of their status.
114 /// If you're wanting to check if a use has an active billing subscription,
115 /// use `get_active_billing_subscriptions` instead.
116 pub async fn get_billing_subscriptions(
117 &self,
118 user_id: UserId,
119 ) -> Result<Vec<billing_subscription::Model>> {
120 self.transaction(|tx| async move {
121 let subscriptions = billing_subscription::Entity::find()
122 .inner_join(billing_customer::Entity)
123 .filter(billing_customer::Column::UserId.eq(user_id))
124 .order_by_asc(billing_subscription::Column::Id)
125 .all(&*tx)
126 .await?;
127
128 Ok(subscriptions)
129 })
130 .await
131 }
132
133 pub async fn get_active_billing_subscriptions(
134 &self,
135 user_ids: HashSet<UserId>,
136 ) -> Result<HashMap<UserId, (billing_customer::Model, billing_subscription::Model)>> {
137 self.transaction(|tx| {
138 let user_ids = user_ids.clone();
139 async move {
140 let mut rows = billing_subscription::Entity::find()
141 .inner_join(billing_customer::Entity)
142 .select_also(billing_customer::Entity)
143 .filter(billing_customer::Column::UserId.is_in(user_ids))
144 .filter(
145 billing_subscription::Column::StripeSubscriptionStatus
146 .eq(StripeSubscriptionStatus::Active),
147 )
148 .order_by_asc(billing_subscription::Column::Id)
149 .stream(&*tx)
150 .await?;
151
152 let mut subscriptions = HashMap::default();
153 while let Some(row) = rows.next().await {
154 if let (subscription, Some(customer)) = row? {
155 subscriptions.insert(customer.user_id, (customer, subscription));
156 }
157 }
158 Ok(subscriptions)
159 }
160 })
161 .await
162 }
163
164 /// Returns whether the user has an active billing subscription.
165 pub async fn has_active_billing_subscription(&self, user_id: UserId) -> Result<bool> {
166 Ok(self.count_active_billing_subscriptions(user_id).await? > 0)
167 }
168
169 /// Returns the count of the active billing subscriptions for the user with the specified ID.
170 pub async fn count_active_billing_subscriptions(&self, user_id: UserId) -> Result<usize> {
171 self.transaction(|tx| async move {
172 let count = billing_subscription::Entity::find()
173 .inner_join(billing_customer::Entity)
174 .filter(
175 billing_customer::Column::UserId.eq(user_id).and(
176 billing_subscription::Column::StripeSubscriptionStatus
177 .eq(StripeSubscriptionStatus::Active),
178 ),
179 )
180 .count(&*tx)
181 .await?;
182
183 Ok(count as usize)
184 })
185 .await
186 }
187}