Files
Momento/memento-note/lib/billing/cancel-subscription.ts

67 lines
2.1 KiB
TypeScript

import { prisma } from '@/lib/prisma';
import { stripe } from '@/lib/stripe';
export interface CancelSubscriptionResult {
success: boolean;
error?: string;
}
/**
* Cancels an active user subscription by setting its auto-renew (cancel_at_period_end) status to true
* in Stripe and synchronizing the status back into the local Prisma database.
*
* @param userId - Unique identifier of the user
* @returns An object indicating the success or failure of the operation
*/
export async function cancelSubscription(userId: string): Promise<CancelSubscriptionResult> {
if (!userId) {
return { success: false, error: 'User ID is required' };
}
try {
const subscription = await prisma.subscription.findUnique({
where: { userId },
});
if (!subscription) {
return { success: false, error: 'No active subscription found' };
}
if (subscription.stripeSubscriptionId) {
// Direct call to Stripe API with period end cancellation set to true
const updatedSub = await stripe.subscriptions.update(subscription.stripeSubscriptionId, {
cancel_at_period_end: true,
});
// Local database synchronization
await prisma.subscription.update({
where: { userId },
data: {
cancelAtPeriodEnd: true,
canceledAt: updatedSub.canceled_at ? new Date(updatedSub.canceled_at * 1000) : new Date(),
currentPeriodEnd: updatedSub.current_period_end ? new Date(updatedSub.current_period_end * 1000) : subscription.currentPeriodEnd,
updatedAt: new Date(),
},
});
} else {
// Mock mode cancel (e.g. for development / local bypass testing)
await prisma.subscription.update({
where: { userId },
data: {
cancelAtPeriodEnd: true,
canceledAt: new Date(),
updatedAt: new Date(),
},
});
}
return { success: true };
} catch (error: any) {
console.error('[cancelSubscription] Error processing cancellation:', error);
return {
success: false,
error: error.message || 'Failed to cancel subscription',
};
}
}