diff --git a/apps/web/src/app/api/licensing/v1/[...route]/route.ts b/apps/web/src/app/api/licensing/v1/[...route]/route.ts index d08bdf43d..3f1f518f2 100644 --- a/apps/web/src/app/api/licensing/v1/[...route]/route.ts +++ b/apps/web/src/app/api/licensing/v1/[...route]/route.ts @@ -46,15 +46,8 @@ if (env.LICENSE_API_AUTH_TOKEN) { }), ), async (c) => { - const { - type, - seats, - expiresAt, - licenseeEmail, - licenseeName, - version, - stripeCustomerId, - } = c.req.valid("json"); + const { type, seats, expiresAt, licenseeEmail, licenseeName, version } = + c.req.valid("json"); try { const license = await prisma.license.create({ @@ -67,7 +60,6 @@ if (env.LICENSE_API_AUTH_TOKEN) { expiresAt, licenseeEmail, licenseeName, - stripeCustomerId, }, }); return c.json({ diff --git a/apps/web/src/app/api/stripe/buy-license/route.ts b/apps/web/src/app/api/stripe/buy-license/route.ts index 9b96d2dbf..225313252 100644 --- a/apps/web/src/app/api/stripe/buy-license/route.ts +++ b/apps/web/src/app/api/stripe/buy-license/route.ts @@ -63,6 +63,7 @@ export async function GET(request: NextRequest) { success_url: "https://rallly.co/licensing/thank-you", metadata: { licenseType: type, + version: 4, seats, } satisfies LicenseCheckoutMetadata, }); diff --git a/apps/web/src/app/api/stripe/webhook/handlers/checkout/completed.ts b/apps/web/src/app/api/stripe/webhook/handlers/checkout/completed.ts index 035310fd3..6cfacb9e2 100644 --- a/apps/web/src/app/api/stripe/webhook/handlers/checkout/completed.ts +++ b/apps/web/src/app/api/stripe/webhook/handlers/checkout/completed.ts @@ -1,3 +1,4 @@ +import { env } from "@/env"; import { licensingClient } from "@/features/licensing/client"; import { licenseCheckoutMetadataSchema } from "@/features/licensing/schema"; import { subscriptionCheckoutMetadataSchema } from "@/features/subscription/schema"; @@ -38,10 +39,11 @@ async function handleSelfHostedCheckoutSessionCompleted( if (!success) { // If there is no metadata than this is likely a donation from a payment link + console.info("No metadata found for session: ", checkoutSession.id); return; } - const { licenseType, seats } = data; + const { licenseType, version, seats } = data; const customerDetails = checkoutSession.customer_details; @@ -63,13 +65,13 @@ async function handleSelfHostedCheckoutSessionCompleted( type: licenseType, licenseeEmail: email, licenseeName: customerDetails.name ?? undefined, + version, seats, - stripeCustomerId: checkoutSession.customer as string, }); if (!license || !license.data) { throw new Error( - `Failed to create team license for session: ${checkoutSession.id} - ${license?.error}`, + `Failed to create license for session: ${checkoutSession.id} - ${license?.error}`, ); } @@ -79,7 +81,7 @@ async function handleSelfHostedCheckoutSessionCompleted( to: email, from: { name: "Luke from Rallly", - address: process.env.SUPPORT_EMAIL, + address: env.SUPPORT_EMAIL, }, props: { licenseKey: license.data.key, diff --git a/apps/web/src/features/licensing/lib/licensing-client.ts b/apps/web/src/features/licensing/lib/licensing-client.ts index f74cc6658..ee51b31dd 100644 --- a/apps/web/src/features/licensing/lib/licensing-client.ts +++ b/apps/web/src/features/licensing/lib/licensing-client.ts @@ -10,7 +10,7 @@ export class LicensingClient { authToken?: string; constructor({ - apiUrl = "https://licensing.rallly.co", + apiUrl = "https://licensing.rallly.co/api/licensing/v1", authToken, }: { apiUrl?: string; @@ -24,7 +24,7 @@ export class LicensingClient { throw new Error("Licensing API auth token is not configured."); } - const res = await fetch(`${this.apiUrl}/api/v1/licenses`, { + const res = await fetch(`${this.apiUrl}/licenses`, { method: "POST", headers: { "Content-Type": "application/json", @@ -39,16 +39,13 @@ export class LicensingClient { return createLicenseResponseSchema.parse(await res.json()); } async validateLicenseKey(input: ValidateLicenseInputKeySchema) { - const res = await fetch( - `${this.apiUrl}/api/v1/licenses/actions/validate-key`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(input), + const res = await fetch(`${this.apiUrl}/licenses/actions/validate-key`, { + method: "POST", + headers: { + "Content-Type": "application/json", }, - ); + body: JSON.stringify(input), + }); if (!res.ok) { throw new Error("Failed to validate license key."); diff --git a/apps/web/src/features/licensing/schema.ts b/apps/web/src/features/licensing/schema.ts index 1cd638c46..96e2884a0 100644 --- a/apps/web/src/features/licensing/schema.ts +++ b/apps/web/src/features/licensing/schema.ts @@ -31,12 +31,11 @@ export type ApiResponse = { export const createLicenseInputSchema = z.object({ type: licenseTypeSchema, - seats: z.number().optional(), - expiresAt: z.date().optional(), + seats: z.coerce.number().optional(), + expiresAt: z.coerce.date().optional(), licenseeEmail: z.string().optional(), licenseeName: z.string().optional(), - version: z.number().optional(), - stripeCustomerId: z.string().optional(), + version: z.coerce.number().optional(), }); export type CreateLicenseInput = z.infer; @@ -80,7 +79,8 @@ export type ValidateLicenseKeyResponse = z.infer< export const licenseCheckoutMetadataSchema = z.object({ licenseType: licenseTypeSchema, - seats: z.number(), + version: z.coerce.number(), + seats: z.coerce.number(), }); export type LicenseCheckoutMetadata = z.infer< diff --git a/packages/database/prisma/migrations/20250526175615_remove_stripe_customer_id/migration.sql b/packages/database/prisma/migrations/20250526175615_remove_stripe_customer_id/migration.sql new file mode 100644 index 000000000..9174cae28 --- /dev/null +++ b/packages/database/prisma/migrations/20250526175615_remove_stripe_customer_id/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `stripe_customer_id` on the `licenses` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "licenses" DROP COLUMN "stripe_customer_id"; diff --git a/packages/database/prisma/models/licensing.prisma b/packages/database/prisma/models/licensing.prisma index 1bae90e40..5aa27d8ca 100644 --- a/packages/database/prisma/models/licensing.prisma +++ b/packages/database/prisma/models/licensing.prisma @@ -20,7 +20,6 @@ model License { licenseeEmail String? @map("licensee_email") licenseeName String? @map("licensee_name") status LicenseStatus @default(ACTIVE) @map("status") - stripeCustomerId String? @map("stripe_customer_id") validations LicenseValidation[]