fix(billing): only reactivate suspended workspaces when subscription is in good standing (#22687)
## Context
When a subscription's trial ends without a valid payment method, Stripe
emits
`customer.subscription.updated` (`active → past_due`) within seconds of
`trial_end`.
Our webhook handler correctly suspends the workspace for this event,
because it lands
inside the 24h "trial just ended" window checked by
`shouldSuspendWorkspace`.
However, the workspace does not *stay* suspended if an other
subscription update event arrived.
## Problem
Reactivation was gated only on the negation of the suspend heuristic:
```ts
} else if (workspace.activationStatus === WorkspaceActivationStatus.SUSPENDED) {
await this.workspaceService.reactivateWorkspace(workspaceId);
}
<!-- This is an auto-generated description by cubic. -->
<a href="https://cubic.dev/pr/twentyhq/twenty/pull/22687?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
+18
-3
@@ -150,9 +150,7 @@ export class BillingWebhookSubscriptionService {
|
||||
'currentBillingSubscription',
|
||||
]);
|
||||
|
||||
const shouldSuspend = this.shouldSuspendWorkspace(data);
|
||||
|
||||
if (shouldSuspend) {
|
||||
if (this.shouldSuspendWorkspace(data)) {
|
||||
if (workspace.activationStatus === WorkspaceActivationStatus.ACTIVE) {
|
||||
await this.workspaceService.suspendWorkspace(workspaceId);
|
||||
} else if (
|
||||
@@ -162,6 +160,7 @@ export class BillingWebhookSubscriptionService {
|
||||
await this.workspaceService.deleteWorkspace(workspace.id);
|
||||
}
|
||||
} else if (
|
||||
this.shouldReactivateWorkspace(data) &&
|
||||
workspace.activationStatus === WorkspaceActivationStatus.SUSPENDED
|
||||
) {
|
||||
await this.workspaceService.reactivateWorkspace(workspaceId);
|
||||
@@ -216,6 +215,22 @@ export class BillingWebhookSubscriptionService {
|
||||
);
|
||||
}
|
||||
|
||||
shouldReactivateWorkspace(
|
||||
data:
|
||||
| Stripe.CustomerSubscriptionUpdatedEvent.Data
|
||||
| Stripe.CustomerSubscriptionCreatedEvent.Data
|
||||
| Stripe.CustomerSubscriptionDeletedEvent.Data,
|
||||
): boolean {
|
||||
const status = data.object.status as SubscriptionStatus;
|
||||
|
||||
const activeStatuses = [
|
||||
SubscriptionStatus.Active,
|
||||
SubscriptionStatus.Trialing,
|
||||
];
|
||||
|
||||
return activeStatuses.includes(status);
|
||||
}
|
||||
|
||||
async updateBillingSubscriptionItems(
|
||||
subscriptionId: string,
|
||||
event:
|
||||
|
||||
Reference in New Issue
Block a user