billing_subscriptions.rs

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