From e362c91befea6dc42116425dc3c329e449ebec00 Mon Sep 17 00:00:00 2001 From: John Yeo Date: Thu, 24 Jul 2025 19:49:20 +0100 Subject: [PATCH] fix: email whitespace error --- server/src/internal/api/apiRouter.ts | 3 +- .../orgs/handlers/handleConnectStripe.ts | 142 +++++++++++------- .../orgs/handlers/handleDeleteStripe.ts | 21 ++- .../src/internal/platform/platformRouter.ts | 90 +++++++++++ vite/src/views/customers/CreateCustomer.tsx | 4 +- 5 files changed, 200 insertions(+), 60 deletions(-) create mode 100644 server/src/internal/platform/platformRouter.ts diff --git a/server/src/internal/api/apiRouter.ts b/server/src/internal/api/apiRouter.ts index 2223a2a50..5bb63fc0e 100644 --- a/server/src/internal/api/apiRouter.ts +++ b/server/src/internal/api/apiRouter.ts @@ -27,6 +27,7 @@ import { handleConnectStripe } from "../orgs/handlers/handleConnectStripe.js"; import { handleDeleteStripe } from "../orgs/handlers/handleDeleteStripe.js"; import { refreshCacheMiddleware } from "@/middleware/refreshCacheMiddleware.js"; +import { platformRouter } from "../platform/platformRouter.js"; const apiRouter: Router = Router(); @@ -66,5 +67,5 @@ apiRouter.post("/setup_payment", handleSetupPayment); apiRouter.use("/query", analyticsRouter); apiRouter.post("/org/stripe", handleConnectStripe); apiRouter.delete("/org/stripe", handleDeleteStripe); - +apiRouter.use("/platform", platformRouter); export { apiRouter }; diff --git a/server/src/internal/orgs/handlers/handleConnectStripe.ts b/server/src/internal/orgs/handlers/handleConnectStripe.ts index 9861aba96..858d9d971 100644 --- a/server/src/internal/orgs/handlers/handleConnectStripe.ts +++ b/server/src/internal/orgs/handlers/handleConnectStripe.ts @@ -17,6 +17,86 @@ import { AppEnv } from "@autumn/shared"; import { nullish } from "@/utils/genUtils.js"; import { clearOrgCache } from "../orgUtils/clearOrgCache.js"; +export const connectStripe = async ({ + db, + orgId, + logger, + testApiKey, + liveApiKey, + defaultCurrency, + successUrl, +}: { + db: any; + orgId: string; + logger: any; + testApiKey: string; + liveApiKey: string; + defaultCurrency?: string; + successUrl: string; +}) => { + // 1. Check if API keys are valid + try { + await clearOrgCache({ + db, + orgId, + logger, + }); + + await checkKeyValid(testApiKey); + await checkKeyValid(liveApiKey); + + // Get default currency from Stripe + let stripe = new Stripe(testApiKey); + let account = await stripe.accounts.retrieve(); + if (nullish(defaultCurrency) && nullish(account.default_currency)) { + throw new RecaseError({ + message: "Default currency not set", + code: ErrCode.StripeKeyInvalid, + statusCode: 500, + }); + } else if (nullish(defaultCurrency)) { + defaultCurrency = account.default_currency; + } + } catch (error: any) { + console.error("Error checking stripe keys", error); + throw new RecaseError({ + message: error.message || "Invalid Stripe API keys", + code: ErrCode.StripeKeyInvalid, + statusCode: 500, + data: error, + }); + } + // 2. Create webhook endpoint + let testWebhook: Stripe.WebhookEndpoint; + let liveWebhook: Stripe.WebhookEndpoint; + try { + testWebhook = await createWebhookEndpoint( + testApiKey, + AppEnv.Sandbox, + orgId + ); + liveWebhook = await createWebhookEndpoint(liveApiKey, AppEnv.Live, orgId); + } catch (error) { + throw new RecaseError({ + message: "Error creating stripe webhook", + code: ErrCode.StripeKeyInvalid, + statusCode: 500, + data: error, + }); + } + + return { + defaultCurrency, + stripeConfig: { + test_api_key: encryptData(testApiKey), + live_api_key: encryptData(liveApiKey), + test_webhook_secret: encryptData(testWebhook.secret as string), + live_webhook_secret: encryptData(liveWebhook.secret as string), + success_url: successUrl, + }, + }; +}; + export const handleConnectStripe = async (req: any, res: any) => routeHandler({ req, @@ -33,60 +113,16 @@ export const handleConnectStripe = async (req: any, res: any) => }); } - // 1. Check if API keys are valid - try { - await clearOrgCache({ + let { defaultCurrency: finalDefaultCurrency, stripeConfig } = + await connectStripe({ db, orgId, logger, - }); - - await checkKeyValid(testApiKey); - await checkKeyValid(liveApiKey); - - // Get default currency from Stripe - let stripe = new Stripe(testApiKey); - let account = await stripe.accounts.retrieve(); - if (nullish(defaultCurrency) && nullish(account.default_currency)) { - throw new RecaseError({ - message: "Default currency not set", - code: ErrCode.StripeKeyInvalid, - statusCode: 500, - }); - } else if (nullish(defaultCurrency)) { - defaultCurrency = account.default_currency; - } - } catch (error: any) { - console.error("Error checking stripe keys", error); - throw new RecaseError({ - message: error.message || "Invalid Stripe API keys", - code: ErrCode.StripeKeyInvalid, - statusCode: 500, - data: error, - }); - } - // 2. Create webhook endpoint - let testWebhook: Stripe.WebhookEndpoint; - let liveWebhook: Stripe.WebhookEndpoint; - try { - testWebhook = await createWebhookEndpoint( testApiKey, - AppEnv.Sandbox, - req.orgId - ); - liveWebhook = await createWebhookEndpoint( liveApiKey, - AppEnv.Live, - req.orgId - ); - } catch (error) { - throw new RecaseError({ - message: "Error creating stripe webhook", - code: ErrCode.StripeKeyInvalid, - statusCode: 500, - data: error, + defaultCurrency, + successUrl, }); - } // 1. Update org in Supabase await OrgService.update({ @@ -94,14 +130,8 @@ export const handleConnectStripe = async (req: any, res: any) => orgId: req.orgId, updates: { stripe_connected: true, - default_currency: defaultCurrency, - stripe_config: { - test_api_key: encryptData(testApiKey), - live_api_key: encryptData(liveApiKey), - test_webhook_secret: encryptData(testWebhook.secret as string), - live_webhook_secret: encryptData(liveWebhook.secret as string), - success_url: successUrl, - }, + default_currency: finalDefaultCurrency, + stripe_config: stripeConfig, }, }); diff --git a/server/src/internal/orgs/handlers/handleDeleteStripe.ts b/server/src/internal/orgs/handlers/handleDeleteStripe.ts index 00b8e5ea6..295038231 100644 --- a/server/src/internal/orgs/handlers/handleDeleteStripe.ts +++ b/server/src/internal/orgs/handlers/handleDeleteStripe.ts @@ -2,7 +2,26 @@ import { routeHandler } from "@/utils/routerUtils.js"; import { OrgService } from "../OrgService.js"; import { createStripeCli } from "@/external/stripe/utils.js"; import { clearOrgCache } from "../orgUtils/clearOrgCache.js"; -import { AppEnv } from "@autumn/shared"; +import { AppEnv, Organization } from "@autumn/shared"; + +export const disconnectStripe = async (org: Organization) => { + const testStripeCli = createStripeCli({ org, env: AppEnv.Sandbox }); + const liveStripeCli = createStripeCli({ org, env: AppEnv.Live }); + + const testWebhooks = await testStripeCli.webhookEndpoints.list(); + for (const webhook of testWebhooks.data) { + if (webhook.url.includes(org.id)) { + await testStripeCli.webhookEndpoints.del(webhook.id); + } + } + + const liveWebhooks = await liveStripeCli.webhookEndpoints.list(); + for (const webhook of liveWebhooks.data) { + if (webhook.url.includes(org.id)) { + await liveStripeCli.webhookEndpoints.del(webhook.id); + } + } +}; export const handleDeleteStripe = async (req: any, res: any) => routeHandler({ diff --git a/server/src/internal/platform/platformRouter.ts b/server/src/internal/platform/platformRouter.ts new file mode 100644 index 000000000..8320b34ba --- /dev/null +++ b/server/src/internal/platform/platformRouter.ts @@ -0,0 +1,90 @@ +import { generateId } from "better-auth"; +import { NextFunction, Router } from "express"; +import { member, organizations, user as userTable } from "@autumn/shared"; +import { ExtendedRequest } from "@/utils/models/Request.js"; +import { routeHandler } from "@/utils/routerUtils.js"; +import { slugify } from "@/utils/genUtils.js"; +import { eq } from "drizzle-orm"; +import { connectStripe } from "../orgs/handlers/handleConnectStripe.js"; +import { z } from "zod"; + +const platformRouter = Router(); + +const platformAuthMiddleware = (req: any, res: any, next: NextFunction) => { + next(); +}; + +platformRouter.use(platformAuthMiddleware); + +const ExchangeSchema = z.object({ + organization: z.string(), + email: z.string(), + stripe_test_key: z.string().nonempty(), + stripe_live_key: z.string().nonempty(), +}); + +platformRouter.post("/exchange", (req: any, res: any) => + routeHandler({ + req, + res, + action: "exchange", + handler: async (req: ExtendedRequest, res: any) => { + let { organization, email, stripe_test_key, stripe_live_key } = req.body; + + // 1. Create user with email + const { db } = req; + + // let user = await db.insert(userTable).values({ + // id: generateId(), + // name: "", + // email, + // emailVerified: true, + // createdAt: new Date(), + // updatedAt: new Date(), + // role: "user", + // banned: false, + // banReason: null, + // banExpires: null, + // }); + + // let { defaultCurrency, stripeConfig } = await connectStripe({ + // db, + // orgId: generateId(), + // logger: req.logtail, + // testApiKey: stripe_test_key, + // liveApiKey: stripe_live_key, + // successUrl: "https://useautumn.com", + // }); + + // // 2. Create org + // let orgId = generateId(); + // await db.insert(organizations).values({ + // id: orgId, + // slug: `${slugify(organization)}_${Math.floor(10000000 + Math.random() * 90000000)}`, + // name: organization, + // logo: "", + // createdAt: new Date(), + // metadata: "", + // stripe_connected: true, + // default_currency: defaultCurrency, + // stripe_config: stripeConfig, + // }); + + // // 3. Create membership + // await db.insert(member).values({ + // id: generateId(), + // organizationId: orgId, + // userId: user!.id, + // role: "owner", + // createdAt: new Date(), + // }); + + res.status(200).json({ + // message: "User created", + // user, + }); + }, + }) +); + +export { platformRouter }; diff --git a/vite/src/views/customers/CreateCustomer.tsx b/vite/src/views/customers/CreateCustomer.tsx index 23be37bc9..e985a87f5 100644 --- a/vite/src/views/customers/CreateCustomer.tsx +++ b/vite/src/views/customers/CreateCustomer.tsx @@ -38,7 +38,7 @@ function CreateCustomer() { ...fields, id: fields.id ? fields.id : null, name: fields.name || null, - email: fields.email || null, + email: fields.email ? fields.email.trim() : null, fingerprint: fields.fingerprint ? fields.fingerprint : undefined, }); @@ -49,7 +49,7 @@ function CreateCustomer() { customer.id || customer.autumn_id || customer.internal_id }`, navigate, - env, + env ); } toast.success("Customer created successfully");