fix: update stripe team member billing (#2991)

This commit is contained in:
David Nguyen
2026-07-23 14:09:06 +10:00
committed by GitHub
parent 26f0c4c5b7
commit 54befb5962
17 changed files with 674 additions and 309 deletions
@@ -1,9 +1,7 @@
import { syncMemberCountWithStripeSeatPlan } from '@documenso/ee/server-only/stripe/update-subscription-item-quantity';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobs } from '@documenso/lib/jobs/client';
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
import { prisma } from '@documenso/prisma';
import { OrganisationMemberInviteStatus } from '@documenso/prisma/client';
import { authenticatedProcedure } from '../trpc';
import { ZLeaveOrganisationRequestSchema, ZLeaveOrganisationResponseSchema } from './leave-organisation.types';
@@ -24,26 +22,11 @@ export const leaveOrganisationRoute = authenticatedProcedure
const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ organisationId, userId }),
include: {
organisationClaim: true,
subscription: true,
teams: {
select: {
id: true,
},
},
invites: {
where: {
status: OrganisationMemberInviteStatus.PENDING,
},
select: {
id: true,
},
},
members: {
select: {
id: true,
},
},
},
});
@@ -59,17 +42,6 @@ export const leaveOrganisationRoute = authenticatedProcedure
});
}
const { organisationClaim } = organisation;
const inviteCount = organisation.invites.length;
const newMemberCount = organisation.members.length + inviteCount - 1;
// Leaving is a reducing operation, so we don't gate it on the subscription
// being present. Sync Stripe only when one exists.
if (organisation.subscription) {
await syncMemberCountWithStripeSeatPlan(organisation.subscription, organisationClaim, newMemberCount);
}
const teamIds = organisation.teams.map((team) => team.id);
await prisma.$transaction(async (tx) => {
@@ -101,6 +73,13 @@ export const leaveOrganisationRoute = authenticatedProcedure
});
});
// A member was removed — queue a seat sync to true the Stripe quantity down
// to the new count (no proration, no credit).
await jobs.triggerJob({
name: 'internal.sync-organisation-seats',
payload: { organisationId },
});
await jobs.triggerJob({
name: 'send.organisation-member-left.email',
payload: {