This commit is contained in:
pradeepkumar
2026-04-07 19:55:12 +05:30
parent e7898536ec
commit 0dc13cb43a
2 changed files with 41 additions and 13 deletions

View File

@@ -97,19 +97,38 @@ export class StripeWebhookController {
return;
}
// Check if user already has an active subscription (prevent duplicates)
// Check if user already has an active subscription with this same Stripe ID
// (prevent true duplicates only — new Stripe subscriptions for same user are allowed
// because createCheckoutSession cleanup handles stale records)
const existingActive = await this.subscriptionService.getActivePlan(userId);
if (existingActive) {
this.logger.log(`User ${userId} already has active subscription ${existingActive.id}, skipping duplicate`);
if (existingActive && existingActive.stripeSubscriptionId === stripeSubscriptionId) {
this.logger.log(`User ${userId} already has this exact active subscription, skipping duplicate`);
return;
}
// Fetch full subscription from Stripe to get period dates immediately
let currentPeriodStart: Date | undefined;
let currentPeriodEnd: Date | undefined;
try {
const stripeSub: any = await this.stripeService.getStripe().subscriptions.retrieve(stripeSubscriptionId);
if (stripeSub.current_period_start) {
currentPeriodStart = new Date(stripeSub.current_period_start * 1000);
}
if (stripeSub.current_period_end) {
currentPeriodEnd = new Date(stripeSub.current_period_end * 1000);
}
} catch (err: any) {
this.logger.warn(`Could not fetch Stripe subscription ${stripeSubscriptionId}: ${err.message}`);
}
await this.subscriptionService.createSubscription({
userId,
planId,
stripeCustomerId,
stripeSubscriptionId,
status: SubscriptionStatus.ACTIVE,
currentPeriodStart,
currentPeriodEnd,
});
this.logger.log(`Created subscription for user ${userId}, plan ${planId}`);

View File

@@ -24,7 +24,7 @@ export class StripeService {
}
}
private getStripe(): Stripe {
getStripe(): Stripe {
if (!this.stripe) {
throw new BadRequestException(
'Stripe is not configured. Please set STRIPE_SECRET_KEY in environment.',
@@ -71,21 +71,31 @@ export class StripeService {
throw new BadRequestException('Invalid or inactive plan');
}
// Check if user already has an active subscription
const activeSubscription = await this.prisma.agentSubscription.findFirst({
where: { userId, status: 'ACTIVE' },
// Check if user has a VALID active subscription (ACTIVE with valid period end in the future)
const now = new Date();
const validActive = await this.prisma.agentSubscription.findFirst({
where: {
userId,
status: 'ACTIVE',
currentPeriodEnd: { gt: now },
},
});
if (activeSubscription) {
if (validActive) {
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
// Clean up stale subscriptions (failed states OR ACTIVE with no period / expired period)
// This handles the case where checkout completed but invoice.paid never fired,
// leaving an orphaned ACTIVE record without proper period dates
const staleSubscriptions = await this.prisma.agentSubscription.findMany({
where: {
userId,
status: { in: ['INCOMPLETE', 'UNPAID', 'PAST_DUE'] },
OR: [
{ status: { in: ['INCOMPLETE', 'UNPAID', 'PAST_DUE', 'CANCELED'] } },
{ status: 'ACTIVE', currentPeriodEnd: null },
{ status: 'ACTIVE', currentPeriodEnd: { lte: now } },
],
},
});
@@ -101,8 +111,7 @@ export class StripeService {
}
await this.prisma.agentSubscription.deleteMany({
where: {
userId,
status: { in: ['INCOMPLETE', 'UNPAID', 'PAST_DUE'] },
id: { in: staleSubscriptions.map((s) => s.id) },
},
});
this.logger.log(`Cleaned up ${staleSubscriptions.length} stale subscription(s) for user ${userId}`);