fix
This commit is contained in:
@@ -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}`);
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user