From 11186d8d297570270c4c0be17e2315041f8f3836 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Fri, 20 Oct 2023 18:26:42 +0200 Subject: [PATCH] :construction_worker: Add convenient script for migrating Stripe prices --- packages/scripts/createChatsPrices.ts | 12 +- packages/scripts/getUsage.ts | 29 +++++ .../scripts/migrateSubscriptionItemPriceId.ts | 109 ++++++++++++++++++ packages/scripts/package.json | 3 +- 4 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 packages/scripts/getUsage.ts create mode 100644 packages/scripts/migrateSubscriptionItemPriceId.ts diff --git a/packages/scripts/createChatsPrices.ts b/packages/scripts/createChatsPrices.ts index a7e22306f27..7efc01f2a05 100644 --- a/packages/scripts/createChatsPrices.ts +++ b/packages/scripts/createChatsPrices.ts @@ -17,7 +17,11 @@ const createChatsPrices = async () => { await stripe.prices.create({ currency: 'usd', billing_scheme: 'tiered', - recurring: { interval: 'month', usage_type: 'metered' }, + recurring: { + interval: 'month', + usage_type: 'metered', + aggregate_usage: 'last_during_period', + }, tiers: starterChatTiers, tiers_mode: 'volume', tax_behavior: 'exclusive', @@ -33,7 +37,11 @@ const createChatsPrices = async () => { await stripe.prices.create({ currency: 'usd', billing_scheme: 'tiered', - recurring: { interval: 'month', usage_type: 'metered' }, + recurring: { + interval: 'month', + usage_type: 'metered', + aggregate_usage: 'last_during_period', + }, tiers: proChatTiers, tiers_mode: 'volume', tax_behavior: 'exclusive', diff --git a/packages/scripts/getUsage.ts b/packages/scripts/getUsage.ts new file mode 100644 index 00000000000..00ae98b6e9d --- /dev/null +++ b/packages/scripts/getUsage.ts @@ -0,0 +1,29 @@ +import { PrismaClient } from '@typebot.io/prisma' +import { promptAndSetEnvironment } from './utils' + +const getUsage = async () => { + await promptAndSetEnvironment() + const prisma = new PrismaClient({ + log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'], + }) + + prisma.$on('query', (e) => { + console.log(e.query) + console.log(e.params) + console.log(e.duration, 'ms') + }) + + const count = await prisma.result.count({ + where: { + typebot: { workspaceId: '' }, + hasStarted: true, + createdAt: { + gte: '2023-09-18T00:00:00.000Z', + }, + }, + }) + + console.log(count) +} + +getUsage() diff --git a/packages/scripts/migrateSubscriptionItemPriceId.ts b/packages/scripts/migrateSubscriptionItemPriceId.ts new file mode 100644 index 00000000000..07f25633f6a --- /dev/null +++ b/packages/scripts/migrateSubscriptionItemPriceId.ts @@ -0,0 +1,109 @@ +import { PrismaClient } from '@typebot.io/prisma' +import { promptAndSetEnvironment } from './utils' +import { writeFileSync } from 'fs' +import Stripe from 'stripe' + +const migrateSubscriptionItemPriceId = async () => { + await promptAndSetEnvironment() + const prisma = new PrismaClient({ + log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'], + }) + + prisma.$on('query', (e) => { + console.log(e.query) + console.log(e.params) + console.log(e.duration, 'ms') + }) + + if ( + !process.env.STRIPE_STARTER_CHATS_PRICE_ID_OLD || + !process.env.STRIPE_STARTER_CHATS_PRICE_ID || + !process.env.STRIPE_PRO_CHATS_PRICE_ID_OLD || + !process.env.STRIPE_PRO_CHATS_PRICE_ID || + !process.env.STRIPE_SECRET_KEY + ) + throw new Error('Missing some env variables') + + const workspacesWithPaidPlan = await prisma.workspace.findMany({ + where: { + plan: { + in: ['PRO', 'STARTER'], + }, + isSuspended: false, + }, + select: { + plan: true, + name: true, + id: true, + stripeId: true, + isQuarantined: true, + members: { + select: { + user: { + select: { email: true }, + }, + }, + }, + }, + }) + + writeFileSync( + './workspacesWithPaidPlan.json', + JSON.stringify(workspacesWithPaidPlan, null, 2) + ) + + const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { + apiVersion: '2022-11-15', + }) + + let i = 0 + for (const workspace of workspacesWithPaidPlan) { + i += 1 + console.log( + `(${i} / ${workspacesWithPaidPlan.length})`, + 'Migrating workspace:', + workspace.id, + workspace.name, + workspace.stripeId, + JSON.stringify(workspace.members.map((member) => member.user.email)) + ) + if (!workspace.stripeId) { + console.log('No stripe ID, skipping...') + continue + } + + const subscriptions = await stripe.subscriptions.list({ + customer: workspace.stripeId, + }) + + const currentSubscription = subscriptions.data + .filter((sub) => ['past_due', 'active'].includes(sub.status)) + .sort((a, b) => a.created - b.created) + .shift() + + if (!currentSubscription) { + console.log('No current subscription in workspace:', workspace.id) + continue + } + + const subscriptionItem = currentSubscription.items.data.find( + (item) => + item.price.id === process.env.STRIPE_STARTER_CHATS_PRICE_ID_OLD || + item.price.id === process.env.STRIPE_PRO_CHATS_PRICE_ID_OLD + ) + + if (!subscriptionItem) { + console.log('Could not find subscriptio item. Skipping...') + continue + } + + await stripe.subscriptionItems.update(subscriptionItem.id, { + price: + workspace.plan === 'STARTER' + ? process.env.STRIPE_STARTER_CHATS_PRICE_ID + : process.env.STRIPE_PRO_CHATS_PRICE_ID, + }) + } +} + +migrateSubscriptionItemPriceId() diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 04f2c203f03..abfe8d94004 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -17,7 +17,8 @@ "checkSubscriptionsStatus": "tsx checkSubscriptionsStatus.ts", "createChatsPrices": "tsx createChatsPrices.ts", "migrateSubscriptionsToUsageBased": "tsx migrateSubscriptionsToUsageBased.ts", - "importContactToBrevo": "tsx importContactToBrevo.ts" + "importContactToBrevo": "tsx importContactToBrevo.ts", + "getUsage": "tsx getUsage.ts" }, "devDependencies": { "@typebot.io/emails": "workspace:*",