diff --git a/packages/console/app/src/routes/stripe/webhook.ts b/packages/console/app/src/routes/stripe/webhook.ts index fc7b18fbc..34f83b446 100644 --- a/packages/console/app/src/routes/stripe/webhook.ts +++ b/packages/console/app/src/routes/stripe/webhook.ts @@ -421,7 +421,10 @@ export async function POST(input: APIEvent) { }) } if (body.type === "invoice.payment_succeeded") { - if (body.data.object.billing_reason === "subscription_cycle" || body.data.object.billing_reason === "subscription_create") { + if ( + body.data.object.billing_reason === "subscription_cycle" || + body.data.object.billing_reason === "subscription_create" + ) { const invoiceID = body.data.object.id as string const amountInCents = body.data.object.amount_paid const customerID = body.data.object.customer as string diff --git a/packages/console/core/migrations/meta/0055_snapshot.json b/packages/console/core/migrations/meta/0055_snapshot.json index fadb753b4..82293d866 100644 --- a/packages/console/core/migrations/meta/0055_snapshot.json +++ b/packages/console/core/migrations/meta/0055_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -193,9 +184,7 @@ "indexes": { "time_created": { "name": "time_created", - "columns": [ - "time_created" - ], + "columns": ["time_created"], "isUnique": false } }, @@ -203,9 +192,7 @@ "compositePrimaryKeys": { "benchmark_id_pk": { "name": "benchmark_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -388,16 +375,12 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true }, "global_subscription_id": { "name": "global_subscription_id", - "columns": [ - "subscription_id" - ], + "columns": ["subscription_id"], "isUnique": true } }, @@ -405,10 +388,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -502,10 +482,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -590,10 +567,7 @@ "indexes": { "workspace_user_id": { "name": "workspace_user_id", - "columns": [ - "workspace_id", - "user_id" - ], + "columns": ["workspace_id", "user_id"], "isUnique": true } }, @@ -601,10 +575,7 @@ "compositePrimaryKeys": { "subscription_workspace_id_id_pk": { "name": "subscription_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -731,10 +702,7 @@ "indexes": { "usage_time_created": { "name": "usage_time_created", - "columns": [ - "workspace_id", - "time_created" - ], + "columns": ["workspace_id", "time_created"], "isUnique": false } }, @@ -742,10 +710,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -781,10 +746,7 @@ "compositePrimaryKeys": { "ip_rate_limit_ip_interval_pk": { "name": "ip_rate_limit_ip_interval_pk", - "columns": [ - "ip", - "interval" - ] + "columns": ["ip", "interval"] } }, "uniqueConstraints": {}, @@ -836,9 +798,7 @@ "compositePrimaryKeys": { "ip_ip_pk": { "name": "ip_ip_pk", - "columns": [ - "ip" - ] + "columns": ["ip"] } }, "uniqueConstraints": {}, @@ -916,9 +876,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -926,10 +884,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -986,10 +941,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -997,10 +949,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -1064,10 +1013,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -1075,10 +1021,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -1191,32 +1134,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -1224,10 +1157,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -1284,9 +1214,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -1294,9 +1222,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -1313,4 +1239,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/_journal.json b/packages/console/core/migrations/meta/_journal.json index 0314e38c9..f807eab66 100644 --- a/packages/console/core/migrations/meta/_journal.json +++ b/packages/console/core/migrations/meta/_journal.json @@ -395,4 +395,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts index 373f0c595..7031a384b 100644 --- a/packages/console/core/src/billing.ts +++ b/packages/console/core/src/billing.ts @@ -290,65 +290,68 @@ export namespace Billing { }, ) - export const subscribe = fn(z.object({ - seats: z.number(), - coupon: z.string().optional(), - }), async ({ seats, coupon }) => { - const user = Actor.assert("user") - const billing = await Database.use((tx) => - tx - .select({ - customerID: BillingTable.customerID, - paymentMethodID: BillingTable.paymentMethodID, - subscriptionID: BillingTable.subscriptionID, - subscriptionPlan: BillingTable.subscriptionPlan, - timeSubscriptionSelected: BillingTable.timeSubscriptionSelected, - }) - .from(BillingTable) - .where(eq(BillingTable.workspaceID, Actor.workspace())) - .then((rows) => rows[0]), - ) + export const subscribe = fn( + z.object({ + seats: z.number(), + coupon: z.string().optional(), + }), + async ({ seats, coupon }) => { + const user = Actor.assert("user") + const billing = await Database.use((tx) => + tx + .select({ + customerID: BillingTable.customerID, + paymentMethodID: BillingTable.paymentMethodID, + subscriptionID: BillingTable.subscriptionID, + subscriptionPlan: BillingTable.subscriptionPlan, + timeSubscriptionSelected: BillingTable.timeSubscriptionSelected, + }) + .from(BillingTable) + .where(eq(BillingTable.workspaceID, Actor.workspace())) + .then((rows) => rows[0]), + ) - if (!billing) throw new Error("Billing record not found") - if (!billing.timeSubscriptionSelected) throw new Error("Not selected for subscription") - if (billing.subscriptionID) throw new Error("Already subscribed") - if (!billing.customerID) throw new Error("No customer ID") - if (!billing.paymentMethodID) throw new Error("No payment method") - if (!billing.subscriptionPlan) throw new Error("No subscription plan") + if (!billing) throw new Error("Billing record not found") + if (!billing.timeSubscriptionSelected) throw new Error("Not selected for subscription") + if (billing.subscriptionID) throw new Error("Already subscribed") + if (!billing.customerID) throw new Error("No customer ID") + if (!billing.paymentMethodID) throw new Error("No payment method") + if (!billing.subscriptionPlan) throw new Error("No subscription plan") - const subscription = await Billing.stripe().subscriptions.create({ - customer: billing.customerID, - default_payment_method: billing.paymentMethodID, - items: [{ price: BlackData.planToPriceID({ plan: billing.subscriptionPlan }) }], - metadata: { - workspaceID: Actor.workspace(), - }, - }) - - await Database.transaction(async (tx) => { - await tx - .update(BillingTable) - .set({ - subscriptionID: subscription.id, - subscription: { - status: "subscribed", - coupon, - seats, - plan: billing.subscriptionPlan!, - }, - subscriptionPlan: null, - timeSubscriptionBooked: null, - timeSubscriptionSelected: null, - }) - .where(eq(BillingTable.workspaceID, Actor.workspace())) - - await tx.insert(SubscriptionTable).values({ - workspaceID: Actor.workspace(), - id: Identifier.create("subscription"), - userID: user.properties.userID, + const subscription = await Billing.stripe().subscriptions.create({ + customer: billing.customerID, + default_payment_method: billing.paymentMethodID, + items: [{ price: BlackData.planToPriceID({ plan: billing.subscriptionPlan }) }], + metadata: { + workspaceID: Actor.workspace(), + }, }) - }) - return subscription.id - }) + await Database.transaction(async (tx) => { + await tx + .update(BillingTable) + .set({ + subscriptionID: subscription.id, + subscription: { + status: "subscribed", + coupon, + seats, + plan: billing.subscriptionPlan!, + }, + subscriptionPlan: null, + timeSubscriptionBooked: null, + timeSubscriptionSelected: null, + }) + .where(eq(BillingTable.workspaceID, Actor.workspace())) + + await tx.insert(SubscriptionTable).values({ + workspaceID: Actor.workspace(), + id: Identifier.create("subscription"), + userID: user.properties.userID, + }) + }) + + return subscription.id + }, + ) } diff --git a/packages/console/core/src/black.ts b/packages/console/core/src/black.ts index 93134de55..5f8db6273 100644 --- a/packages/console/core/src/black.ts +++ b/packages/console/core/src/black.ts @@ -28,28 +28,37 @@ export namespace BlackData { return input }) - export const getLimits = fn(z.object({ + export const getLimits = fn( + z.object({ plan: z.enum(SubscriptionPlan), - }), ({ plan }) => { - const json = JSON.parse(Resource.ZEN_BLACK_LIMITS.value) - return Schema.parse(json)[plan] - }) + }), + ({ plan }) => { + const json = JSON.parse(Resource.ZEN_BLACK_LIMITS.value) + return Schema.parse(json)[plan] + }, + ) - export const planToPriceID = fn(z.object({ + export const planToPriceID = fn( + z.object({ plan: z.enum(SubscriptionPlan), - }), ({ plan }) => { - if (plan === "200") return Resource.ZEN_BLACK_PRICE.plan200 - if (plan === "100") return Resource.ZEN_BLACK_PRICE.plan100 - return Resource.ZEN_BLACK_PRICE.plan20 - }) + }), + ({ plan }) => { + if (plan === "200") return Resource.ZEN_BLACK_PRICE.plan200 + if (plan === "100") return Resource.ZEN_BLACK_PRICE.plan100 + return Resource.ZEN_BLACK_PRICE.plan20 + }, + ) - export const priceIDToPlan = fn(z.object({ - priceID: z.string(), - }), ({ priceID }) => { - if (priceID === Resource.ZEN_BLACK_PRICE.plan200) return "200" - if (priceID === Resource.ZEN_BLACK_PRICE.plan100) return "100" - return "20" - }) + export const priceIDToPlan = fn( + z.object({ + priceID: z.string(), + }), + ({ priceID }) => { + if (priceID === Resource.ZEN_BLACK_PRICE.plan200) return "200" + if (priceID === Resource.ZEN_BLACK_PRICE.plan100) return "100" + return "20" + }, + ) } export namespace Black {