summaryrefslogtreecommitdiffhomepage
path: root/packages/console/core/src/billing.ts
diff options
context:
space:
mode:
authorFrank <[email protected]>2026-02-24 04:45:39 -0500
committerFrank <[email protected]>2026-02-24 04:45:41 -0500
commitfb6d201ee03d73967c554394742be360e2ff782d (patch)
tree25cbda2e9ec598dfa5a5493f67cb432c5287d624 /packages/console/core/src/billing.ts
parentcda2af2589ddef9265ca2db379ecd4ab556f6be8 (diff)
downloadopencode-fb6d201ee03d73967c554394742be360e2ff782d.tar.gz
opencode-fb6d201ee03d73967c554394742be360e2ff782d.zip
wip: zen lite
Diffstat (limited to 'packages/console/core/src/billing.ts')
-rw-r--r--packages/console/core/src/billing.ts82
1 files changed, 79 insertions, 3 deletions
diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts
index 2c1cdb068..fcf238a35 100644
--- a/packages/console/core/src/billing.ts
+++ b/packages/console/core/src/billing.ts
@@ -1,6 +1,6 @@
import { Stripe } from "stripe"
import { Database, eq, sql } from "./drizzle"
-import { BillingTable, PaymentTable, SubscriptionTable, UsageTable } from "./schema/billing.sql"
+import { BillingTable, LiteTable, PaymentTable, SubscriptionTable, UsageTable } from "./schema/billing.sql"
import { Actor } from "./actor"
import { fn } from "./util/fn"
import { z } from "zod"
@@ -9,6 +9,7 @@ import { Identifier } from "./identifier"
import { centsToMicroCents } from "./util/price"
import { User } from "./user"
import { BlackData } from "./black"
+import { LiteData } from "./lite"
export namespace Billing {
export const ITEM_CREDIT_NAME = "opencode credits"
@@ -233,6 +234,56 @@ export namespace Billing {
},
)
+ export const generateLiteCheckoutUrl = fn(
+ z.object({
+ successUrl: z.string(),
+ cancelUrl: z.string(),
+ }),
+ async (input) => {
+ const user = Actor.assert("user")
+ const { successUrl, cancelUrl } = input
+
+ const email = await User.getAuthEmail(user.properties.userID)
+ const billing = await Billing.get()
+
+ if (billing.subscriptionID) throw new Error("Already subscribed to Black")
+ if (billing.liteSubscriptionID) throw new Error("Already subscribed to Lite")
+
+ const session = await Billing.stripe().checkout.sessions.create({
+ mode: "subscription",
+ billing_address_collection: "required",
+ line_items: [{ price: LiteData.priceID(), quantity: 1 }],
+ ...(billing.customerID
+ ? {
+ customer: billing.customerID,
+ customer_update: {
+ name: "auto",
+ address: "auto",
+ },
+ }
+ : {
+ customer_email: email!,
+ }),
+ currency: "usd",
+ payment_method_types: ["card"],
+ tax_id_collection: {
+ enabled: true,
+ },
+ success_url: successUrl,
+ cancel_url: cancelUrl,
+ subscription_data: {
+ metadata: {
+ workspaceID: Actor.workspace(),
+ userID: user.properties.userID,
+ type: "lite",
+ },
+ },
+ })
+
+ return session.url
+ },
+ )
+
export const generateSessionUrl = fn(
z.object({
returnUrl: z.string(),
@@ -271,7 +322,7 @@ export namespace Billing {
},
)
- export const subscribe = fn(
+ export const subscribeBlack = fn(
z.object({
seats: z.number(),
coupon: z.string().optional(),
@@ -336,7 +387,7 @@ export namespace Billing {
},
)
- export const unsubscribe = fn(
+ export const unsubscribeBlack = fn(
z.object({
subscriptionID: z.string(),
}),
@@ -360,4 +411,29 @@ export namespace Billing {
})
},
)
+
+ export const unsubscribeLite = fn(
+ z.object({
+ subscriptionID: z.string(),
+ }),
+ async ({ subscriptionID }) => {
+ const workspaceID = await Database.use((tx) =>
+ tx
+ .select({ workspaceID: BillingTable.workspaceID })
+ .from(BillingTable)
+ .where(eq(BillingTable.liteSubscriptionID, subscriptionID))
+ .then((rows) => rows[0]?.workspaceID),
+ )
+ if (!workspaceID) throw new Error("Workspace ID not found for subscription")
+
+ await Database.transaction(async (tx) => {
+ await tx
+ .update(BillingTable)
+ .set({ liteSubscriptionID: null, lite: null })
+ .where(eq(BillingTable.workspaceID, workspaceID))
+
+ await tx.delete(LiteTable).where(eq(LiteTable.workspaceID, workspaceID))
+ })
+ },
+ )
}