From 917552f0416ef96649f6b82ddf70be098acf6283 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 22 Aug 2025 16:52:31 -0700 Subject: [PATCH] fix(billing): vercel cron not processing billing periods (#1112) --- apps/sim/app/api/billing/daily/route.ts | 61 +++++++++++++++++++++---- apps/sim/lib/auth/internal.ts | 4 +- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/apps/sim/app/api/billing/daily/route.ts b/apps/sim/app/api/billing/daily/route.ts index 4b88f3563..4ed088e86 100644 --- a/apps/sim/app/api/billing/daily/route.ts +++ b/apps/sim/app/api/billing/daily/route.ts @@ -67,7 +67,7 @@ export async function POST(request: NextRequest) { { status: 500 } ) } catch (error) { - logger.error('Fatal error in monthly billing cron job', { error }) + logger.error('Fatal error in daily billing cron job', { error }) return NextResponse.json( { @@ -90,18 +90,59 @@ export async function GET(request: NextRequest) { return authError } - return NextResponse.json({ - status: 'ready', - message: - 'Daily billing check cron job is ready to process users and organizations with periods ending today', - currentDate: new Date().toISOString().split('T')[0], + const startTime = Date.now() + const result = await processDailyBillingCheck() + const duration = Date.now() - startTime + + if (result.success) { + logger.info('Daily billing check (GET) completed successfully', { + processedUsers: result.processedUsers, + processedOrganizations: result.processedOrganizations, + totalChargedAmount: result.totalChargedAmount, + duration: `${duration}ms`, + }) + + return NextResponse.json({ + success: true, + summary: { + processedUsers: result.processedUsers, + processedOrganizations: result.processedOrganizations, + totalChargedAmount: result.totalChargedAmount, + duration: `${duration}ms`, + }, + }) + } + + logger.error('Daily billing check (GET) completed with errors', { + processedUsers: result.processedUsers, + processedOrganizations: result.processedOrganizations, + totalChargedAmount: result.totalChargedAmount, + errorCount: result.errors.length, + errors: result.errors, + duration: `${duration}ms`, }) - } catch (error) { - logger.error('Error in billing health check', { error }) + return NextResponse.json( { - status: 'error', - error: error instanceof Error ? error.message : 'Unknown error', + success: false, + summary: { + processedUsers: result.processedUsers, + processedOrganizations: result.processedOrganizations, + totalChargedAmount: result.totalChargedAmount, + errorCount: result.errors.length, + duration: `${duration}ms`, + }, + errors: result.errors, + }, + { status: 500 } + ) + } catch (error) { + logger.error('Fatal error in daily billing (GET) cron job', { error }) + return NextResponse.json( + { + success: false, + error: 'Internal server error during daily billing check', + details: error instanceof Error ? error.message : 'Unknown error', }, { status: 500 } ) diff --git a/apps/sim/lib/auth/internal.ts b/apps/sim/lib/auth/internal.ts index 29d74988c..c82355e84 100644 --- a/apps/sim/lib/auth/internal.ts +++ b/apps/sim/lib/auth/internal.ts @@ -57,8 +57,10 @@ export async function verifyInternalToken(token: string): Promise { export function verifyCronAuth(request: NextRequest, context?: string): NextResponse | null { const authHeader = request.headers.get('authorization') const expectedAuth = `Bearer ${env.CRON_SECRET}` + const isVercelCron = request.headers.get('x-vercel-cron') === '1' - if (authHeader !== expectedAuth) { + // Allow Vercel Cron requests (they include x-vercel-cron header instead of Authorization) + if (!isVercelCron && authHeader !== expectedAuth) { const contextInfo = context ? ` for ${context}` : '' logger.warn(`Unauthorized CRON access attempt${contextInfo}`, { providedAuth: authHeader,