From b491052f69361927063ee7bc2f04cefd9594b7d4 Mon Sep 17 00:00:00 2001 From: DocFast CEO Date: Tue, 10 Mar 2026 17:03:44 +0100 Subject: [PATCH] refactor: extract billing HTML templates into billing-templates.ts (TDD) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract renderSuccessPage() and renderAlreadyProvisionedPage() from billing.ts - Share common styles via SHARED_STYLES constant - 11 TDD tests: content rendering, XSS escaping, structure validation - billing.ts: 369 → 334 lines (-35 lines, inline HTML removed) - 647 tests passing (59 files), 0 tsc errors --- src/__tests__/billing-templates.test.ts | 64 +++++++++++++++++++++++++ src/routes/billing.ts | 41 ++-------------- src/utils/billing-templates.ts | 41 ++++++++++++++++ 3 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 src/__tests__/billing-templates.test.ts create mode 100644 src/utils/billing-templates.ts diff --git a/src/__tests__/billing-templates.test.ts b/src/__tests__/billing-templates.test.ts new file mode 100644 index 0000000..377e441 --- /dev/null +++ b/src/__tests__/billing-templates.test.ts @@ -0,0 +1,64 @@ +import { describe, it, expect } from "vitest"; +import { renderSuccessPage, renderAlreadyProvisionedPage } from "../utils/billing-templates.js"; + +describe("billing-templates", () => { + describe("renderSuccessPage", () => { + it("includes the API key in the output", () => { + const html = renderSuccessPage("df_pro_abc123"); + expect(html).toContain("df_pro_abc123"); + }); + + it("escapes HTML in the API key", () => { + const html = renderSuccessPage(''); + expect(html).not.toContain(" -`); + res.send(renderSuccessPage(keyInfo.key)); } catch (err: unknown) { logger.error({ err }, "Success page error"); res.status(500).json({ error: "Failed to retrieve session" }); diff --git a/src/utils/billing-templates.ts b/src/utils/billing-templates.ts new file mode 100644 index 0000000..970bc91 --- /dev/null +++ b/src/utils/billing-templates.ts @@ -0,0 +1,41 @@ +import { escapeHtml } from "./html.js"; + +const SHARED_STYLES = ` +body { font-family: system-ui; background: #0a0a0a; color: #e8e8e8; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; } +.card { background: #141414; border: 1px solid #222; border-radius: 16px; padding: 48px; max-width: 500px; text-align: center; } +h1 { color: #4f9; margin-bottom: 8px; } +p { color: #888; line-height: 1.6; } +a { color: #4f9; } +`; + +export function renderSuccessPage(apiKey: string): string { + const escaped = escapeHtml(apiKey); + return ` +Welcome to DocFast Pro! + +
+

🎉 Welcome to Pro!

+

Your API key:

+
${escaped}
+

Save this key! It won't be shown again.

+

5,000 PDFs/month • All endpoints • Priority support

+

View API docs →

+
+ +`; +} + +export function renderAlreadyProvisionedPage(): string { + return ` +DocFast Pro — Key Already Provisioned + +
+

✅ Key Already Provisioned

+

A Pro API key has already been created for this purchase.

+

If you lost your key, use the key recovery feature.

+

View API docs →

+
`; +}