import { NextRequest, NextResponse } from 'next/server'; import { auth } from '@/auth'; import { getUserInfo, getEffectiveTier } from '@/lib/entitlements'; import { stripe } from '@/lib/stripe'; import { getDynamicPrices, isBillingEnabled } from '@/lib/billing/stripe-prices'; import { syncSubscriptionFromStripe } from '@/lib/billing/sync-subscription-from-stripe'; import { shouldOfferSubscriptionTrial, SUBSCRIPTION_TRIAL_DAYS } from '@/lib/billing/trial'; export const dynamic = 'force-dynamic'; export async function GET(req: NextRequest) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const userId = session.user.id; const { prisma } = await import('@/lib/prisma'); const sessionId = req.nextUrl.searchParams.get('session_id'); if (sessionId && sessionId.startsWith('cs_')) { try { const checkoutSession = await stripe.checkout.sessions.retrieve(sessionId); if (checkoutSession.subscription && checkoutSession.status === 'complete') { const subId = typeof checkoutSession.subscription === 'string' ? checkoutSession.subscription : (checkoutSession.subscription as { id: string }).id; const sub = await stripe.subscriptions.retrieve(subId); const syncUserId = (checkoutSession.metadata?.userId as string | undefined) ?? (sub.metadata?.userId as string | undefined) ?? userId; if (syncUserId === userId) { await syncSubscriptionFromStripe(sub, userId); } } } catch (err) { console.error('[billing/status] Failed to sync Stripe session:', err); } } try { const { tier, status, currentPeriodEnd } = await getUserInfo(userId); const effectiveTier = await getEffectiveTier(userId); const subscription = await prisma.subscription.findUnique({ where: { userId } }); const prices = await getDynamicPrices(); const billingEnabled = await isBillingEnabled(); const { getPackPublicPrices } = await import('@/lib/billing/credit-packs'); const creditPacks = await getPackPublicPrices(); // Si retour checkout pack : créditer au cas où le webhook n'a pas encore tourné if (sessionId && sessionId.startsWith('cs_')) { try { const checkoutSession = await stripe.checkout.sessions.retrieve(sessionId); if ( checkoutSession.mode === 'payment' && checkoutSession.status === 'complete' && (checkoutSession.metadata?.type === 'credit_pack' || checkoutSession.metadata?.packId) && checkoutSession.metadata?.userId === userId ) { const { addPurchasedCredits } = await import('@/lib/credits'); const { getCreditPack, isCreditPackId, } = await import('@/lib/billing/credit-packs'); const packId = checkoutSession.metadata?.packId; let credits = Number(checkoutSession.metadata?.credits ?? 0); if (packId && isCreditPackId(packId) && (!Number.isFinite(credits) || credits <= 0)) { credits = getCreditPack(packId).credits; } if (Number.isFinite(credits) && credits > 0) { await addPurchasedCredits(userId, credits, { stripeSessionId: checkoutSession.id, packId: packId ?? null, type: 'credit_pack', source: 'billing_status_sync', }); } } } catch (packSyncErr) { console.error('[billing/status] pack sync failed:', packSyncErr); } } const trialEligible = await shouldOfferSubscriptionTrial(userId); return NextResponse.json({ tier, effectiveTier, status, currentPeriodEnd: currentPeriodEnd ?? null, currentPeriodStart: subscription?.currentPeriodStart ?? null, cancelAtPeriodEnd: subscription?.cancelAtPeriodEnd ?? false, hasStripeSubscription: !!subscription?.stripeSubscriptionId, trialEndsAt: subscription?.trialEndsAt?.toISOString() ?? null, trialEligible, trialDays: trialEligible ? SUBSCRIPTION_TRIAL_DAYS : 0, prices, creditPacks, billingEnabled, }); } catch (error) { console.error('[billing/status]', error); return NextResponse.json({ error: 'Failed to fetch billing status' }, { status: 500 }); } }