Receipt

An order receipt as an A4 page and a PDF, made from the same React component. Presented here for discussion: is this how im-web should make receipts?

From order to PDF

  1. 1

    Order placed

    Checkout copies the lines, prices and buyer into an order snapshot (ReceiptOrder).

  2. 2

    Receipt page

    A React page renders the snapshot as an A4 sheet: sizes in mm and pt, brand colours fixed, print CSS sets the page box.

  3. 3

    Headless Chrome

    The PDF route opens that page in Chrome (playwright-core, the installed Chrome), in print mode, and waits for fonts and images.

  4. 4

    PDF

    Chrome prints A4 with margins and a footer template for page numbers. The route returns application/pdf.

Printing from the browser (Cmd+P) uses the same page and print CSS, so it matches the PDF.

One Chrome stays running between requests; each PDF gets a fresh tab. Chrome loads the page from localhost, not through a tunnel.

Ways to make the PDF

What this feature does, against the usual alternatives.

Headless Chrome

This feature

Print the web page to PDF on the server.

+ Reuses the web components and Tailwind. What you see is what prints. Page breaks, repeated table headers and footers come from Chrome.

– Chrome must run on the server (container size, memory, cold start). The page must be reachable by Chrome, so auth needs a way in.

Browser print only

Print button, no server PDF.

+ Nothing to run on the server. Already works here (Print).

– Result depends on the customer’s browser and settings. No file to email or store.

React PDF renderer

Lay out the receipt with a PDF-specific component set.

+ No browser on the server. Precise control of the PDF.

– A second layout to build and keep in step with the web. Its own styling model, not Tailwind.

PDF service

Send HTML to a separate rendering service (self-hosted or bought).

+ Chrome-quality output without Chrome in the app.

– Another service to run or pay for. Same auth question for assets.

Questions for im-web

What needs deciding before this could move to im-web.

  1. Generate once when the order is paid and store the file, or render on every download?
  2. Where Chrome runs in im-web’s deployment, or whether a rendering service is preferred.
  3. How Chrome reaches the receipt page behind login: a short-lived signed link, or render from data without auth.
  4. Language: the receipt is English only. Norwegian and Swedish (TT) need translated labels and license texts.
  5. Accessibility: should the PDF be tagged for screen readers?
  6. Legal content: what a Norwegian receipt must contain (seller org. no., VAT per rate), checked by finance.
  7. Payment method and invoice details once checkout has payment (status says "Paid" today).

The code

All in src/routes/features/receipt/.

-lib/types.tsReceiptOrder: the order snapshot a receipt needs
-lib/receipt.tsxThe receipt component, print CSS, PDF footer, seller details
-lib/receipt-page.tsxFull-page view with Print and Download PDF
-lib/pdf.server.tsHeadless Chrome: one shared browser, A4, footer, the PDF response
-lib/samples.server.tsSample orders for the variations, with the feature’s own data
sample.$orderId.tsx, .pdf.tsA sample receipt page and its PDF