zen: black admin
This commit is contained in:
163
packages/console/core/script/black-transfer.ts
Normal file
163
packages/console/core/script/black-transfer.ts
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { Billing } from "../src/billing.js"
|
||||||
|
import { and, Database, desc, eq, isNotNull, lt, sql } from "../src/drizzle/index.js"
|
||||||
|
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
||||||
|
|
||||||
|
const fromWrkID = process.argv[2]
|
||||||
|
const toWrkID = process.argv[3]
|
||||||
|
|
||||||
|
if (!fromWrkID || !toWrkID) {
|
||||||
|
console.error("Usage: bun foo.ts <fromWrkID> <toWrkID>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Transferring subscription from ${fromWrkID} to ${toWrkID}`)
|
||||||
|
|
||||||
|
// Look up the FROM workspace billing
|
||||||
|
const fromBilling = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
subscriptionCouponID: BillingTable.subscriptionCouponID,
|
||||||
|
paymentMethodID: BillingTable.paymentMethodID,
|
||||||
|
paymentMethodType: BillingTable.paymentMethodType,
|
||||||
|
paymentMethodLast4: BillingTable.paymentMethodLast4,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, fromWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromBilling) throw new Error(`Error: FROM workspace has no billing record`)
|
||||||
|
if (!fromBilling.customerID) throw new Error(`Error: FROM workspace has no Stripe customer ID`)
|
||||||
|
if (!fromBilling.subscriptionID) throw new Error(`Error: FROM workspace has no subscription`)
|
||||||
|
|
||||||
|
const fromSubscription = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({ userID: SubscriptionTable.userID })
|
||||||
|
.from(SubscriptionTable)
|
||||||
|
.where(eq(SubscriptionTable.workspaceID, fromWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromSubscription) throw new Error(`Error: FROM workspace has no subscription`)
|
||||||
|
|
||||||
|
// Look up the previous customer ID in FROM workspace
|
||||||
|
const subscriptionPayment = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: PaymentTable.customerID,
|
||||||
|
timeCreated: PaymentTable.timeCreated,
|
||||||
|
})
|
||||||
|
.from(PaymentTable)
|
||||||
|
.where(and(eq(PaymentTable.workspaceID, fromWrkID), sql`JSON_EXTRACT(enrichment, '$.type') = 'subscription'`))
|
||||||
|
.then((rows) => {
|
||||||
|
if (rows.length > 1) {
|
||||||
|
console.error(`Error: Multiple subscription payments found for workspace ${fromWrkID}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
return rows[0]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const fromPrevPayment = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({ customerID: PaymentTable.customerID })
|
||||||
|
.from(PaymentTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(PaymentTable.workspaceID, fromWrkID),
|
||||||
|
isNotNull(PaymentTable.customerID),
|
||||||
|
lt(PaymentTable.timeCreated, subscriptionPayment.timeCreated),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.orderBy(desc(PaymentTable.timeCreated))
|
||||||
|
.limit(1)
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromPrevPayment?.customerID) throw new Error(`Error: FROM workspace has no previous Stripe customer to revert to`)
|
||||||
|
if (fromPrevPayment.customerID === fromBilling.customerID)
|
||||||
|
throw new Error(`Error: FROM workspace has the same Stripe customer ID as the current one`)
|
||||||
|
|
||||||
|
const fromPrevPaymentMethods = await Billing.stripe().customers.listPaymentMethods(fromPrevPayment.customerID, {})
|
||||||
|
if (fromPrevPaymentMethods.data.length === 0)
|
||||||
|
throw new Error(`Error: FROM workspace has no previous Stripe payment methods`)
|
||||||
|
|
||||||
|
// Look up the TO workspace billing
|
||||||
|
const toBilling = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, toWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!toBilling) throw new Error(`Error: TO workspace has no billing record`)
|
||||||
|
if (toBilling.subscriptionID) throw new Error(`Error: TO workspace already has a subscription`)
|
||||||
|
|
||||||
|
console.log(`FROM:`)
|
||||||
|
console.log(` Old Customer ID: ${fromBilling.customerID}`)
|
||||||
|
console.log(` New Customer ID: ${fromPrevPayment.customerID}`)
|
||||||
|
console.log(`TO:`)
|
||||||
|
console.log(` Old Customer ID: ${toBilling.customerID}`)
|
||||||
|
console.log(` New Customer ID: ${fromBilling.customerID}`)
|
||||||
|
|
||||||
|
// Clear workspaceID from Stripe customer metadata
|
||||||
|
await Billing.stripe().customers.update(fromPrevPayment.customerID, {
|
||||||
|
metadata: {
|
||||||
|
workspaceID: fromWrkID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await Billing.stripe().customers.update(fromBilling.customerID, {
|
||||||
|
metadata: {
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await Database.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID: fromPrevPayment.customerID,
|
||||||
|
subscriptionID: null,
|
||||||
|
subscriptionCouponID: null,
|
||||||
|
paymentMethodID: fromPrevPaymentMethods.data[0].id,
|
||||||
|
paymentMethodLast4: fromPrevPaymentMethods.data[0].card?.last4 ?? null,
|
||||||
|
paymentMethodType: fromPrevPaymentMethods.data[0].type,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, fromWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID: fromBilling.customerID,
|
||||||
|
subscriptionID: fromBilling.subscriptionID,
|
||||||
|
subscriptionCouponID: fromBilling.subscriptionCouponID,
|
||||||
|
paymentMethodID: fromBilling.paymentMethodID,
|
||||||
|
paymentMethodLast4: fromBilling.paymentMethodLast4,
|
||||||
|
paymentMethodType: fromBilling.paymentMethodType,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, toWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(SubscriptionTable)
|
||||||
|
.set({
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
userID: fromSubscription.userID,
|
||||||
|
})
|
||||||
|
.where(eq(SubscriptionTable.workspaceID, fromWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(PaymentTable)
|
||||||
|
.set({
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(PaymentTable.workspaceID, fromWrkID),
|
||||||
|
sql`JSON_EXTRACT(enrichment, '$.type') = 'subscription'`,
|
||||||
|
eq(PaymentTable.amount, 20000000000),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`done`)
|
||||||
@@ -143,6 +143,7 @@ async function printWorkspace(workspaceID: string) {
|
|||||||
amount: PaymentTable.amount,
|
amount: PaymentTable.amount,
|
||||||
paymentID: PaymentTable.paymentID,
|
paymentID: PaymentTable.paymentID,
|
||||||
invoiceID: PaymentTable.invoiceID,
|
invoiceID: PaymentTable.invoiceID,
|
||||||
|
customerID: PaymentTable.customerID,
|
||||||
timeCreated: PaymentTable.timeCreated,
|
timeCreated: PaymentTable.timeCreated,
|
||||||
timeRefunded: PaymentTable.timeRefunded,
|
timeRefunded: PaymentTable.timeRefunded,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import { Billing } from "../src/billing.js"
|
|
||||||
import { and, Database, eq } from "../src/drizzle/index.js"
|
|
||||||
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
|
||||||
|
|
||||||
const workspaceID = process.argv[2]
|
|
||||||
|
|
||||||
if (!workspaceID) {
|
|
||||||
console.error("Usage: bun remove-black.ts <workspaceID>")
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Removing subscription from workspace ${workspaceID}`)
|
|
||||||
|
|
||||||
// Look up the workspace billing
|
|
||||||
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) {
|
|
||||||
console.error(`Error: No billing record found for workspace ${workspaceID}`)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!billing.subscriptionID) {
|
|
||||||
console.error(`Error: Workspace ${workspaceID} does not have a subscription`)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(` Customer ID: ${billing.customerID}`)
|
|
||||||
console.log(` Subscription ID: ${billing.subscriptionID}`)
|
|
||||||
|
|
||||||
// Clear workspaceID from Stripe customer metadata
|
|
||||||
if (billing.customerID) {
|
|
||||||
//await Billing.stripe().customers.update(billing.customerID, {
|
|
||||||
// metadata: {
|
|
||||||
// workspaceID: "",
|
|
||||||
// },
|
|
||||||
//})
|
|
||||||
//console.log(`Cleared workspaceID from Stripe customer metadata`)
|
|
||||||
}
|
|
||||||
|
|
||||||
await Database.transaction(async (tx) => {
|
|
||||||
// Clear subscription-related fields from billing table
|
|
||||||
await tx
|
|
||||||
.update(BillingTable)
|
|
||||||
.set({
|
|
||||||
// customerID: null,
|
|
||||||
subscriptionID: null,
|
|
||||||
subscriptionCouponID: null,
|
|
||||||
// paymentMethodID: null,
|
|
||||||
// paymentMethodLast4: null,
|
|
||||||
// paymentMethodType: null,
|
|
||||||
})
|
|
||||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
|
||||||
|
|
||||||
// Delete from subscription table
|
|
||||||
await tx.delete(SubscriptionTable).where(eq(SubscriptionTable.workspaceID, workspaceID))
|
|
||||||
|
|
||||||
// Delete from payments table
|
|
||||||
await tx
|
|
||||||
.delete(PaymentTable)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(PaymentTable.workspaceID, workspaceID),
|
|
||||||
eq(PaymentTable.enrichment, { type: "subscription" }),
|
|
||||||
eq(PaymentTable.amount, 20000000000),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(`Successfully removed subscription from workspace ${workspaceID}`)
|
|
||||||
Reference in New Issue
Block a user