1use std::sync::Arc;
2use stripe::SubscriptionStatus;
3
4use crate::AppState;
5use crate::db::billing_subscription::StripeSubscriptionStatus;
6use crate::db::{CreateBillingCustomerParams, billing_customer};
7use crate::stripe_client::{StripeClient, StripeCustomerId};
8
9impl From<SubscriptionStatus> for StripeSubscriptionStatus {
10 fn from(value: SubscriptionStatus) -> Self {
11 match value {
12 SubscriptionStatus::Incomplete => Self::Incomplete,
13 SubscriptionStatus::IncompleteExpired => Self::IncompleteExpired,
14 SubscriptionStatus::Trialing => Self::Trialing,
15 SubscriptionStatus::Active => Self::Active,
16 SubscriptionStatus::PastDue => Self::PastDue,
17 SubscriptionStatus::Canceled => Self::Canceled,
18 SubscriptionStatus::Unpaid => Self::Unpaid,
19 SubscriptionStatus::Paused => Self::Paused,
20 }
21 }
22}
23
24/// Finds or creates a billing customer using the provided customer.
25pub async fn find_or_create_billing_customer(
26 app: &Arc<AppState>,
27 stripe_client: &dyn StripeClient,
28 customer_id: &StripeCustomerId,
29) -> anyhow::Result<Option<billing_customer::Model>> {
30 // If we already have a billing customer record associated with the Stripe customer,
31 // there's nothing more we need to do.
32 if let Some(billing_customer) = app
33 .db
34 .get_billing_customer_by_stripe_customer_id(customer_id.0.as_ref())
35 .await?
36 {
37 return Ok(Some(billing_customer));
38 }
39
40 let customer = stripe_client.get_customer(customer_id).await?;
41
42 let Some(email) = customer.email else {
43 return Ok(None);
44 };
45
46 let Some(user) = app.db.get_user_by_email(&email).await? else {
47 return Ok(None);
48 };
49
50 let billing_customer = app
51 .db
52 .create_billing_customer(&CreateBillingCustomerParams {
53 user_id: user.id,
54 stripe_customer_id: customer.id.to_string(),
55 })
56 .await?;
57
58 Ok(Some(billing_customer))
59}