| import { Billing } from "../src/billing.js" |
| import { and, Database, eq, isNull } from "../src/drizzle/index.js" |
| import { UserTable } from "../src/schema/user.sql.js" |
| import { BillingTable, SubscriptionTable } from "../src/schema/billing.sql.js" |
| import { Identifier } from "../src/identifier.js" |
| import { AuthTable } from "../src/schema/auth.sql.js" |
| import { BlackData } from "../src/black.js" |
|
|
| const plan = "200" |
| const couponID = "JAIr0Pe1" |
| const workspaceID = process.argv[2] |
| const seats = parseInt(process.argv[3]) |
|
|
| console.log(`Gifting ${seats} seats of Black to workspace ${workspaceID}`) |
|
|
| if (!workspaceID || !seats) throw new Error("Usage: bun foo.ts <workspaceID> <seats>") |
|
|
| |
| const users = await Database.use((tx) => |
| tx |
| .select({ |
| id: UserTable.id, |
| role: UserTable.role, |
| email: AuthTable.subject, |
| }) |
| .from(UserTable) |
| .leftJoin(AuthTable, and(eq(AuthTable.accountID, UserTable.accountID), eq(AuthTable.provider, "email"))) |
| .where(and(eq(UserTable.workspaceID, workspaceID), isNull(UserTable.timeDeleted))), |
| ) |
| if (users.length === 0) throw new Error(`Error: No users found in workspace ${workspaceID}`) |
| if (users.length !== seats) |
| throw new Error(`Error: Workspace ${workspaceID} has ${users.length} users, expected ${seats}`) |
| const adminUser = users.find((user) => user.role === "admin") |
| if (!adminUser) throw new Error(`Error: No admin user found in workspace ${workspaceID}`) |
| if (!adminUser.email) throw new Error(`Error: Admin user ${adminUser.id} has no email`) |
|
|
| |
| const billing = await Database.use((tx) => |
| tx |
| .select({ |
| customerID: BillingTable.customerID, |
| subscriptionID: BillingTable.subscriptionID, |
| }) |
| .from(BillingTable) |
| .where(eq(BillingTable.workspaceID, workspaceID)) |
| .then((rows) => rows[0]), |
| ) |
| if (!billing) throw new Error(`Error: Workspace ${workspaceID} has no billing record`) |
| if (billing.subscriptionID) throw new Error(`Error: Workspace ${workspaceID} already has a subscription`) |
|
|
| |
| const customerID = |
| billing.customerID ?? |
| (await (() => |
| Billing.stripe() |
| .customers.create({ |
| email: adminUser.email, |
| metadata: { |
| workspaceID, |
| }, |
| }) |
| .then((customer) => customer.id))()) |
| console.log(`Customer ID: ${customerID}`) |
|
|
| const subscription = await Billing.stripe().subscriptions.create({ |
| customer: customerID!, |
| items: [ |
| { |
| price: BlackData.planToPriceID({ plan }), |
| discounts: [{ coupon: couponID }], |
| quantity: seats, |
| }, |
| ], |
| metadata: { |
| workspaceID, |
| }, |
| }) |
| console.log(`Subscription ID: ${subscription.id}`) |
|
|
| await Database.transaction(async (tx) => { |
| |
| await tx |
| .update(BillingTable) |
| .set({ |
| customerID, |
| subscriptionID: subscription.id, |
| subscription: { status: "subscribed", coupon: couponID, seats, plan }, |
| }) |
| .where(eq(BillingTable.workspaceID, workspaceID)) |
|
|
| |
| for (const user of users) { |
| await tx.insert(SubscriptionTable).values({ |
| workspaceID, |
| id: Identifier.create("subscription"), |
| userID: user.id, |
| }) |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| }) |
|
|
| console.log(`done`) |
|
|