// @ts-nocheck import { waitUntil } from '@vercel/functions' import { NextApiRequest, NextApiResponse } from 'next' import { VERSION } from 'stripe-experiment-sync' import { install, uninstall } from 'stripe-experiment-sync/supabase' import { z } from 'zod' const InstallBodySchema = z.object({ projectRef: z.string().min(1), stripeSecretKey: z.string().min(1), startTime: z.number().positive().optional(), }) const UninstallBodySchema = z.object({ projectRef: z.string().min(1), startTime: z.number().positive().optional(), }) async function isStripeSyncEnabled() { // The ConfigClient doesn't seem to work properly so we'll just gate access from the frontend // for now return true } function getBearerToken(req: NextApiRequest) { const authHeader = req.headers.authorization if (!authHeader || Array.isArray(authHeader)) return null const match = authHeader.match(/^Bearer\s+(.+)$/i) return match?.[1]?.trim() ?? null } export const config = { maxDuration: 300, // 5 minutes, since the installation process can take a while even if happening in background } export default async function handler(req: NextApiRequest, res: NextApiResponse) { // Hide endpoint if the integration is disabled. if (!(await isStripeSyncEnabled())) { return res.status(404).json({ data: null, error: { message: 'Not Found' } }) } const { method } = req switch (method) { case 'POST': return handleSetupStripeSyncInstall(req, res) case 'DELETE': return handleDeleteStripeSyncInstall(req, res) default: return res .status(405) .json({ data: null, error: { message: `Method ${method} Not Allowed` } }) } } async function handleDeleteStripeSyncInstall(req: NextApiRequest, res: NextApiResponse) { const brivenToken = getBearerToken(req) if (!brivenToken) { return res .status(401) .json({ data: null, error: { message: 'Unauthorized: Invalid Authorization header' } }) } const parsed = UninstallBodySchema.safeParse(req.body) if (!parsed.success) { return res .status(400) .json({ data: null, error: { message: 'Bad Request: Invalid request body' } }) } const { projectRef, startTime } = parsed.data waitUntil( uninstall({ brivenAccessToken: brivenToken, brivenProjectRef: projectRef, baseProjectUrl: process.env.NEXT_PUBLIC_CUSTOMER_DOMAIN, brivenManagementUrl: process.env.NEXT_PUBLIC_API_DOMAIN, startTime, }).catch((error) => { console.error('Stripe Sync Engine uninstallation failed.', error) throw error }) ) return res .status(200) .json({ data: { message: 'Stripe Sync uninstallation initiated' }, error: null }) } async function handleSetupStripeSyncInstall(req: NextApiRequest, res: NextApiResponse) { const brivenToken = getBearerToken(req) if (!brivenToken) { return res .status(401) .json({ data: null, error: { message: 'Unauthorized: Invalid Authorization header' } }) } const parsed = InstallBodySchema.safeParse(req.body) if (!parsed.success) { return res .status(400) .json({ data: null, error: { message: 'Bad Request: Invalid request body' } }) } const { projectRef, stripeSecretKey, startTime } = parsed.data // Validate the Stripe API key before proceeding with installation try { const stripeResponse = await fetch('https://api.stripe.com/v1/account', { method: 'GET', headers: { Authorization: `Bearer ${stripeSecretKey}`, 'Content-Type': 'application/x-www-form-urlencoded', }, }) if (!stripeResponse.ok) { const errorData = await stripeResponse.json() const errorMessage = errorData.error?.message || `Invalid Stripe API key (HTTP ${stripeResponse.status})` return res.status(400).json({ data: null, error: { message: errorMessage }, }) } } catch (error) { const normalizedErrorMessage = error instanceof Error ? error.message : String(error) return res.status(400).json({ data: null, error: { message: `Failed to validate Stripe API key: ${normalizedErrorMessage}` }, }) } waitUntil( install({ brivenAccessToken: brivenToken, brivenProjectRef: projectRef, stripeKey: stripeSecretKey, baseProjectUrl: process.env.NEXT_PUBLIC_CUSTOMER_DOMAIN, brivenManagementUrl: process.env.NEXT_PUBLIC_API_DOMAIN, packageVersion: VERSION, startTime, }).catch((error) => { console.error('Stripe Sync Engine installation failed.', error) throw error }) ) return res .status(200) .json({ data: { message: 'Stripe Sync setup initiated', version: VERSION }, error: null }) }