diff --git a/app/v1/installations/[installationId]/route.ts b/app/v1/installations/[installationId]/route.ts index 28ad334..2c1609c 100644 --- a/app/v1/installations/[installationId]/route.ts +++ b/app/v1/installations/[installationId]/route.ts @@ -55,6 +55,7 @@ export const GET = withAuth(async (claims) => { (plan) => plan.id === installation.billingPlanId ); return Response.json({ + metadata: installation.metadata, billingPlan: { ...billingPlan, scope: "installation", @@ -72,10 +73,10 @@ export const PATCH = withAuth(async (claims, request) => { return new Response(null, { status: 400 }); } - await updateInstallation( - claims.installation_id, - requestBody.data.billingPlanId - ); + await updateInstallation(claims.installation_id, { + billingPlanId: requestBody.data.billingPlanId, + metadata: requestBody.data.metadata, + }); return new Response(null, { status: 204 }); }); diff --git a/app/v1/installations/new/plans/route.ts b/app/v1/installations/new/plans/route.ts new file mode 100644 index 0000000..8666ff5 --- /dev/null +++ b/app/v1/installations/new/plans/route.ts @@ -0,0 +1,7 @@ +import { getNewInstallationtBillingPlans } from "@/lib/partner"; +import { withAuth } from "@/lib/vercel/auth"; + +export const GET = withAuth(async () => { + const billingPlans = await getNewInstallationtBillingPlans(); + return Response.json(billingPlans); +}); diff --git a/lib/partner/index.ts b/lib/partner/index.ts index b425853..1ae34e0 100644 --- a/lib/partner/index.ts +++ b/lib/partner/index.ts @@ -85,11 +85,18 @@ export async function installIntegration( export async function updateInstallation( installationId: string, - billingPlanId: string + { + billingPlanId, + metadata, + }: { billingPlanId: string; metadata?: Record } ): Promise { const installation = await getInstallation(installationId); const pipeline = kv.pipeline(); - await pipeline.set(installationId, { ...installation, billingPlanId }); + await pipeline.set(installationId, { + ...installation, + billingPlanId, + metadata: metadata !== undefined ? metadata : installation.metadata, + }); await pipeline.exec(); } @@ -140,7 +147,9 @@ export async function provisionResource( serializeResource(resource) ); await kv.lpush(`${installationId}:resources`, resource.id); - await updateInstallation(installationId, request.billingPlanId); + await updateInstallation(installationId, { + billingPlanId: request.billingPlanId, + }); return { ...resource, @@ -421,6 +430,14 @@ export async function getInstallationtBillingPlans( }; } +export async function getNewInstallationtBillingPlans( + _experimental_metadata?: Record +): Promise { + return { + plans: billingPlans, + }; +} + export async function getProductBillingPlans( _productId: string, installationId: string, @@ -446,6 +463,7 @@ export async function getInstallation(installationId: string): Promise< InstallIntegrationRequest & { type: "marketplace" | "external"; billingPlanId: string; + metadata?: Record; deletedAt?: number; } > { @@ -453,6 +471,7 @@ export async function getInstallation(installationId: string): Promise< InstallIntegrationRequest & { type: "marketplace" | "external"; billingPlanId: string; + metadata?: Record; deletedAt?: number; } >(installationId); diff --git a/lib/vercel/auth.ts b/lib/vercel/auth.ts index b86fdfa..7127f3c 100644 --- a/lib/vercel/auth.ts +++ b/lib/vercel/auth.ts @@ -50,7 +50,7 @@ export async function verifyToken(token: string): Promise { ); if (claims.aud !== env.INTEGRATION_CLIENT_ID) { - throw new AuthError("Invalid audience"); + throw new AuthError(`Invalid audience: ${claims.aud}`); } if (claims.iss !== "https://marketplace.vercel.com") { diff --git a/lib/vercel/schemas.ts b/lib/vercel/schemas.ts index f748c73..b3e6b65 100644 --- a/lib/vercel/schemas.ts +++ b/lib/vercel/schemas.ts @@ -21,6 +21,8 @@ export const usageTypeSchema = z.enum(["total", "interval", "rate"]); export type UsageType = z.infer; +const metadataSchema = z.record(z.unknown()); + // Account and Installation export const installIntegrationRequestSchema = z.object({ @@ -30,10 +32,13 @@ export const installIntegrationRequestSchema = z.object({ access_token: z.string().min(1), token_type: z.string().min(1), }), + billingPlanId: z.string().optional(), + metadata: metadataSchema.optional(), }); export const updateInstallationRequestSchema = z.object({ billingPlanId: z.string(), + metadata: metadataSchema.optional(), }); export type InstallIntegrationRequest = z.infer< @@ -169,8 +174,6 @@ export type ProvisionPurchaseResponse = z.infer< // Product -const metadataSchema = z.record(z.unknown()); - const notificationSchema = z.object({ level: z.enum(["info", "warn", "error"]), title: z.string().max(100),