example · applications
Receipt Generator Example
By Portix.One Published
Quick answer
Calculate totals in the transaction service and pass a complete immutable receipt model to a renderer. Use separate renderers for HTML, PDF, and ESC/POS; none should recalculate tax or mutate the sale.
Quick answer
Calculate totals in the transaction service and pass a complete immutable receipt model to a renderer. Use separate renderers for HTML, PDF, and ESC/POS; none should recalculate tax or mutate the sale.
type ReceiptModel = {
id: string;
issuedAt: string;
merchant: { name: string; taxId?: string };
lines: { description: string; quantity: string; amount: string }[];
subtotal: string;
taxLines: { label: string; amount: string }[];
total: string;
currency: string;
paymentSummary: string;
};
interface ReceiptRenderer<T> {
render(model: Readonly<ReceiptModel>): Promise<T>;
}
async function generate<T>(model: ReceiptModel, renderer: ReceiptRenderer<T>) {
validateRequiredFields(model);
return renderer.render(Object.freeze(model));
}
Use decimal or minor-unit arithmetic before building the model. Store the renderer/template version and artifact checksum. Escape untrusted text in HTML and constrain control characters in raw formats.
Verify
- Line totals reconcile to subtotal and taxes.
- Currency and rounding are explicit.
- Long names and zero/negative lines render intentionally.
- Repeated rendering produces equivalent output.
- Legally required fields are reviewed per market.