zen: coupons

This commit is contained in:
Frank
2026-04-27 01:36:26 -04:00
parent 17701628bd
commit e8c20235b8
13 changed files with 2741 additions and 10 deletions
+24
View File
@@ -115,6 +115,27 @@ const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100
appliesToProducts: [zenLiteProduct.id], appliesToProducts: [zenLiteProduct.id],
duration: "once", duration: "once",
}) })
const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
name: "3 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 3,
})
const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
name: "6 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 6,
})
const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
name: "12 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 12,
})
const zenLitePrice = new stripe.Price("ZenLitePrice", { const zenLitePrice = new stripe.Price("ZenLitePrice", {
product: zenLiteProduct.id, product: zenLiteProduct.id,
currency: "usd", currency: "usd",
@@ -131,6 +152,9 @@ const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
priceInr: 92900, priceInr: 92900,
firstMonth50Coupon: zenLiteCouponFirstMonth50.id, firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
firstMonth100Coupon: zenLiteCouponFirstMonth100.id, firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
sixMonths100Coupon: zenLiteCouponSixMonths100.id,
twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
}, },
}) })
@@ -160,8 +160,16 @@ export async function POST(input: APIEvent) {
userID: userID, userID: userID,
}) })
if (userEmail && coupon === LiteData.firstMonth100Coupon) { if (userEmail) {
await Billing.redeemCoupon(userEmail, "GOFREEMONTH") if (coupon === LiteData.firstMonth100Coupon) {
await Billing.redeemCoupon(userEmail, "GOFREEMONTH")
} else if (coupon === LiteData.threeMonths100Coupon) {
await Billing.redeemCoupon(userEmail, "GO3MONTHS100")
} else if (coupon === LiteData.sixMonths100Coupon) {
await Billing.redeemCoupon(userEmail, "GO6MONTHS100")
} else if (coupon === LiteData.twelveMonths100Coupon) {
await Billing.redeemCoupon(userEmail, "GO12MONTHS100")
}
} }
}) })
}) })
@@ -0,0 +1 @@
ALTER TABLE `coupon` MODIFY COLUMN `type` enum('BUILDATHON','GOFREEMONTH','GO3MONTHS100','GO6MONTHS100','GO12MONTHS100') NOT NULL;
+14 -7
View File
@@ -176,13 +176,13 @@ export namespace Billing {
) )
} }
export const hasCoupon = async (email: string, type: (typeof CouponType)[number]) => { export const getCoupons = async (email: string) => {
return await Database.use((tx) => return await Database.use((tx) =>
tx tx
.select() .select({ type: CouponTable.type, timeRedeemed: CouponTable.timeRedeemed })
.from(CouponTable) .from(CouponTable)
.where(and(eq(CouponTable.email, email), eq(CouponTable.type, type), isNull(CouponTable.timeRedeemed))) .where(and(eq(CouponTable.email, email), isNull(CouponTable.timeRedeemed)))
.then((rows) => rows.length > 0), .then((rows) => rows.map((row) => row.type)),
) )
} }
@@ -290,9 +290,16 @@ export namespace Billing {
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")) const coupons = await Billing.getCoupons(email)
? LiteData.firstMonth100Coupon const coupon = coupons.includes("GO12MONTHS100")
: LiteData.firstMonth50Coupon ? LiteData.twelveMonths100Coupon
: coupons.includes("GO6MONTHS100")
? LiteData.sixMonths100Coupon
: coupons.includes("GO3MONTHS100")
? LiteData.threeMonths100Coupon
: coupons.includes("GOFREEMONTH")
? LiteData.firstMonth100Coupon
: LiteData.firstMonth50Coupon
const createSession = () => const createSession = () =>
Billing.stripe().checkout.sessions.create({ Billing.stripe().checkout.sessions.create({
mode: "subscription", mode: "subscription",
+3
View File
@@ -13,5 +13,8 @@ export namespace LiteData {
export const priceInr = fn(z.void(), () => Resource.ZEN_LITE_PRICE.priceInr) export const priceInr = fn(z.void(), () => Resource.ZEN_LITE_PRICE.priceInr)
export const firstMonth100Coupon = Resource.ZEN_LITE_PRICE.firstMonth100Coupon export const firstMonth100Coupon = Resource.ZEN_LITE_PRICE.firstMonth100Coupon
export const firstMonth50Coupon = Resource.ZEN_LITE_PRICE.firstMonth50Coupon export const firstMonth50Coupon = Resource.ZEN_LITE_PRICE.firstMonth50Coupon
export const threeMonths100Coupon = Resource.ZEN_LITE_PRICE.threeMonths100Coupon
export const sixMonths100Coupon = Resource.ZEN_LITE_PRICE.sixMonths100Coupon
export const twelveMonths100Coupon = Resource.ZEN_LITE_PRICE.twelveMonths100Coupon
export const planName = fn(z.void(), () => "lite") export const planName = fn(z.void(), () => "lite")
} }
@@ -133,7 +133,7 @@ export const UsageTable = mysqlTable(
(table) => [...workspaceIndexes(table), index("usage_time_created").on(table.workspaceID, table.timeCreated)], (table) => [...workspaceIndexes(table), index("usage_time_created").on(table.workspaceID, table.timeCreated)],
) )
export const CouponType = ["BUILDATHON", "GOFREEMONTH"] as const export const CouponType = ["BUILDATHON", "GOFREEMONTH", "GO3MONTHS100", "GO6MONTHS100", "GO12MONTHS100"] as const
export const CouponTable = mysqlTable( export const CouponTable = mysqlTable(
"coupon", "coupon",
{ {
+3
View File
@@ -148,6 +148,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {
+3
View File
@@ -148,6 +148,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {
+3
View File
@@ -148,6 +148,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {
+3
View File
@@ -148,6 +148,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {
+3
View File
@@ -148,6 +148,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {
+3
View File
@@ -174,6 +174,9 @@ declare module "sst" {
"price": string "price": string
"priceInr": number "priceInr": number
"product": string "product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable" "type": "sst.sst.Linkable"
} }
"ZEN_MODELS1": { "ZEN_MODELS1": {