black-gift.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { Billing } from "../src/billing.js"
  2. import { and, Database, eq, isNull, sql } from "../src/drizzle/index.js"
  3. import { UserTable } from "../src/schema/user.sql.js"
  4. import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
  5. import { Identifier } from "../src/identifier.js"
  6. import { centsToMicroCents } from "../src/util/price.js"
  7. import { AuthTable } from "../src/schema/auth.sql.js"
  8. import { BlackData } from "../src/black.js"
  9. import { Actor } from "../src/actor.js"
  10. const plan = "200"
  11. const couponID = "JAIr0Pe1"
  12. const workspaceID = process.argv[2]
  13. const seats = parseInt(process.argv[3])
  14. console.log(`Gifting ${seats} seats of Black to workspace ${workspaceID}`)
  15. if (!workspaceID || !seats) throw new Error("Usage: bun foo.ts <workspaceID> <seats>")
  16. // Get workspace user
  17. const users = await Database.use((tx) =>
  18. tx
  19. .select({
  20. id: UserTable.id,
  21. role: UserTable.role,
  22. email: AuthTable.subject,
  23. })
  24. .from(UserTable)
  25. .leftJoin(AuthTable, and(eq(AuthTable.accountID, UserTable.accountID), eq(AuthTable.provider, "email")))
  26. .where(and(eq(UserTable.workspaceID, workspaceID), isNull(UserTable.timeDeleted))),
  27. )
  28. if (users.length === 0) throw new Error(`Error: No users found in workspace ${workspaceID}`)
  29. if (users.length !== seats)
  30. throw new Error(`Error: Workspace ${workspaceID} has ${users.length} users, expected ${seats}`)
  31. const adminUser = users.find((user) => user.role === "admin")
  32. if (!adminUser) throw new Error(`Error: No admin user found in workspace ${workspaceID}`)
  33. if (!adminUser.email) throw new Error(`Error: Admin user ${adminUser.id} has no email`)
  34. // Get Billing
  35. const billing = await Database.use((tx) =>
  36. tx
  37. .select({
  38. customerID: BillingTable.customerID,
  39. subscriptionID: BillingTable.subscriptionID,
  40. })
  41. .from(BillingTable)
  42. .where(eq(BillingTable.workspaceID, workspaceID))
  43. .then((rows) => rows[0]),
  44. )
  45. if (!billing) throw new Error(`Error: Workspace ${workspaceID} has no billing record`)
  46. if (billing.subscriptionID) throw new Error(`Error: Workspace ${workspaceID} already has a subscription`)
  47. // Look up the Stripe customer by email
  48. const customerID =
  49. billing.customerID ??
  50. (await (() =>
  51. Billing.stripe()
  52. .customers.create({
  53. email: adminUser.email,
  54. metadata: {
  55. workspaceID,
  56. },
  57. })
  58. .then((customer) => customer.id))())
  59. console.log(`Customer ID: ${customerID}`)
  60. const subscription = await Billing.stripe().subscriptions.create({
  61. customer: customerID!,
  62. items: [
  63. {
  64. price: BlackData.planToPriceID({ plan }),
  65. discounts: [{ coupon: couponID }],
  66. quantity: seats,
  67. },
  68. ],
  69. metadata: {
  70. workspaceID,
  71. },
  72. })
  73. console.log(`Subscription ID: ${subscription.id}`)
  74. await Database.transaction(async (tx) => {
  75. // Set customer id, subscription id, and payment method on workspace billing
  76. await tx
  77. .update(BillingTable)
  78. .set({
  79. customerID,
  80. subscriptionID: subscription.id,
  81. subscription: { status: "subscribed", coupon: couponID, seats, plan },
  82. })
  83. .where(eq(BillingTable.workspaceID, workspaceID))
  84. // Create a row in subscription table
  85. for (const user of users) {
  86. await tx.insert(SubscriptionTable).values({
  87. workspaceID,
  88. id: Identifier.create("subscription"),
  89. userID: user.id,
  90. })
  91. }
  92. //
  93. // // Create a row in payments table
  94. // await tx.insert(PaymentTable).values({
  95. // workspaceID,
  96. // id: Identifier.create("payment"),
  97. // amount: centsToMicroCents(amountInCents),
  98. // customerID,
  99. // invoiceID,
  100. // paymentID,
  101. // enrichment: {
  102. // type: "subscription",
  103. // couponID,
  104. // },
  105. // })
  106. })
  107. console.log(`done`)