{i18n.t("workspace.lite.subscription.message")}
-
>
diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts
index ee41652ef2..66b9806985 100644
--- a/packages/console/core/src/billing.ts
+++ b/packages/console/core/src/billing.ts
@@ -239,10 +239,11 @@ export namespace Billing {
z.object({
successUrl: z.string(),
cancelUrl: z.string(),
+ method: z.enum(["alipay", "upi"]).optional(),
}),
async (input) => {
const user = Actor.assert("user")
- const { successUrl, cancelUrl } = input
+ const { successUrl, cancelUrl, method } = input
const email = await User.getAuthEmail(user.properties.userID)
const billing = await Billing.get()
@@ -250,38 +251,102 @@ export namespace Billing {
if (billing.subscriptionID) throw new Error("Already subscribed to Black")
if (billing.liteSubscriptionID) throw new Error("Already subscribed to Lite")
- const session = await Billing.stripe().checkout.sessions.create({
- mode: "subscription",
- billing_address_collection: "required",
- line_items: [{ price: LiteData.priceID(), quantity: 1 }],
- discounts: [{ coupon: LiteData.firstMonth50Coupon() }],
- ...(billing.customerID
- ? {
- customer: billing.customerID,
- customer_update: {
- name: "auto",
- address: "auto",
- },
+ const createSession = () =>
+ Billing.stripe().checkout.sessions.create({
+ mode: "subscription",
+ discounts: [{ coupon: LiteData.firstMonth50Coupon() }],
+ ...(billing.customerID
+ ? {
+ customer: billing.customerID,
+ customer_update: {
+ name: "auto",
+ address: "auto",
+ },
+ }
+ : {
+ customer_email: email!,
+ }),
+ ...(() => {
+ if (method === "alipay") {
+ return {
+ line_items: [{ price: LiteData.priceID(), quantity: 1 }],
+ payment_method_types: ["alipay"],
+ adaptive_pricing: {
+ enabled: false,
+ },
+ }
}
- : {
- customer_email: email!,
- }),
- currency: "usd",
- tax_id_collection: {
- enabled: true,
- },
- success_url: successUrl,
- cancel_url: cancelUrl,
- subscription_data: {
- metadata: {
- workspaceID: Actor.workspace(),
- userID: user.properties.userID,
- type: "lite",
+ if (method === "upi") {
+ return {
+ line_items: [
+ {
+ price_data: {
+ currency: "inr",
+ product: LiteData.productID(),
+ recurring: {
+ interval: "month",
+ interval_count: 1,
+ },
+ unit_amount: LiteData.priceInr(),
+ },
+ quantity: 1,
+ },
+ ],
+ payment_method_types: ["upi"] as any,
+ adaptive_pricing: {
+ enabled: false,
+ },
+ }
+ }
+ return {
+ line_items: [{ price: LiteData.priceID(), quantity: 1 }],
+ billing_address_collection: "required",
+ }
+ })(),
+ tax_id_collection: {
+ enabled: true,
},
- },
- })
+ success_url: successUrl,
+ cancel_url: cancelUrl,
+ subscription_data: {
+ metadata: {
+ workspaceID: Actor.workspace(),
+ userID: user.properties.userID,
+ type: "lite",
+ },
+ },
+ })
- return session.url
+ try {
+ const session = await createSession()
+ return session.url
+ } catch (e: any) {
+ if (
+ e.type !== "StripeInvalidRequestError" ||
+ !e.message.includes("You cannot combine currencies on a single customer")
+ )
+ throw e
+
+ // get pending payment intent
+ const intents = await Billing.stripe().paymentIntents.search({
+ query: `-status:'canceled' AND -status:'processing' AND -status:'succeeded' AND customer:'${billing.customerID}'`,
+ })
+ if (intents.data.length === 0) throw e
+
+ for (const intent of intents.data) {
+ // get checkout session
+ const sessions = await Billing.stripe().checkout.sessions.list({
+ customer: billing.customerID!,
+ payment_intent: intent.id,
+ })
+
+ // delete pending payment intent
+ await Billing.stripe().checkout.sessions.expire(sessions.data[0].id)
+ }
+
+ const session = await createSession()
+ return session.url
+ }
},
)
diff --git a/packages/console/core/src/lite.ts b/packages/console/core/src/lite.ts
index 8c5b63d0c7..2c4a09f711 100644
--- a/packages/console/core/src/lite.ts
+++ b/packages/console/core/src/lite.ts
@@ -10,6 +10,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 firstMonth50Coupon = fn(z.void(), () => Resource.ZEN_LITE_PRICE.firstMonth50Coupon)
export const planName = fn(z.void(), () => "lite")
}
diff --git a/packages/console/core/src/schema/billing.sql.ts b/packages/console/core/src/schema/billing.sql.ts
index a5c70c2115..b06ca8966d 100644
--- a/packages/console/core/src/schema/billing.sql.ts
+++ b/packages/console/core/src/schema/billing.sql.ts
@@ -88,6 +88,7 @@ export const PaymentTable = mysqlTable(
enrichment: json("enrichment").$type<
| {
type: "subscription" | "lite"
+ currency?: "inr"
couponID?: string
}
| {
diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts
index 5e2693ad86..6b842639ad 100644
--- a/packages/console/core/sst-env.d.ts
+++ b/packages/console/core/sst-env.d.ts
@@ -145,6 +145,7 @@ declare module "sst" {
"ZEN_LITE_PRICE": {
"firstMonth50Coupon": string
"price": string
+ "priceInr": number
"product": string
"type": "sst.sst.Linkable"
}
diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts
index 5e2693ad86..6b842639ad 100644
--- a/packages/console/function/sst-env.d.ts
+++ b/packages/console/function/sst-env.d.ts
@@ -145,6 +145,7 @@ declare module "sst" {
"ZEN_LITE_PRICE": {
"firstMonth50Coupon": string
"price": string
+ "priceInr": number
"product": string
"type": "sst.sst.Linkable"
}
diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts
index 5e2693ad86..6b842639ad 100644
--- a/packages/console/resource/sst-env.d.ts
+++ b/packages/console/resource/sst-env.d.ts
@@ -145,6 +145,7 @@ declare module "sst" {
"ZEN_LITE_PRICE": {
"firstMonth50Coupon": string
"price": string
+ "priceInr": number
"product": string
"type": "sst.sst.Linkable"
}
diff --git a/packages/desktop-electron/src/main/ipc.ts b/packages/desktop-electron/src/main/ipc.ts
index 71b3c33958..543f857a5e 100644
--- a/packages/desktop-electron/src/main/ipc.ts
+++ b/packages/desktop-electron/src/main/ipc.ts
@@ -6,6 +6,11 @@ import type { InitStep, ServerReadyData, SqliteMigrationProgress, TitlebarTheme,
import { getStore } from "./store"
import { setTitlebar } from "./windows"
+const pickerFilters = (ext?: string[]) => {
+ if (!ext || ext.length === 0) return undefined
+ return [{ name: "Files", extensions: ext }]
+}
+
type Deps = {
killSidecar: () => void
installCli: () => Promise