From 54b3b3fe05fd66f8f3b479ec438b53d38dbc6d0d Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 18 Apr 2026 17:33:26 -0400 Subject: [PATCH] zen: redeem go --- infra/console.ts | 1 - .../console/app/src/routes/stripe/webhook.ts | 7 ++++++ .../routes/workspace/[id]/billing/index.tsx | 2 +- packages/console/core/src/billing.ts | 23 +++++++++++++++---- packages/console/core/src/lite.ts | 8 ++----- packages/console/core/sst-env.d.ts | 4 ---- packages/console/function/sst-env.d.ts | 4 ---- packages/console/resource/sst-env.d.ts | 4 ---- packages/enterprise/sst-env.d.ts | 4 ---- packages/function/sst-env.d.ts | 4 ---- sst-env.d.ts | 4 ---- 11 files changed, 29 insertions(+), 36 deletions(-) diff --git a/infra/console.ts b/infra/console.ts index 8925f37d5a..f1f5692b7a 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -236,7 +236,6 @@ new sst.cloudflare.x.SolidStart("Console", { SALESFORCE_INSTANCE_URL, ZEN_BLACK_PRICE, ZEN_LITE_PRICE, - new sst.Secret("ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES"), new sst.Secret("ZEN_LIMITS"), new sst.Secret("ZEN_SESSION_SECRET"), ...ZEN_MODELS, diff --git a/packages/console/app/src/routes/stripe/webhook.ts b/packages/console/app/src/routes/stripe/webhook.ts index 0d8cf61cfa..c28d9ebbb9 100644 --- a/packages/console/app/src/routes/stripe/webhook.ts +++ b/packages/console/app/src/routes/stripe/webhook.ts @@ -9,6 +9,7 @@ import { Actor } from "@opencode-ai/console-core/actor.js" import { Resource } from "@opencode-ai/console-resource" import { LiteData } from "@opencode-ai/console-core/lite.js" import { BlackData } from "@opencode-ai/console-core/black.js" +import { User } from "@opencode-ai/console-core/user.js" export async function POST(input: APIEvent) { const body = await Billing.stripe().webhooks.constructEventAsync( @@ -109,6 +110,8 @@ export async function POST(input: APIEvent) { if (type === "lite") { const workspaceID = body.data.object.metadata?.workspaceID const userID = body.data.object.metadata?.userID + const userEmail = body.data.object.metadata?.userEmail + const coupon = body.data.object.metadata?.coupon const customerID = body.data.object.customer as string const invoiceID = body.data.object.latest_invoice as string const subscriptionID = body.data.object.id as string @@ -156,6 +159,10 @@ export async function POST(input: APIEvent) { id: Identifier.create("lite"), userID: userID, }) + + if (userEmail && coupon === LiteData.firstMonth100Coupon) { + await Billing.redeemCoupon(userEmail, "GOFREEMONTH") + } }) }) } diff --git a/packages/console/app/src/routes/workspace/[id]/billing/index.tsx b/packages/console/app/src/routes/workspace/[id]/billing/index.tsx index fb48485354..11185436be 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/index.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/index.tsx @@ -25,9 +25,9 @@ export default function () { + - diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts index d96b19ad79..a3252f1d78 100644 --- a/packages/console/core/src/billing.ts +++ b/packages/console/core/src/billing.ts @@ -1,5 +1,5 @@ import { Stripe } from "stripe" -import { and, Database, eq, sql } from "./drizzle" +import { and, Database, eq, isNull, sql } from "./drizzle" import { BillingTable, CouponTable, @@ -176,6 +176,16 @@ export namespace Billing { ) } + export const hasCoupon = async (email: string, type: (typeof CouponType)[number]) => { + return await Database.use((tx) => + tx + .select() + .from(CouponTable) + .where(and(eq(CouponTable.email, email), eq(CouponTable.type, type), isNull(CouponTable.timeRedeemed))) + .then((rows) => rows.length > 0), + ) + } + export const setMonthlyLimit = fn(z.number(), async (input) => { return await Database.use((tx) => tx @@ -274,16 +284,19 @@ export namespace Billing { const user = Actor.assert("user") const { successUrl, cancelUrl, method } = input - const email = await User.getAuthEmail(user.properties.userID) + 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 coupon = (await Billing.hasCoupon(email, "GOFREEMONTH")) + ? LiteData.firstMonth100Coupon + : LiteData.firstMonth50Coupon const createSession = () => Billing.stripe().checkout.sessions.create({ mode: "subscription", - discounts: [{ coupon: LiteData.firstMonthCoupon(email!) }], + discounts: [{ coupon }], ...(billing.customerID ? { customer: billing.customerID, @@ -293,7 +306,7 @@ export namespace Billing { }, } : { - customer_email: email!, + customer_email: email, }), ...(() => { if (method === "alipay") { @@ -341,6 +354,8 @@ export namespace Billing { metadata: { workspaceID: Actor.workspace(), userID: user.properties.userID, + userEmail: email, + coupon, type: "lite", }, }, diff --git a/packages/console/core/src/lite.ts b/packages/console/core/src/lite.ts index 3343192c19..c049776643 100644 --- a/packages/console/core/src/lite.ts +++ b/packages/console/core/src/lite.ts @@ -11,11 +11,7 @@ export namespace LiteData { export const productID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.product) export const priceID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.price) export const priceInr = fn(z.void(), () => Resource.ZEN_LITE_PRICE.priceInr) - export const firstMonthCoupon = fn(z.string(), (email) => { - const invitees = Resource.ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES.value.split(",") - return invitees.includes(email) - ? Resource.ZEN_LITE_PRICE.firstMonth100Coupon - : Resource.ZEN_LITE_PRICE.firstMonth50Coupon - }) + export const firstMonth100Coupon = Resource.ZEN_LITE_PRICE.firstMonth100Coupon + export const firstMonth50Coupon = Resource.ZEN_LITE_PRICE.firstMonth50Coupon export const planName = fn(z.void(), () => "lite") } diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index b77ee3c5bf..bfba1b8f2e 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -142,10 +142,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index b77ee3c5bf..bfba1b8f2e 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -142,10 +142,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index b77ee3c5bf..bfba1b8f2e 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -142,10 +142,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index b77ee3c5bf..bfba1b8f2e 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -142,10 +142,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index b77ee3c5bf..bfba1b8f2e 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -142,10 +142,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string diff --git a/sst-env.d.ts b/sst-env.d.ts index 2a40a9f3c9..e0c3665a96 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -168,10 +168,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": { - "type": "sst.sst.Secret" - "value": string - } "ZEN_LITE_PRICE": { "firstMonth100Coupon": string "firstMonth50Coupon": string