collab: Lay groundwork for reconciling with Stripe using the events API (#15459)

This PR lays the initial groundwork for using the Stripe events API to
reconcile the data in our system with what's in Stripe.

We're using the events API over webhooks so that we don't need to stand
up the associated infrastructure needed to handle webhooks effectively
(namely an asynchronous job queue).

Since we haven't configured the Stripe API keys yet, we won't actually
spawn the reconciliation background task yet, so this is currently a
no-op.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-07-29 23:50:07 -04:00 committed by GitHub
parent 28c14cdee4
commit d93891ba63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 257 additions and 4 deletions

View file

@ -39,4 +39,18 @@ impl Database {
})
.await
}
/// Returns the billing customer for the user with the specified Stripe customer ID.
pub async fn get_billing_customer_by_stripe_customer_id(
&self,
stripe_customer_id: &str,
) -> Result<Option<billing_customer::Model>> {
self.transaction(|tx| async move {
Ok(billing_customer::Entity::find()
.filter(billing_customer::Column::StripeCustomerId.eq(stripe_customer_id))
.one(&*tx)
.await?)
})
.await
}
}

View file

@ -30,6 +30,31 @@ impl Database {
.await
}
/// Upserts the billing subscription by its Stripe subscription ID.
pub async fn upsert_billing_subscription_by_stripe_subscription_id(
&self,
params: &CreateBillingSubscriptionParams,
) -> Result<()> {
self.transaction(|tx| async move {
billing_subscription::Entity::insert(billing_subscription::ActiveModel {
billing_customer_id: ActiveValue::set(params.billing_customer_id),
stripe_subscription_id: ActiveValue::set(params.stripe_subscription_id.clone()),
stripe_subscription_status: ActiveValue::set(params.stripe_subscription_status),
..Default::default()
})
.on_conflict(
OnConflict::columns([billing_subscription::Column::StripeSubscriptionId])
.update_columns([billing_subscription::Column::StripeSubscriptionStatus])
.to_owned(),
)
.exec_with_returning(&*tx)
.await?;
Ok(())
})
.await
}
/// Returns the billing subscription with the specified ID.
pub async fn get_billing_subscription_by_id(
&self,

View file

@ -61,6 +61,17 @@ impl Database {
.await
}
/// Returns a user by email address. There are no access checks here, so this should only be used internally.
pub async fn get_user_by_email(&self, email: &str) -> Result<Option<User>> {
self.transaction(|tx| async move {
Ok(user::Entity::find()
.filter(user::Column::EmailAddress.eq(email))
.one(&*tx)
.await?)
})
.await
}
/// Returns a user by GitHub user ID. There are no access checks here, so this should only be used internally.
pub async fn get_user_by_github_user_id(&self, github_user_id: i32) -> Result<Option<User>> {
self.transaction(|tx| async move {