🔧 Introduce new "unpaid" subscription status mirror in webhook handler

This commit is contained in:
Baptiste Arnaud 2025-08-25 17:20:58 +02:00
parent 4193512f1c
commit 4ea6903dd8
No known key found for this signature in database
2 changed files with 21 additions and 68 deletions

View File

@ -131,6 +131,7 @@ export const webhookHandler = async (
},
select: {
isPastDue: true,
isQuarantined: true,
id: true,
plan: true,
members: {
@ -201,10 +202,28 @@ export const webhookHandler = async (
return res.send({ message: "Workspace set to past due." });
}
if (
subscription.status === "unpaid" &&
previous &&
previous.status !== "unpaid" &&
!existingWorkspace.isQuarantined
) {
await prisma.workspace.updateMany({
where: {
id: existingWorkspace.id,
},
data: {
isQuarantined: true,
},
});
return res.send({ message: "Workspace quarantined" });
}
if (
subscription.status === "active" &&
previous &&
previous.status === "past_due" &&
(previous.status === "past_due" || previous?.status === "unpaid") &&
existingWorkspace.isPastDue
) {
await prisma.workspace.updateMany({
@ -230,73 +249,6 @@ export const webhookHandler = async (
return res.send({ message: "Nothing to do" });
}
case "invoice.payment_failed": {
const invoice = event.data.object;
if (invoice.collection_method !== "charge_automatically")
return res.send({
message: "Manual payment required",
});
const stripeId =
typeof invoice.customer === "string"
? invoice.customer
: invoice.customer?.id;
if (!stripeId) {
throw new Error("Stripe ID not found");
}
const existingWorkspace = await prisma.workspace.findFirst({
where: {
stripeId,
},
select: {
id: true,
plan: true,
isPastDue: true,
isQuarantined: true,
members: {
select: { userId: true, role: true },
where: { role: WorkspaceRole.ADMIN },
},
},
});
if (!existingWorkspace) {
return res.send({
message: "Workspace not found for failed invoice",
});
}
if (existingWorkspace.isQuarantined) {
return res.send({
message: "Workspace is already quarantined",
});
}
if (invoice.next_payment_attempt === null) {
if (!existingWorkspace.isQuarantined) {
await prisma.workspace.updateMany({
where: {
id: existingWorkspace.id,
},
data: {
isQuarantined: true,
isPastDue: true,
},
});
}
return res.send({
message:
"Invoice payment permanently failed - workspace marked as past due",
});
}
return res.send({
message: `Invoice payment failed (attempt ${invoice.attempt_count}) - retries may continue`,
});
}
case "customer.subscription.deleted": {
const subscription = event.data.object as Stripe.Subscription;
const { data } = await stripe.subscriptions.list({

View File

@ -4,6 +4,7 @@ export const resetBillingProps = async () => {
console.log("Resetting billing props...");
const { count } = await prisma.workspace.updateMany({
where: {
isPastDue: { not: true },
OR: [
{
isQuarantined: true,