diff --git a/web/__tests__/api/stripe-routes.test.ts b/web/__tests__/api/stripe-routes.test.ts index 5804a914..a371d6b0 100644 --- a/web/__tests__/api/stripe-routes.test.ts +++ b/web/__tests__/api/stripe-routes.test.ts @@ -356,6 +356,21 @@ describe("Stripe checkout and webhook routes", () => { mockSkillRow(); }); + it("rejects a null checkout body before rate-limit or database work", async () => { + const res = await checkoutPOST( + jsonRequest("http://localhost/api/stripe/checkout", null) + ); + const body = await res.json(); + + expect(res.status).toBe(400); + expect(body.error).toBe("skillId is required"); + expect(mocks.checkRateLimit).not.toHaveBeenCalled(); + expect(mocks.verifyWalletSignature).not.toHaveBeenCalled(); + expect(initializeDatabase).not.toHaveBeenCalled(); + expect(mockSql).not.toHaveBeenCalled(); + expect(mocks.createCheckoutSession).not.toHaveBeenCalled(); + }); + it("rejects malformed skill IDs before checkout side effects", async () => { const res = await checkoutPOST( checkoutRequest({ diff --git a/web/app/api/stripe/checkout/route.ts b/web/app/api/stripe/checkout/route.ts index 1bcec8b2..ef7c689e 100644 --- a/web/app/api/stripe/checkout/route.ts +++ b/web/app/api/stripe/checkout/route.ts @@ -85,7 +85,7 @@ export async function POST(req: NextRequest) { auth?: AuthPayload; }; try { - body = await req.json(); + body = (await req.json()) ?? {}; } catch { return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); }