import type { NextRequest } from "next/server"; import { paymentWebhookInputSchema } from "@salixcommerce/core"; import { errorResponse } from "@/server/commerce/http"; import { paymentResultResponse } from "@/server/payments/http"; import { paymentRuntime } from "@/server/payments/payments"; export const runtime = "nodejs"; const MAX_WEBHOOK_BYTES = 1_048_576; export async function POST(request: NextRequest) { if (paymentRuntime.provider?.key !== "stripe") { return errorResponse("PAYMENTS_UNAVAILABLE", "Stripe payments are not configured.", 503); } const declaredLength = readContentLength(request.headers.get("content-length")); if (declaredLength !== null && declaredLength > MAX_WEBHOOK_BYTES) { return errorResponse("INVALID_REQUEST", "Stripe webhook body is too large.", 400); } const signature = request.headers.get("stripe-signature"); if (signature === null) { return errorResponse("INVALID_REQUEST", "Stripe webhook signature is required.", 400); } const rawBody = await request.text(); const parsed = paymentWebhookInputSchema.safeParse({ rawBody, signature }); if (!parsed.success) { return errorResponse("INVALID_REQUEST", "Invalid Stripe webhook request.", 400); } return paymentResultResponse(await paymentRuntime.service.processWebhook(parsed.data)); } function readContentLength(value: string | null): number | null { if (value === null) return null; if (!/^[0-9]+$/u.test(value)) return Number.POSITIVE_INFINITY; const length = Number(value); return Number.isSafeInteger(length) ? length : Number.POSITIVE_INFINITY; }