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