billing_subscriptions.rs

  1use anyhow::Context as _;
  2
  3use crate::db::billing_subscription::{
  4    StripeCancellationReason, StripeSubscriptionStatus, SubscriptionKind,
  5};
  6
  7use super::*;
  8
  9#[derive(Debug)]
 10pub struct CreateBillingSubscriptionParams {
 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_cancellation_reason: Option<StripeCancellationReason>,
 16    pub stripe_current_period_start: Option<i64>,
 17    pub stripe_current_period_end: Option<i64>,
 18}
 19
 20#[derive(Debug, Default)]
 21pub struct UpdateBillingSubscriptionParams {
 22    pub billing_customer_id: ActiveValue<BillingCustomerId>,
 23    pub kind: ActiveValue<Option<SubscriptionKind>>,
 24    pub stripe_subscription_id: ActiveValue<String>,
 25    pub stripe_subscription_status: ActiveValue<StripeSubscriptionStatus>,
 26    pub stripe_cancel_at: ActiveValue<Option<DateTime>>,
 27    pub stripe_cancellation_reason: ActiveValue<Option<StripeCancellationReason>>,
 28    pub stripe_current_period_start: ActiveValue<Option<i64>>,
 29    pub stripe_current_period_end: ActiveValue<Option<i64>>,
 30}
 31
 32impl Database {
 33    /// Creates a new billing subscription.
 34    pub async fn create_billing_subscription(
 35        &self,
 36        params: &CreateBillingSubscriptionParams,
 37    ) -> Result<billing_subscription::Model> {
 38        self.transaction(|tx| async move {
 39            let id = billing_subscription::Entity::insert(billing_subscription::ActiveModel {
 40                billing_customer_id: ActiveValue::set(params.billing_customer_id),
 41                kind: ActiveValue::set(params.kind),
 42                stripe_subscription_id: ActiveValue::set(params.stripe_subscription_id.clone()),
 43                stripe_subscription_status: ActiveValue::set(params.stripe_subscription_status),
 44                stripe_cancellation_reason: ActiveValue::set(params.stripe_cancellation_reason),
 45                stripe_current_period_start: ActiveValue::set(params.stripe_current_period_start),
 46                stripe_current_period_end: ActiveValue::set(params.stripe_current_period_end),
 47                ..Default::default()
 48            })
 49            .exec(&*tx)
 50            .await?
 51            .last_insert_id;
 52
 53            Ok(billing_subscription::Entity::find_by_id(id)
 54                .one(&*tx)
 55                .await?
 56                .context("failed to retrieve inserted billing subscription")?)
 57        })
 58        .await
 59    }
 60
 61    /// Updates the specified billing subscription.
 62    pub async fn update_billing_subscription(
 63        &self,
 64        id: BillingSubscriptionId,
 65        params: &UpdateBillingSubscriptionParams,
 66    ) -> Result<()> {
 67        self.transaction(|tx| async move {
 68            billing_subscription::Entity::update(billing_subscription::ActiveModel {
 69                id: ActiveValue::set(id),
 70                billing_customer_id: params.billing_customer_id.clone(),
 71                kind: params.kind.clone(),
 72                stripe_subscription_id: params.stripe_subscription_id.clone(),
 73                stripe_subscription_status: params.stripe_subscription_status.clone(),
 74                stripe_cancel_at: params.stripe_cancel_at.clone(),
 75                stripe_cancellation_reason: params.stripe_cancellation_reason.clone(),
 76                stripe_current_period_start: params.stripe_current_period_start.clone(),
 77                stripe_current_period_end: params.stripe_current_period_end.clone(),
 78                created_at: ActiveValue::not_set(),
 79            })
 80            .exec(&*tx)
 81            .await?;
 82
 83            Ok(())
 84        })
 85        .await
 86    }
 87
 88    /// Returns the billing subscription with the specified Stripe subscription ID.
 89    pub async fn get_billing_subscription_by_stripe_subscription_id(
 90        &self,
 91        stripe_subscription_id: &str,
 92    ) -> Result<Option<billing_subscription::Model>> {
 93        self.transaction(|tx| async move {
 94            Ok(billing_subscription::Entity::find()
 95                .filter(
 96                    billing_subscription::Column::StripeSubscriptionId.eq(stripe_subscription_id),
 97                )
 98                .one(&*tx)
 99                .await?)
100        })
101        .await
102    }
103
104    pub async fn get_active_billing_subscription(
105        &self,
106        user_id: UserId,
107    ) -> Result<Option<billing_subscription::Model>> {
108        self.transaction(|tx| async move {
109            Ok(billing_subscription::Entity::find()
110                .inner_join(billing_customer::Entity)
111                .filter(billing_customer::Column::UserId.eq(user_id))
112                .filter(
113                    Condition::all()
114                        .add(
115                            Condition::any()
116                                .add(
117                                    billing_subscription::Column::StripeSubscriptionStatus
118                                        .eq(StripeSubscriptionStatus::Active),
119                                )
120                                .add(
121                                    billing_subscription::Column::StripeSubscriptionStatus
122                                        .eq(StripeSubscriptionStatus::Trialing),
123                                ),
124                        )
125                        .add(billing_subscription::Column::Kind.is_not_null()),
126                )
127                .one(&*tx)
128                .await?)
129        })
130        .await
131    }
132
133    /// Returns whether the user has an active billing subscription.
134    pub async fn has_active_billing_subscription(&self, user_id: UserId) -> Result<bool> {
135        Ok(self.count_active_billing_subscriptions(user_id).await? > 0)
136    }
137
138    /// Returns the count of the active billing subscriptions for the user with the specified ID.
139    pub async fn count_active_billing_subscriptions(&self, user_id: UserId) -> Result<usize> {
140        self.transaction(|tx| async move {
141            let count = billing_subscription::Entity::find()
142                .inner_join(billing_customer::Entity)
143                .filter(
144                    billing_customer::Column::UserId.eq(user_id).and(
145                        billing_subscription::Column::StripeSubscriptionStatus
146                            .eq(StripeSubscriptionStatus::Active)
147                            .or(billing_subscription::Column::StripeSubscriptionStatus
148                                .eq(StripeSubscriptionStatus::Trialing)),
149                    ),
150                )
151                .count(&*tx)
152                .await?;
153
154            Ok(count as usize)
155        })
156        .await
157    }
158}