fix: prevent duplicate subscriptions by validating active status in webhook and cleaning up stale records during creation
This commit is contained in:
@@ -91,12 +91,19 @@ export class StripeWebhookController {
|
||||
const stripeCustomerId = session.customer as string;
|
||||
|
||||
// Check if subscription already exists (idempotency)
|
||||
const existing = await this.subscriptionService.getSubscriptionByStripeId(stripeSubscriptionId);
|
||||
if (existing) {
|
||||
const existingByStripeId = await this.subscriptionService.getSubscriptionByStripeId(stripeSubscriptionId);
|
||||
if (existingByStripeId) {
|
||||
this.logger.log(`Subscription ${stripeSubscriptionId} already exists, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user already has an active subscription (prevent duplicates)
|
||||
const existingActive = await this.subscriptionService.getActivePlan(userId);
|
||||
if (existingActive) {
|
||||
this.logger.log(`User ${userId} already has active subscription ${existingActive.id}, skipping duplicate`);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.subscriptionService.createSubscription({
|
||||
userId,
|
||||
planId,
|
||||
|
||||
@@ -80,6 +80,34 @@ export class StripeService {
|
||||
throw new BadRequestException('You already have an active subscription');
|
||||
}
|
||||
|
||||
// Clean up any incomplete/failed subscriptions to prevent duplicates
|
||||
// when user retries payment after a previous failure
|
||||
const staleSubscriptions = await this.prisma.agentSubscription.findMany({
|
||||
where: {
|
||||
userId,
|
||||
status: { in: ['INCOMPLETE', 'UNPAID', 'PAST_DUE'] },
|
||||
},
|
||||
});
|
||||
|
||||
if (staleSubscriptions.length > 0) {
|
||||
// Cancel stale Stripe subscriptions before deleting DB records
|
||||
for (const sub of staleSubscriptions) {
|
||||
if (!sub.stripeSubscriptionId) continue;
|
||||
try {
|
||||
await this.getStripe().subscriptions.cancel(sub.stripeSubscriptionId);
|
||||
} catch (err: any) {
|
||||
this.logger.warn(`Could not cancel stale Stripe subscription ${sub.stripeSubscriptionId}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
await this.prisma.agentSubscription.deleteMany({
|
||||
where: {
|
||||
userId,
|
||||
status: { in: ['INCOMPLETE', 'UNPAID', 'PAST_DUE'] },
|
||||
},
|
||||
});
|
||||
this.logger.log(`Cleaned up ${staleSubscriptions.length} stale subscription(s) for user ${userId}`);
|
||||
}
|
||||
|
||||
// Get or create Stripe customer
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
|
||||
Reference in New Issue
Block a user