stripe-sync.ts150 lines · main
1// @ts-nocheck
2import { waitUntil } from '@vercel/functions'
3import { NextApiRequest, NextApiResponse } from 'next'
4import { VERSION } from 'stripe-experiment-sync'
5import { install, uninstall } from 'stripe-experiment-sync/supabase'
6import { z } from 'zod'
7
8const InstallBodySchema = z.object({
9 projectRef: z.string().min(1),
10 stripeSecretKey: z.string().min(1),
11 startTime: z.number().positive().optional(),
12})
13
14const UninstallBodySchema = z.object({
15 projectRef: z.string().min(1),
16 startTime: z.number().positive().optional(),
17})
18
19async function isStripeSyncEnabled() {
20 // The ConfigClient doesn't seem to work properly so we'll just gate access from the frontend
21 // for now
22 return true
23}
24
25function getBearerToken(req: NextApiRequest) {
26 const authHeader = req.headers.authorization
27 if (!authHeader || Array.isArray(authHeader)) return null
28 const match = authHeader.match(/^Bearer\s+(.+)$/i)
29 return match?.[1]?.trim() ?? null
30}
31
32export const config = {
33 maxDuration: 300, // 5 minutes, since the installation process can take a while even if happening in background
34}
35
36export default async function handler(req: NextApiRequest, res: NextApiResponse) {
37 // Hide endpoint if the integration is disabled.
38 if (!(await isStripeSyncEnabled())) {
39 return res.status(404).json({ data: null, error: { message: 'Not Found' } })
40 }
41
42 const { method } = req
43 switch (method) {
44 case 'POST':
45 return handleSetupStripeSyncInstall(req, res)
46 case 'DELETE':
47 return handleDeleteStripeSyncInstall(req, res)
48 default:
49 return res
50 .status(405)
51 .json({ data: null, error: { message: `Method ${method} Not Allowed` } })
52 }
53}
54
55async function handleDeleteStripeSyncInstall(req: NextApiRequest, res: NextApiResponse) {
56 const brivenToken = getBearerToken(req)
57 if (!brivenToken) {
58 return res
59 .status(401)
60 .json({ data: null, error: { message: 'Unauthorized: Invalid Authorization header' } })
61 }
62
63 const parsed = UninstallBodySchema.safeParse(req.body)
64 if (!parsed.success) {
65 return res
66 .status(400)
67 .json({ data: null, error: { message: 'Bad Request: Invalid request body' } })
68 }
69 const { projectRef, startTime } = parsed.data
70
71 waitUntil(
72 uninstall({
73 brivenAccessToken: brivenToken,
74 brivenProjectRef: projectRef,
75 baseProjectUrl: process.env.NEXT_PUBLIC_CUSTOMER_DOMAIN,
76 brivenManagementUrl: process.env.NEXT_PUBLIC_API_DOMAIN,
77 startTime,
78 }).catch((error) => {
79 console.error('Stripe Sync Engine uninstallation failed.', error)
80 throw error
81 })
82 )
83
84 return res
85 .status(200)
86 .json({ data: { message: 'Stripe Sync uninstallation initiated' }, error: null })
87}
88
89async function handleSetupStripeSyncInstall(req: NextApiRequest, res: NextApiResponse) {
90 const brivenToken = getBearerToken(req)
91 if (!brivenToken) {
92 return res
93 .status(401)
94 .json({ data: null, error: { message: 'Unauthorized: Invalid Authorization header' } })
95 }
96
97 const parsed = InstallBodySchema.safeParse(req.body)
98 if (!parsed.success) {
99 return res
100 .status(400)
101 .json({ data: null, error: { message: 'Bad Request: Invalid request body' } })
102 }
103 const { projectRef, stripeSecretKey, startTime } = parsed.data
104
105 // Validate the Stripe API key before proceeding with installation
106 try {
107 const stripeResponse = await fetch('https://api.stripe.com/v1/account', {
108 method: 'GET',
109 headers: {
110 Authorization: `Bearer ${stripeSecretKey}`,
111 'Content-Type': 'application/x-www-form-urlencoded',
112 },
113 })
114
115 if (!stripeResponse.ok) {
116 const errorData = await stripeResponse.json()
117 const errorMessage =
118 errorData.error?.message || `Invalid Stripe API key (HTTP ${stripeResponse.status})`
119 return res.status(400).json({
120 data: null,
121 error: { message: errorMessage },
122 })
123 }
124 } catch (error) {
125 const normalizedErrorMessage = error instanceof Error ? error.message : String(error)
126
127 return res.status(400).json({
128 data: null,
129 error: { message: `Failed to validate Stripe API key: ${normalizedErrorMessage}` },
130 })
131 }
132 waitUntil(
133 install({
134 brivenAccessToken: brivenToken,
135 brivenProjectRef: projectRef,
136 stripeKey: stripeSecretKey,
137 baseProjectUrl: process.env.NEXT_PUBLIC_CUSTOMER_DOMAIN,
138 brivenManagementUrl: process.env.NEXT_PUBLIC_API_DOMAIN,
139 packageVersion: VERSION,
140 startTime,
141 }).catch((error) => {
142 console.error('Stripe Sync Engine installation failed.', error)
143 throw error
144 })
145 )
146
147 return res
148 .status(200)
149 .json({ data: { message: 'Stripe Sync setup initiated', version: VERSION }, error: null })
150}