go: support coupon
parent
41612b3dbe
commit
01c5eb679c
|
|
@ -109,6 +109,12 @@ const zenLiteCouponFirstMonth50 = new stripe.Coupon("ZenLiteCouponFirstMonth50",
|
||||||
appliesToProducts: [zenLiteProduct.id],
|
appliesToProducts: [zenLiteProduct.id],
|
||||||
duration: "once",
|
duration: "once",
|
||||||
})
|
})
|
||||||
|
const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100", {
|
||||||
|
name: "First month 100% off",
|
||||||
|
percentOff: 100,
|
||||||
|
appliesToProducts: [zenLiteProduct.id],
|
||||||
|
duration: "once",
|
||||||
|
})
|
||||||
const zenLitePrice = new stripe.Price("ZenLitePrice", {
|
const zenLitePrice = new stripe.Price("ZenLitePrice", {
|
||||||
product: zenLiteProduct.id,
|
product: zenLiteProduct.id,
|
||||||
currency: "usd",
|
currency: "usd",
|
||||||
|
|
@ -124,6 +130,7 @@ const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
|
||||||
price: zenLitePrice.id,
|
price: zenLitePrice.id,
|
||||||
priceInr: 92900,
|
priceInr: 92900,
|
||||||
firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
|
firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
|
||||||
|
firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -229,6 +236,7 @@ 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,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Stripe } from "stripe"
|
||||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
import type { APIEvent } from "@solidjs/start/server"
|
import type { APIEvent } from "@solidjs/start/server"
|
||||||
import { and, Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js"
|
import { and, Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
|
|
@ -111,27 +112,17 @@ export async function POST(input: APIEvent) {
|
||||||
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
|
||||||
|
const paymentMethodID = body.data.object.default_payment_method as string
|
||||||
|
|
||||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||||
if (!userID) throw new Error("User ID not found")
|
if (!userID) throw new Error("User ID not found")
|
||||||
if (!customerID) throw new Error("Customer ID not found")
|
if (!customerID) throw new Error("Customer ID not found")
|
||||||
if (!invoiceID) throw new Error("Invoice ID not found")
|
if (!invoiceID) throw new Error("Invoice ID not found")
|
||||||
if (!subscriptionID) throw new Error("Subscription ID not found")
|
if (!subscriptionID) throw new Error("Subscription ID not found")
|
||||||
|
if (!paymentMethodID) throw new Error("Payment method ID not found")
|
||||||
// get payment id from invoice
|
|
||||||
const invoice = await Billing.stripe().invoices.retrieve(invoiceID, {
|
|
||||||
expand: ["payments"],
|
|
||||||
})
|
|
||||||
const paymentID = invoice.payments?.data[0].payment.payment_intent as string
|
|
||||||
if (!paymentID) throw new Error("Payment ID not found")
|
|
||||||
|
|
||||||
// get payment method for the payment intent
|
// get payment method for the payment intent
|
||||||
const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
|
const paymentMethod = await Billing.stripe().paymentMethods.retrieve(paymentMethodID)
|
||||||
expand: ["payment_method"],
|
|
||||||
})
|
|
||||||
const paymentMethod = paymentIntent.payment_method
|
|
||||||
if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")
|
|
||||||
|
|
||||||
await Actor.provide("system", { workspaceID }, async () => {
|
await Actor.provide("system", { workspaceID }, async () => {
|
||||||
// look up current billing
|
// look up current billing
|
||||||
const billing = await Billing.get()
|
const billing = await Billing.get()
|
||||||
|
|
@ -200,26 +191,18 @@ export async function POST(input: APIEvent) {
|
||||||
const amountInCents = body.data.object.amount_paid
|
const amountInCents = body.data.object.amount_paid
|
||||||
const customerID = body.data.object.customer as string
|
const customerID = body.data.object.customer as string
|
||||||
const subscriptionID = body.data.object.parent?.subscription_details?.subscription as string
|
const subscriptionID = body.data.object.parent?.subscription_details?.subscription as string
|
||||||
|
const productID = body.data.object.lines?.data[0].pricing?.price_details?.product as string
|
||||||
|
|
||||||
if (!customerID) throw new Error("Customer ID not found")
|
if (!customerID) throw new Error("Customer ID not found")
|
||||||
if (!invoiceID) throw new Error("Invoice ID not found")
|
if (!invoiceID) throw new Error("Invoice ID not found")
|
||||||
if (!subscriptionID) throw new Error("Subscription ID not found")
|
if (!subscriptionID) throw new Error("Subscription ID not found")
|
||||||
|
|
||||||
// get coupon id from subscription
|
// get coupon id from subscription
|
||||||
const subscriptionData = await Billing.stripe().subscriptions.retrieve(subscriptionID, {
|
|
||||||
expand: ["discounts"],
|
|
||||||
})
|
|
||||||
const couponID =
|
|
||||||
typeof subscriptionData.discounts[0] === "string"
|
|
||||||
? subscriptionData.discounts[0]
|
|
||||||
: subscriptionData.discounts[0]?.coupon?.id
|
|
||||||
const productID = subscriptionData.items.data[0].price.product as string
|
|
||||||
|
|
||||||
// get payment id from invoice
|
|
||||||
const invoice = await Billing.stripe().invoices.retrieve(invoiceID, {
|
const invoice = await Billing.stripe().invoices.retrieve(invoiceID, {
|
||||||
expand: ["payments"],
|
expand: ["discounts", "payments"],
|
||||||
})
|
})
|
||||||
const paymentID = invoice.payments?.data[0].payment.payment_intent as string
|
const paymentID = invoice.payments?.data[0]?.payment.payment_intent as string
|
||||||
|
const couponID = (invoice.discounts[0] as Stripe.Discount).coupon?.id as string
|
||||||
if (!paymentID) {
|
if (!paymentID) {
|
||||||
// payment id can be undefined when using coupon
|
// payment id can be undefined when using coupon
|
||||||
if (!couponID) throw new Error("Payment ID not found")
|
if (!couponID) throw new Error("Payment ID not found")
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ export namespace Billing {
|
||||||
const createSession = () =>
|
const createSession = () =>
|
||||||
Billing.stripe().checkout.sessions.create({
|
Billing.stripe().checkout.sessions.create({
|
||||||
mode: "subscription",
|
mode: "subscription",
|
||||||
discounts: [{ coupon: LiteData.firstMonth50Coupon() }],
|
discounts: [{ coupon: LiteData.firstMonthCoupon(email!) }],
|
||||||
...(billing.customerID
|
...(billing.customerID
|
||||||
? {
|
? {
|
||||||
customer: billing.customerID,
|
customer: billing.customerID,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ 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 firstMonth50Coupon = fn(z.void(), () => Resource.ZEN_LITE_PRICE.firstMonth50Coupon)
|
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 planName = fn(z.void(), () => "lite")
|
export const planName = fn(z.void(), () => "lite")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,12 @@ 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
|
||||||
"firstMonth50Coupon": string
|
"firstMonth50Coupon": string
|
||||||
"price": string
|
"price": string
|
||||||
"priceInr": number
|
"priceInr": number
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue