From 8ea3a4f4b618b8427f4feda17cdcb0e69bd3b286 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Sun, 18 May 2025 03:11:45 +0700 Subject: [PATCH] feat: WIP stripe billing refactor update for 2025 --- apps/api/src/db/schema/consumer.ts | 8 +++++--- apps/api/src/db/schema/types.ts | 2 +- apps/api/src/lib/billing/upsert-consumer.ts | 3 ++- .../src/lib/billing/upsert-stripe-subscription.ts | 14 +++++++------- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/api/src/db/schema/consumer.ts b/apps/api/src/db/schema/consumer.ts index f89fd7e7..47e5d589 100644 --- a/apps/api/src/db/schema/consumer.ts +++ b/apps/api/src/db/schema/consumer.ts @@ -87,10 +87,10 @@ export const consumers = pgTable( stripeStatus: text(), // Main Stripe Subscription id - stripeSubscriptionId: stripeId(), + _stripeSubscriptionId: stripeId(), // [lineItemSlug: string]: string - stripeSubscriptionLineItemIdMap: jsonb() + _stripeSubscriptionLineItemIdMap: jsonb() .$type>() .default({}) .notNull(), @@ -132,7 +132,7 @@ export const consumerRelationsSchema: z.ZodType = z.enum(['user', 'project', 'deployment']) export const consumerSelectSchema = createSelectSchema(consumers, { - stripeSubscriptionLineItemIdMap: z.record(z.string(), z.string()), + _stripeSubscriptionLineItemIdMap: z.record(z.string(), z.string()), deploymentId: (schema) => schema.refine((id) => validators.deploymentId(id), { @@ -145,6 +145,8 @@ export const consumerSelectSchema = createSelectSchema(consumers, { }) }) .omit({ + _stripeSubscriptionId: true, + _stripeSubscriptionLineItemIdMap: true, _stripeCustomerId: true }) .extend({ diff --git a/apps/api/src/db/schema/types.ts b/apps/api/src/db/schema/types.ts index 23e2a280..6e6939be 100644 --- a/apps/api/src/db/schema/types.ts +++ b/apps/api/src/db/schema/types.ts @@ -341,7 +341,7 @@ export const pricingPlanMapSchema = z export type PricingPlanMap = z.infer // TODO -// export const stripeSubscriptionLineItemIdMapSchema = z +// export const _stripeSubscriptionLineItemIdMapSchema = z // .record(pricingPlanLineItemHashSchema, z.string().describe('Stripe LineItem id')) // .describe('Map from internal PricingPlanLineItem **hash** to Stripe LineItem id') // .openapi('StripeSubscriptionLineItemMap') diff --git a/apps/api/src/lib/billing/upsert-consumer.ts b/apps/api/src/lib/billing/upsert-consumer.ts index 63e5755e..611cb077 100644 --- a/apps/api/src/lib/billing/upsert-consumer.ts +++ b/apps/api/src/lib/billing/upsert-consumer.ts @@ -33,6 +33,7 @@ export async function upsertConsumer( if (!consumerId) { assert(projectId, 400, 'Missing required "deploymentId"') + assert(plan, 400, 'Missing required "plan"') } const [{ user, stripeCustomer }, existingConsumer] = await Promise.all([ @@ -113,8 +114,8 @@ export async function upsertConsumer( ;[consumer] = await db.insert(schema.consumers).values({ plan, userId, - deploymentId, projectId, + deploymentId, // TODO: refactor / improve token generation token: sha256().slice(0, 24), _stripeCustomerId: stripeCustomer.id diff --git a/apps/api/src/lib/billing/upsert-stripe-subscription.ts b/apps/api/src/lib/billing/upsert-stripe-subscription.ts index 15268d9b..9ca56e55 100644 --- a/apps/api/src/lib/billing/upsert-stripe-subscription.ts +++ b/apps/api/src/lib/billing/upsert-stripe-subscription.ts @@ -53,17 +53,17 @@ export async function upsertStripeSubscription( ? deployment.pricingPlans.find((pricingPlan) => pricingPlan.slug === plan) : undefined - const action: 'create' | 'update' | 'cancel' = consumer.stripeSubscriptionId + const action: 'create' | 'update' | 'cancel' = consumer._stripeSubscriptionId ? plan ? 'update' : 'cancel' : 'create' let subscription: Stripe.Subscription | undefined - if (consumer.stripeSubscriptionId) { + if (consumer._stripeSubscriptionId) { // customer has an existing subscription const existing = await stripe.subscriptions.retrieve( - consumer.stripeSubscriptionId, + consumer._stripeSubscriptionId, ...stripeConnectParams ) const existingItems = existing.items.data @@ -155,7 +155,7 @@ export async function upsertStripeSubscription( assert( items.length || !plan, 500, - `Error updating stripe subscription "${consumer.stripeSubscriptionId}"` + `Error updating stripe subscription "${consumer._stripeSubscriptionId}"` ) for (const item of items) { @@ -182,7 +182,7 @@ export async function upsertStripeSubscription( } subscription = await stripe.subscriptions.update( - consumer.stripeSubscriptionId, + consumer._stripeSubscriptionId, update, ...stripeConnectParams ) @@ -246,7 +246,7 @@ export async function upsertStripeSubscription( ...stripeConnectParams ) - consumer.stripeSubscriptionId = subscription.id + consumer._stripeSubscriptionId = subscription.id } assert(subscription, 500, 'Missing stripe subscription') @@ -258,7 +258,7 @@ export async function upsertStripeSubscription( consumerUpdate.stripeStatus = subscription.status } else { // TODO - consumerUpdate.stripeSubscriptionId = null + consumerUpdate._stripeSubscriptionId = null consumerUpdate.stripeStatus = 'cancelled' }