fix: OpenAPI spec accuracy — hide internal endpoints, mark signup/verify deprecated
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 13m9s

- Remove @openapi annotations from /v1/billing/webhook (Stripe-internal)
- Remove @openapi annotations from /v1/billing/success (browser redirect)
- Mark /v1/signup/verify as deprecated (returns 410)
- Add 3 TDD tests in openapi-spec.test.ts
- Update 2 existing tests in app-routes.test.ts
- 530 tests passing (was 527)
This commit is contained in:
Hoid 2026-03-07 14:06:12 +01:00
parent 1d5d9adf08
commit 6b1b3d584e
15 changed files with 399 additions and 290 deletions

View file

@ -106,14 +106,12 @@ describe("App-level routes", () => {
expect(spec.paths["/v1/signup/verify"].post).toBeDefined();
});
it("includes GET /v1/billing/success", () => {
expect(spec.paths["/v1/billing/success"]).toBeDefined();
expect(spec.paths["/v1/billing/success"].get).toBeDefined();
it("excludes GET /v1/billing/success (browser redirect, not public API)", () => {
expect(spec.paths["/v1/billing/success"]).toBeUndefined();
});
it("includes POST /v1/billing/webhook", () => {
expect(spec.paths["/v1/billing/webhook"]).toBeDefined();
expect(spec.paths["/v1/billing/webhook"].post).toBeDefined();
it("excludes POST /v1/billing/webhook (internal Stripe endpoint)", () => {
expect(spec.paths["/v1/billing/webhook"]).toBeUndefined();
});
});

View file

@ -0,0 +1,18 @@
import { describe, it, expect } from "vitest";
import { swaggerSpec } from "../swagger.js";
describe("OpenAPI spec accuracy", () => {
const spec = swaggerSpec as any;
it("should NOT include /v1/billing/webhook (internal Stripe endpoint)", () => {
expect(spec.paths).not.toHaveProperty("/v1/billing/webhook");
});
it("should NOT include /v1/billing/success (browser redirect page)", () => {
expect(spec.paths).not.toHaveProperty("/v1/billing/success");
});
it("should mark /v1/signup/verify as deprecated", () => {
expect(spec.paths["/v1/signup/verify"]?.post?.deprecated).toBe(true);
});
});

View file

@ -139,37 +139,7 @@ router.post("/checkout", checkoutLimiter, async (req: Request, res: Response) =>
}
});
/**
* @openapi
* /v1/billing/success:
* get:
* tags: [Billing]
* summary: Checkout success page
* description: |
* Provisions a Pro API key after successful Stripe checkout and displays it in an HTML page.
* Called by Stripe redirect after payment completion.
* parameters:
* - in: query
* name: session_id
* required: true
* schema:
* type: string
* description: Stripe Checkout session ID
* responses:
* 200:
* description: HTML page displaying the new API key
* content:
* text/html:
* schema:
* type: string
* 400:
* description: Missing session_id or no customer found
* 409:
* description: Checkout session already used
* 500:
* description: Failed to retrieve session
*/
// Success page — provision Pro API key after checkout
// Success page — provision Pro API key after checkout (browser redirect, not a public API)
router.get("/success", async (req: Request, res: Response) => {
const sessionId = req.query.session_id as string;
if (!sessionId) {
@ -249,48 +219,7 @@ a { color: #4f9; }
}
});
/**
* @openapi
* /v1/billing/webhook:
* post:
* tags: [Billing]
* summary: Stripe webhook endpoint
* description: |
* Receives Stripe webhook events for subscription lifecycle management.
* Requires the raw request body and a valid Stripe-Signature header for verification.
* Handles checkout.session.completed, customer.subscription.updated,
* customer.subscription.deleted, and customer.updated events.
* parameters:
* - in: header
* name: Stripe-Signature
* required: true
* schema:
* type: string
* description: Stripe webhook signature for payload verification
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* description: Raw Stripe event payload
* responses:
* 200:
* description: Webhook received
* content:
* application/json:
* schema:
* type: object
* properties:
* received:
* type: boolean
* example: true
* 400:
* description: Missing Stripe-Signature header or invalid signature
* 500:
* description: Webhook secret not configured
*/
// Stripe webhook for subscription lifecycle events
// Stripe webhook for subscription lifecycle events (internal, not in public API docs)
router.post("/webhook", async (req: Request, res: Response) => {
const sig = req.headers["stripe-signature"] as string;
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

View file

@ -68,9 +68,10 @@ router.post("/free", rejectDuplicateEmail, signupLimiter, async (req: Request, r
* /v1/signup/verify:
* post:
* tags: [Account]
* summary: Verify email and get API key
* summary: Verify email and get API key (discontinued)
* deprecated: true
* description: |
* Verifies the 6-digit code sent to the user's email and provisions a free API key.
* **Discontinued.** Free accounts are no longer available. Try the demo at POST /v1/demo/html or upgrade to Pro at https://docfast.dev.
* Rate limited to 15 attempts per 15 minutes.
* requestBody:
* required: true