From ca9a70ced580c5a73c9b1c0e2672c256ad000858 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Mon, 14 Jul 2025 12:31:06 +1000 Subject: [PATCH] fix: handle trials and resubscribing (#1897) --- .../early-adopter-checkout-metadata.ts | 13 -------- .../stripe/webhook/on-subscription-created.ts | 32 +++++++++++++++---- .../stripe/webhook/on-subscription-updated.ts | 8 ++++- 3 files changed, 33 insertions(+), 20 deletions(-) delete mode 100644 packages/ee/server-only/stripe/webhook/early-adopter-checkout-metadata.ts diff --git a/packages/ee/server-only/stripe/webhook/early-adopter-checkout-metadata.ts b/packages/ee/server-only/stripe/webhook/early-adopter-checkout-metadata.ts deleted file mode 100644 index 7d8c65a09..000000000 --- a/packages/ee/server-only/stripe/webhook/early-adopter-checkout-metadata.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from 'zod'; - -export const ZEarlyAdopterCheckoutMetadataSchema = z.object({ - name: z.string(), - email: z.string(), - signatureText: z.string(), - signatureDataUrl: z.string().optional(), - source: z.literal('marketing'), -}); - -export type TEarlyAdopterCheckoutMetadataSchema = z.infer< - typeof ZEarlyAdopterCheckoutMetadataSchema ->; diff --git a/packages/ee/server-only/stripe/webhook/on-subscription-created.ts b/packages/ee/server-only/stripe/webhook/on-subscription-created.ts index 21e659518..bf1f10f3c 100644 --- a/packages/ee/server-only/stripe/webhook/on-subscription-created.ts +++ b/packages/ee/server-only/stripe/webhook/on-subscription-created.ts @@ -81,17 +81,34 @@ export const onSubscriptionCreated = async ({ subscription }: OnSubscriptionCrea const status = match(subscription.status) .with('active', () => SubscriptionStatus.ACTIVE) + .with('trialing', () => SubscriptionStatus.ACTIVE) .with('past_due', () => SubscriptionStatus.PAST_DUE) .otherwise(() => SubscriptionStatus.INACTIVE); - await prisma.subscription.create({ - data: { + const periodEnd = + subscription.status === 'trialing' && subscription.trial_end + ? new Date(subscription.trial_end * 1000) + : new Date(subscription.current_period_end * 1000); + + await prisma.subscription.upsert({ + where: { + organisationId, + }, + create: { organisationId, status, customerId, planId: subscription.id, priceId: subscription.items.data[0].price.id, - periodEnd: new Date(subscription.current_period_end * 1000), + periodEnd, + cancelAtPeriodEnd: subscription.cancel_at_period_end, + }, + update: { + status, + customerId, + planId: subscription.id, + priceId: subscription.items.data[0].price.id, + periodEnd, cancelAtPeriodEnd: subscription.cancel_at_period_end, }, }); @@ -172,14 +189,17 @@ const handleOrganisationUpdate = async ({ customerId, claim }: HandleOrganisatio } // Todo: logging - if (organisation.subscription) { - console.error('Organisation already has a subscription'); + if ( + organisation.subscription && + organisation.subscription.status !== SubscriptionStatus.INACTIVE + ) { + console.error('Organisation already has an active subscription'); // This should never happen throw Response.json( { success: false, - message: `Organisation already has a subscription`, + message: `Organisation already has an active subscription`, } satisfies StripeWebhookResponse, { status: 500 }, ); diff --git a/packages/ee/server-only/stripe/webhook/on-subscription-updated.ts b/packages/ee/server-only/stripe/webhook/on-subscription-updated.ts index 601adca51..128fe3a8d 100644 --- a/packages/ee/server-only/stripe/webhook/on-subscription-updated.ts +++ b/packages/ee/server-only/stripe/webhook/on-subscription-updated.ts @@ -83,9 +83,15 @@ export const onSubscriptionUpdated = async ({ const status = match(subscription.status) .with('active', () => SubscriptionStatus.ACTIVE) + .with('trialing', () => SubscriptionStatus.ACTIVE) .with('past_due', () => SubscriptionStatus.PAST_DUE) .otherwise(() => SubscriptionStatus.INACTIVE); + const periodEnd = + subscription.status === 'trialing' && subscription.trial_end + ? new Date(subscription.trial_end * 1000) + : new Date(subscription.current_period_end * 1000); + await prisma.$transaction(async (tx) => { await tx.subscription.update({ where: { @@ -96,7 +102,7 @@ export const onSubscriptionUpdated = async ({ status: status, planId: subscription.id, priceId: subscription.items.data[0].price.id, - periodEnd: new Date(subscription.current_period_end * 1000), + periodEnd, cancelAtPeriodEnd: subscription.cancel_at_period_end, }, });