collab: Add endpoint for managing a billing subscription (#15455)

This PR adds a new `POST /billing/subscriptions/manage` endpoint that
can be used to manage a billing subscription.

The endpoint accepts a `github_user_id` to identify the user, as well as
an optional `subscription_id` for managing a specific subscription. If
`subscription_id` is not provided, it try and use the active
subscription, if there is only one.

Right now the endpoint only supports cancelling an active subscription.
This is done by passing `"intent": "cancel"` in the request body.

The endpoint will return the URL to a Stripe customer portal session,
which the caller can redirect the user to.

Here's an example of how to call it:

```sh
curl -X POST "http://localhost:8080/billing/subscriptions/manage" \
     -H "Authorization: <ADMIN_TOKEN>" \
     -H "Content-Type: application/json" \
     -d '{"github_user_id": 12345, "intent": "cancel"}'
```

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-07-29 20:05:17 -04:00 committed by GitHub
parent 4d8ad7ae42
commit 66121fa0e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 124 additions and 4 deletions

View file

@ -32,6 +32,19 @@ impl Database {
.await
}
/// Returns the billing subscription with the specified ID.
pub async fn get_billing_subscription_by_id(
&self,
id: BillingSubscriptionId,
) -> Result<Option<billing_subscription::Model>> {
self.transaction(|tx| async move {
Ok(billing_subscription::Entity::find_by_id(id)
.one(&*tx)
.await?)
})
.await
}
/// Returns all of the billing subscriptions for the user with the specified ID.
///
/// Note that this returns the subscriptions regardless of their status.
@ -44,6 +57,7 @@ impl Database {
self.transaction(|tx| async move {
let subscriptions = billing_subscription::Entity::find()
.filter(billing_subscription::Column::UserId.eq(user_id))
.order_by_asc(billing_subscription::Column::Id)
.all(&*tx)
.await?;
@ -65,6 +79,7 @@ impl Database {
.eq(StripeSubscriptionStatus::Active),
),
)
.order_by_asc(billing_subscription::Column::Id)
.all(&*tx)
.await?;