summaryrefslogtreecommitdiffhomepage
path: root/cloud/core/src/billing.ts
diff options
context:
space:
mode:
authorFrank <[email protected]>2025-08-08 13:22:54 -0400
committerFrank <[email protected]>2025-08-08 13:24:32 -0400
commit183e0911b76025a1f2a82e979d9834fec2131d0e (patch)
tree9987c1753bd64d1ce1d174ab397f1a8c681f642c /cloud/core/src/billing.ts
parentc7bb19ad0712469063eab35589aa5d3602b0c5b1 (diff)
downloadopencode-183e0911b76025a1f2a82e979d9834fec2131d0e.tar.gz
opencode-183e0911b76025a1f2a82e979d9834fec2131d0e.zip
wip: gateway
Diffstat (limited to 'cloud/core/src/billing.ts')
-rw-r--r--cloud/core/src/billing.ts71
1 files changed, 71 insertions, 0 deletions
diff --git a/cloud/core/src/billing.ts b/cloud/core/src/billing.ts
new file mode 100644
index 000000000..1a7bb2946
--- /dev/null
+++ b/cloud/core/src/billing.ts
@@ -0,0 +1,71 @@
+import { Resource } from "sst"
+import { Stripe } from "stripe"
+import { Database, eq, sql } from "./drizzle"
+import { BillingTable, UsageTable } from "./schema/billing.sql"
+import { Actor } from "./actor"
+import { fn } from "./util/fn"
+import { z } from "zod"
+import { Identifier } from "./identifier"
+import { centsToMicroCents } from "./util/price"
+
+export namespace Billing {
+ export const stripe = () =>
+ new Stripe(Resource.STRIPE_SECRET_KEY.value, {
+ apiVersion: "2025-03-31.basil",
+ })
+
+ export const get = async () => {
+ return Database.use(async (tx) =>
+ tx
+ .select({
+ customerID: BillingTable.customerID,
+ paymentMethodID: BillingTable.paymentMethodID,
+ balance: BillingTable.balance,
+ reload: BillingTable.reload,
+ })
+ .from(BillingTable)
+ .where(eq(BillingTable.workspaceID, Actor.workspace()))
+ .then((r) => r[0]),
+ )
+ }
+
+ export const consume = fn(
+ z.object({
+ requestID: z.string().optional(),
+ model: z.string(),
+ inputTokens: z.number(),
+ outputTokens: z.number(),
+ reasoningTokens: z.number().optional(),
+ cacheReadTokens: z.number().optional(),
+ cacheWriteTokens: z.number().optional(),
+ costInCents: z.number(),
+ }),
+ async (input) => {
+ const workspaceID = Actor.workspace()
+ const cost = centsToMicroCents(input.costInCents)
+
+ return await Database.transaction(async (tx) => {
+ await tx.insert(UsageTable).values({
+ workspaceID,
+ id: Identifier.create("usage"),
+ requestID: input.requestID,
+ model: input.model,
+ inputTokens: input.inputTokens,
+ outputTokens: input.outputTokens,
+ reasoningTokens: input.reasoningTokens,
+ cacheReadTokens: input.cacheReadTokens,
+ cacheWriteTokens: input.cacheWriteTokens,
+ cost,
+ })
+ const [updated] = await tx
+ .update(BillingTable)
+ .set({
+ balance: sql`${BillingTable.balance} - ${cost}`,
+ })
+ .where(eq(BillingTable.workspaceID, workspaceID))
+ .returning()
+ return updated.balance
+ })
+ },
+ )
+}