zen: redeem go

This commit is contained in:
Frank
2026-04-18 17:33:26 -04:00
parent 9d012b0621
commit 54b3b3fe05
11 changed files with 29 additions and 36 deletions

View File

@@ -236,7 +236,6 @@ new sst.cloudflare.x.SolidStart("Console", {
SALESFORCE_INSTANCE_URL, SALESFORCE_INSTANCE_URL,
ZEN_BLACK_PRICE, ZEN_BLACK_PRICE,
ZEN_LITE_PRICE, ZEN_LITE_PRICE,
new sst.Secret("ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES"),
new sst.Secret("ZEN_LIMITS"), new sst.Secret("ZEN_LIMITS"),
new sst.Secret("ZEN_SESSION_SECRET"), new sst.Secret("ZEN_SESSION_SECRET"),
...ZEN_MODELS, ...ZEN_MODELS,

View File

@@ -9,6 +9,7 @@ import { Actor } from "@opencode-ai/console-core/actor.js"
import { Resource } from "@opencode-ai/console-resource" import { Resource } from "@opencode-ai/console-resource"
import { LiteData } from "@opencode-ai/console-core/lite.js" import { LiteData } from "@opencode-ai/console-core/lite.js"
import { BlackData } from "@opencode-ai/console-core/black.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) { export async function POST(input: APIEvent) {
const body = await Billing.stripe().webhooks.constructEventAsync( const body = await Billing.stripe().webhooks.constructEventAsync(
@@ -109,6 +110,8 @@ export async function POST(input: APIEvent) {
if (type === "lite") { if (type === "lite") {
const workspaceID = body.data.object.metadata?.workspaceID const workspaceID = body.data.object.metadata?.workspaceID
const userID = body.data.object.metadata?.userID 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 customerID = body.data.object.customer as string
const invoiceID = body.data.object.latest_invoice as string const invoiceID = body.data.object.latest_invoice as string
const subscriptionID = body.data.object.id as string const subscriptionID = body.data.object.id as string
@@ -156,6 +159,10 @@ export async function POST(input: APIEvent) {
id: Identifier.create("lite"), id: Identifier.create("lite"),
userID: userID, userID: userID,
}) })
if (userEmail && coupon === LiteData.firstMonth100Coupon) {
await Billing.redeemCoupon(userEmail, "GOFREEMONTH")
}
}) })
}) })
} }

View File

@@ -25,9 +25,9 @@ export default function () {
<Show when={billingInfo()?.customerID}> <Show when={billingInfo()?.customerID}>
<ReloadSection /> <ReloadSection />
<MonthlyLimitSection /> <MonthlyLimitSection />
<RedeemSection />
<PaymentSection /> <PaymentSection />
</Show> </Show>
<RedeemSection />
</Show> </Show>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,5 @@
import { Stripe } from "stripe" import { Stripe } from "stripe"
import { and, Database, eq, sql } from "./drizzle" import { and, Database, eq, isNull, sql } from "./drizzle"
import { import {
BillingTable, BillingTable,
CouponTable, 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) => { export const setMonthlyLimit = fn(z.number(), async (input) => {
return await Database.use((tx) => return await Database.use((tx) =>
tx tx
@@ -274,16 +284,19 @@ export namespace Billing {
const user = Actor.assert("user") const user = Actor.assert("user")
const { successUrl, cancelUrl, method } = input 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() const billing = await Billing.get()
if (billing.subscriptionID) throw new Error("Already subscribed to Black") if (billing.subscriptionID) throw new Error("Already subscribed to Black")
if (billing.liteSubscriptionID) throw new Error("Already subscribed to Lite") if (billing.liteSubscriptionID) throw new Error("Already subscribed to Lite")
const coupon = (await Billing.hasCoupon(email, "GOFREEMONTH"))
? LiteData.firstMonth100Coupon
: LiteData.firstMonth50Coupon
const createSession = () => const createSession = () =>
Billing.stripe().checkout.sessions.create({ Billing.stripe().checkout.sessions.create({
mode: "subscription", mode: "subscription",
discounts: [{ coupon: LiteData.firstMonthCoupon(email!) }], discounts: [{ coupon }],
...(billing.customerID ...(billing.customerID
? { ? {
customer: billing.customerID, customer: billing.customerID,
@@ -293,7 +306,7 @@ export namespace Billing {
}, },
} }
: { : {
customer_email: email!, customer_email: email,
}), }),
...(() => { ...(() => {
if (method === "alipay") { if (method === "alipay") {
@@ -341,6 +354,8 @@ export namespace Billing {
metadata: { metadata: {
workspaceID: Actor.workspace(), workspaceID: Actor.workspace(),
userID: user.properties.userID, userID: user.properties.userID,
userEmail: email,
coupon,
type: "lite", type: "lite",
}, },
}, },

View File

@@ -11,11 +11,7 @@ export namespace LiteData {
export const productID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.product) export const productID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.product)
export const priceID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.price) export const priceID = fn(z.void(), () => Resource.ZEN_LITE_PRICE.price)
export const priceInr = fn(z.void(), () => Resource.ZEN_LITE_PRICE.priceInr) export const priceInr = fn(z.void(), () => Resource.ZEN_LITE_PRICE.priceInr)
export const firstMonthCoupon = fn(z.string(), (email) => { export const firstMonth100Coupon = Resource.ZEN_LITE_PRICE.firstMonth100Coupon
const invitees = Resource.ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES.value.split(",") export const firstMonth50Coupon = Resource.ZEN_LITE_PRICE.firstMonth50Coupon
return invitees.includes(email)
? Resource.ZEN_LITE_PRICE.firstMonth100Coupon
: Resource.ZEN_LITE_PRICE.firstMonth50Coupon
})
export const planName = fn(z.void(), () => "lite") export const planName = fn(z.void(), () => "lite")
} }

View File

@@ -142,10 +142,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string

View File

@@ -142,10 +142,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string

View File

@@ -142,10 +142,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string

View File

@@ -142,10 +142,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string

View File

@@ -142,10 +142,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string

4
sst-env.d.ts vendored
View File

@@ -168,10 +168,6 @@ declare module "sst" {
"type": "sst.sst.Secret" "type": "sst.sst.Secret"
"value": string "value": string
} }
"ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": { "ZEN_LITE_PRICE": {
"firstMonth100Coupon": string "firstMonth100Coupon": string
"firstMonth50Coupon": string "firstMonth50Coupon": string