summaryrefslogtreecommitdiffhomepage
path: root/packages/console/core/src
diff options
context:
space:
mode:
authorFrank <[email protected]>2026-04-18 16:26:58 -0400
committerFrank <[email protected]>2026-04-18 17:33:28 -0400
commit9d012b062186ef9900cc2673b77c446d38ebd789 (patch)
tree0185f564ac585be5e50e1ba090da8a461e6fb4fc /packages/console/core/src
parentfbb0a93e12740c7fb3f5f7ff62eee027c157e351 (diff)
downloadopencode-9d012b062186ef9900cc2673b77c446d38ebd789.tar.gz
opencode-9d012b062186ef9900cc2673b77c446d38ebd789.zip
zen: redeem credit
Diffstat (limited to 'packages/console/core/src')
-rw-r--r--packages/console/core/src/billing.ts33
-rw-r--r--packages/console/core/src/schema/billing.sql.ts24
2 files changed, 54 insertions, 3 deletions
diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts
index 9de413e60..d96b19ad7 100644
--- a/packages/console/core/src/billing.ts
+++ b/packages/console/core/src/billing.ts
@@ -1,6 +1,14 @@
import { Stripe } from "stripe"
-import { Database, eq, sql } from "./drizzle"
-import { BillingTable, LiteTable, PaymentTable, SubscriptionTable, UsageTable } from "./schema/billing.sql"
+import { and, Database, eq, sql } from "./drizzle"
+import {
+ BillingTable,
+ CouponTable,
+ CouponType,
+ LiteTable,
+ PaymentTable,
+ SubscriptionTable,
+ UsageTable,
+} from "./schema/billing.sql"
import { Actor } from "./actor"
import { fn } from "./util/fn"
import { z } from "zod"
@@ -147,6 +155,27 @@ export namespace Billing {
return amountInMicroCents
}
+ export const redeemCoupon = async (email: string, type: (typeof CouponType)[number]) => {
+ const coupon = await Database.use((tx) =>
+ tx
+ .select()
+ .from(CouponTable)
+ .where(and(eq(CouponTable.email, email), eq(CouponTable.type, type)))
+ .then((rows) => rows[0]),
+ )
+ if (!coupon) throw new Error("Invalid coupon code")
+ if (coupon.timeRedeemed) throw new Error("Coupon already redeemed")
+
+ if (type === "BUILDATHON") await grantCredit(Actor.workspace(), 500)
+
+ await Database.use((tx) =>
+ tx
+ .update(CouponTable)
+ .set({ timeRedeemed: sql`now()` })
+ .where(and(eq(CouponTable.email, email), eq(CouponTable.type, type))),
+ )
+ }
+
export const setMonthlyLimit = fn(z.number(), async (input) => {
return await Database.use((tx) =>
tx
diff --git a/packages/console/core/src/schema/billing.sql.ts b/packages/console/core/src/schema/billing.sql.ts
index b06ca8966..f8dcbd2b1 100644
--- a/packages/console/core/src/schema/billing.sql.ts
+++ b/packages/console/core/src/schema/billing.sql.ts
@@ -1,4 +1,15 @@
-import { bigint, boolean, index, int, json, mysqlEnum, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
+import {
+ bigint,
+ boolean,
+ index,
+ int,
+ json,
+ mysqlEnum,
+ mysqlTable,
+ primaryKey,
+ uniqueIndex,
+ varchar,
+} from "drizzle-orm/mysql-core"
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
@@ -121,3 +132,14 @@ export const UsageTable = mysqlTable(
},
(table) => [...workspaceIndexes(table), index("usage_time_created").on(table.workspaceID, table.timeCreated)],
)
+
+export const CouponType = ["BUILDATHON", "GOFREEMONTH"] as const
+export const CouponTable = mysqlTable(
+ "coupon",
+ {
+ email: varchar("email", { length: 255 }),
+ type: mysqlEnum("type", CouponType).notNull(),
+ timeRedeemed: utc("time_redeemed"),
+ },
+ (table) => [primaryKey({ columns: [table.email, table.type] })],
+)