docs: add missing OpenAPI annotations for signup/verify, billing/success, billing/webhook
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 16m15s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 16m15s
This commit is contained in:
parent
427ec8e894
commit
8b31d11e74
15 changed files with 2167 additions and 128 deletions
|
|
@ -92,6 +92,31 @@ describe("App-level routes", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("OpenAPI spec completeness", () => {
|
||||
let spec: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const res = await request(app).get("/openapi.json");
|
||||
expect(res.status).toBe(200);
|
||||
spec = res.body;
|
||||
});
|
||||
|
||||
it("includes POST /v1/signup/verify", () => {
|
||||
expect(spec.paths["/v1/signup/verify"]).toBeDefined();
|
||||
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("includes POST /v1/billing/webhook", () => {
|
||||
expect(spec.paths["/v1/billing/webhook"]).toBeDefined();
|
||||
expect(spec.paths["/v1/billing/webhook"].post).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Security headers", () => {
|
||||
it("includes helmet security headers", async () => {
|
||||
const res = await request(app).get("/api");
|
||||
|
|
|
|||
|
|
@ -112,6 +112,36 @@ 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
|
||||
router.get("/success", async (req: Request, res: Response) => {
|
||||
const sessionId = req.query.session_id as string;
|
||||
|
|
@ -189,6 +219,47 @@ 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
|
||||
router.post("/webhook", async (req: Request, res: Response) => {
|
||||
const sig = req.headers["stripe-signature"] as string;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,60 @@ router.post("/free", rejectDuplicateEmail, signupLimiter, async (req: Request, r
|
|||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /v1/signup/verify:
|
||||
* post:
|
||||
* tags: [Account]
|
||||
* summary: Verify email and get API key
|
||||
* description: |
|
||||
* Verifies the 6-digit code sent to the user's email and provisions a free API key.
|
||||
* Rate limited to 15 attempts per 15 minutes.
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [email, code]
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: Email address used during signup
|
||||
* example: user@example.com
|
||||
* code:
|
||||
* type: string
|
||||
* description: 6-digit verification code from email
|
||||
* example: "123456"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Email verified, API key issued
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* status:
|
||||
* type: string
|
||||
* example: verified
|
||||
* message:
|
||||
* type: string
|
||||
* apiKey:
|
||||
* type: string
|
||||
* description: The provisioned API key
|
||||
* tier:
|
||||
* type: string
|
||||
* example: free
|
||||
* 400:
|
||||
* description: Missing fields or invalid verification code
|
||||
* 409:
|
||||
* description: Email already verified
|
||||
* 410:
|
||||
* description: Verification code expired
|
||||
* 429:
|
||||
* description: Too many failed attempts
|
||||
*/
|
||||
// Step 2: Verify code — creates API key
|
||||
router.post("/verify", verifyLimiter, async (req: Request, res: Response) => {
|
||||
const { email, code } = req.body || {};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue