feat: Sync Stripe subscription period end and cancellation status with local records.

This commit is contained in:
pradeepkumar
2026-03-14 23:44:06 +05:30
parent 4e602abf49
commit 2c0d0f0aeb
2 changed files with 75 additions and 16 deletions

View File

@@ -42,14 +42,19 @@ export class StripeController {
@Post('create-checkout-session')
@ApiBearerAuth()
@ApiOperation({ summary: 'Create a Stripe Checkout session for subscription' })
@ApiOperation({
summary: 'Create a Stripe Checkout session for subscription',
})
@HttpCode(HttpStatus.OK)
async createCheckoutSession(
@CurrentUser() user: any,
@Body() dto: CreateCheckoutDto,
) {
const frontendUrl = this.configService.get<string>('app.frontendUrl') || 'http://localhost:3000';
const successUrl = dto.successUrl || `${frontendUrl}/agent/settings/billings/confirmation`;
const frontendUrl =
this.configService.get<string>('app.frontendUrl') ||
'http://localhost:3000';
const successUrl =
dto.successUrl || `${frontendUrl}/agent/settings/billings/confirmation`;
const cancelUrl = dto.cancelUrl || `${frontendUrl}/agent/settings/billings`;
return this.stripeService.createCheckoutSession(
@@ -73,7 +78,9 @@ export class StripeController {
throw new BadRequestException('No active subscription found');
}
const returnUrl = dto.returnUrl || `${this.configService.get<string>('app.frontendUrl') || 'http://localhost:3000'}/agent/settings/billings`;
const returnUrl =
dto.returnUrl ||
`${this.configService.get<string>('app.frontendUrl') || 'http://localhost:3000'}/agent/settings/billings`;
return this.stripeService.createBillingPortalSession(
subscription.stripeCustomerId,
returnUrl,
@@ -85,7 +92,35 @@ export class StripeController {
@ApiOperation({ summary: 'Get current user subscription status' })
async getSubscription(@CurrentUser() user: any) {
const subscription = await this.subscriptionService.getActivePlan(user.id);
return subscription || null;
if (!subscription) return null;
// Sync from Stripe if key fields are missing
if (
subscription.stripeSubscriptionId &&
(!subscription.currentPeriodEnd ||
subscription.cancelAtPeriodEnd === null)
) {
try {
const stripeSub = await this.stripeService.getSubscription(
subscription.stripeSubscriptionId,
);
const updated = await this.subscriptionService.updateSubscriptionStatus(
subscription.stripeSubscriptionId,
subscription.status,
{
currentPeriodEnd: new Date(
stripeSub.items.data[0].current_period_end * 1000,
),
cancelAtPeriodEnd: stripeSub.cancel_at_period_end,
},
);
return updated;
} catch {
// Fall through to return existing data
}
}
return subscription;
}
@Post('cancel-subscription')
@@ -98,12 +133,20 @@ export class StripeController {
throw new BadRequestException('No active subscription found');
}
await this.stripeService.cancelSubscription(subscription.stripeSubscriptionId, true);
const stripeSubscription = await this.stripeService.cancelSubscription(
subscription.stripeSubscriptionId,
true,
);
return this.subscriptionService.updateSubscriptionStatus(
subscription.stripeSubscriptionId,
'ACTIVE',
{ cancelAtPeriodEnd: true },
{
cancelAtPeriodEnd: true,
currentPeriodEnd: new Date(
stripeSubscription.items.data[0].current_period_end * 1000,
),
},
);
}

View File

@@ -18,13 +18,17 @@ export class StripeService {
apiVersion: '2025-02-24.acacia' as any,
});
} else {
this.logger.warn('Stripe secret key not configured. Stripe features will be unavailable.');
this.logger.warn(
'Stripe secret key not configured. Stripe features will be unavailable.',
);
}
}
private getStripe(): Stripe {
if (!this.stripe) {
throw new BadRequestException('Stripe is not configured. Please set STRIPE_SECRET_KEY in environment.');
throw new BadRequestException(
'Stripe is not configured. Please set STRIPE_SECRET_KEY in environment.',
);
}
return this.stripe;
}
@@ -90,7 +94,11 @@ export class StripeService {
? `${user.agentProfile.firstName || ''} ${user.agentProfile.lastName || ''}`.trim()
: undefined;
const customerId = await this.createCustomer(userId, user.email, customerName);
const customerId = await this.createCustomer(
userId,
user.email,
customerName,
);
const session = await this.getStripe().checkout.sessions.create({
customer: customerId,
@@ -144,11 +152,19 @@ export class StripeService {
return this.getStripe().subscriptions.cancel(stripeSubscriptionId);
}
constructWebhookEvent(
payload: Buffer,
signature: string,
): Stripe.Event {
const webhookSecret = this.configService.get<string>('stripe.webhookSecret') || '';
return this.getStripe().webhooks.constructEvent(payload, signature, webhookSecret);
async getSubscription(
stripeSubscriptionId: string,
): Promise<Stripe.Subscription> {
return this.getStripe().subscriptions.retrieve(stripeSubscriptionId);
}
constructWebhookEvent(payload: Buffer, signature: string): Stripe.Event {
const webhookSecret =
this.configService.get<string>('stripe.webhookSecret') || '';
return this.getStripe().webhooks.constructEvent(
payload,
signature,
webhookSecret,
);
}
}