Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/self-hosting/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Parameters marked with <span style={{color: '#ef5151'}}>\*</span> are required.
| DEFAULT_WORKSPACE_PLAN | FREE | Default workspace plan on user creation or when a user creates a new workspace. Possible values are `FREE`, `STARTER`, `PRO`, `LIFETIME`, `UNLIMITED`. The default plan for admin user is `UNLIMITED` |
| DISABLE_SIGNUP | false | Disable new user sign ups. Invited users are still able to sign up. |
| NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID | | Typebot ID used for the onboarding. Onboarding page is skipped if not provided. |
| DEBUG | false | If enabled, the server will print valuable logs to debug config issues. |
| TYPEBOT_DEBUG | false | If enabled, the server will print valuable logs to debug config issues. |
| NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE | | Limits the size of each file that can be uploaded in the bots (i.e. Set `10` to limit the file upload to 10MB) |
| CHAT_API_TIMEOUT | | The chat API execution timeout (in ms). It limits the chat API exection time. Useful to avoid getting stuck into an unwanted infinite loop. Note that it does not apply to known long-running blocks like OpenAI or else. |

Expand Down
2 changes: 1 addition & 1 deletion apps/viewer/src/pages/[[...publicId]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "@/components/TypebotPageV3";

const log = (message: string) => {
if (!env.DEBUG) return;
if (!env.TYPEBOT_DEBUG) return;
console.log(`[DEBUG] ${message}`);
};

Expand Down
48 changes: 48 additions & 0 deletions packages/billing/src/api/handleCheckoutCancelRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ORPCError } from "@orpc/server";
import { env } from "@typebot.io/env";
import type { User } from "@typebot.io/user/schemas";
import Stripe from "stripe";
import { z } from "zod";

export const cancelCheckoutSessionInputSchema = z.object({
customerId: z.string(),
returnUrl: z.string(),
});

export const handleCheckoutCancelRedirect = async ({
input: { returnUrl, customerId },
context: { user },
}: {
input: z.infer<typeof cancelCheckoutSessionInputSchema>;
context: { user: Pick<User, "email"> };
}) => {
if (!env.STRIPE_SECRET_KEY)
throw new ORPCError("INTERNAL_SERVER_ERROR", {
message: "Stripe environment variables are missing",
});
const stripe = new Stripe(env.STRIPE_SECRET_KEY);

const redirectResponse = {
headers: {
location: returnUrl,
},
};

const customer = await stripe.customers.retrieve(customerId);

if (
customer.deleted ||
customer.email !== user.email ||
(customer.subscriptions?.data.length ?? 0) > 0
)
return redirectResponse;

const deletedCustomer = await stripe.customers.del(customerId);

if (!deletedCustomer.deleted)
throw new ORPCError("INTERNAL_SERVER_ERROR", {
message: "Failed to delete customer",
});

return redirectResponse;
};
15 changes: 15 additions & 0 deletions packages/billing/src/api/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { workspaceSchema } from "@typebot.io/workspaces/schemas";
import { z } from "zod";
import { invoiceSchema } from "../schemas/invoice";
import { subscriptionSchema } from "../schemas/subscription";
import {
cancelCheckoutSessionInputSchema,
handleCheckoutCancelRedirect,
} from "./handleCheckoutCancelRedirect";
import {
createCheckoutSessionInputSchema,
handleCreateCheckoutSession,
Expand Down Expand Up @@ -40,6 +44,8 @@ import {
updateSubscriptionInputSchema,
} from "./handleUpdateSubscription";

export const cancelCheckoutPath = "/v1/billing/checkout/cancel" as const;

export const billingRouter = {
webhook: publicProcedure
.route({
Expand Down Expand Up @@ -144,4 +150,13 @@ export const billingRouter = {
]),
)
.handler(handleUpdateSubscription),
cancelCheckoutSession: authenticatedProcedure
.route({
successStatus: 307,
outputStructure: "detailed",
method: "GET",
path: cancelCheckoutPath,
})
.input(cancelCheckoutSessionInputSchema)
.handler(handleCheckoutCancelRedirect),
};
5 changes: 4 additions & 1 deletion packages/billing/src/helpers/createCheckoutSessionUrl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { env } from "@typebot.io/env";
import type Stripe from "stripe";
import { cancelCheckoutPath } from "../api/router";

type Props = {
customerId: string;
Expand All @@ -12,9 +13,11 @@ type Props = {
export const createCheckoutSessionUrl =
(stripe: Stripe) =>
async ({ customerId, workspaceId, plan, returnUrl }: Props) => {
const returnUrlOrigin = new URL(returnUrl).origin;
const cancelUrl = `${returnUrlOrigin}/api${cancelCheckoutPath}?returnUrl=${returnUrl}&customerId=${customerId}`;
const session = await stripe.checkout.sessions.create({
success_url: `${returnUrl}?stripe=${plan}&success=true`,
cancel_url: `${returnUrl}?stripe=cancel`,
cancel_url: cancelUrl,
allow_promotion_codes: true,
customer: customerId,
customer_update: {
Expand Down
2 changes: 1 addition & 1 deletion packages/env/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const baseEnv = {
["FREE", "STARTER", "PRO", "LIFETIME", "UNLIMITED"].includes(str),
)
.default("FREE"),
DEBUG: boolean.optional().default(false),
TYPEBOT_DEBUG: boolean.optional().default(false),
CHAT_API_TIMEOUT: z.coerce.number().optional(),
RADAR_HIGH_RISK_KEYWORDS: z
.string()
Expand Down
Loading