Skip to content

ADR: Custom Invoice PDF Renderer Retained

  • Status: Accepted
  • Date: 2026-04-23
  • Decider: Jeffrey Lambert
  • Project: Stripe Billing Transition

Context

Stripe offers a Hosted Invoice Page and a downloadable PDF, configurable with logo, colors, a footer, and custom fields. Adventive currently generates its own custom invoice PDF and delivers it via Mailgun. The question is whether Stripe's hosted invoice design can meet Adventive's brand bar.

Jeffrey rated invoice brand fidelity 5 / 5 — non-negotiable. The current custom PDF is a required artifact until Stripe offers robust invoice-template customization.

Decision

The custom invoice PDF renderer stays. Stripe becomes the system of record for billing (subscriptions, pricing, metering, discounts, retries, dunning), but the customer-facing invoice PDF continues to be rendered by Adventive using the existing Handlebars-template approach.

How this shapes the target architecture

Instead of letting Stripe finalize + email the invoice, Adventive intercepts:

  1. Stripe computes and finalizes the invoice (we keep Stripe's calculation engine).
  2. Stripe fires invoice.finalized webhook.
  3. Adventive Worker receives the webhook, pulls the full invoice object from Stripe.
  4. Worker renders the Adventive-branded PDF from the Stripe invoice data using Handlebars.
  5. Worker attaches the rendered PDF to the Stripe invoice (so Stripe's Hosted Invoice Page links to the Adventive version) OR stores it in R2/S3 and emails the link.
  6. Mailgun delivers the invoice email using the existing Handlebars email template.
  7. Stripe handles payment collection through the Hosted Invoice Page or invoice-paid-by-check flows.

Net effect: Stripe owns billing logic, Adventive owns invoice presentation. Mailgun remains the delivery channel.

Revisit trigger

This decision should be revisited if Stripe releases robust invoice-template customization (e.g., template HTML/CSS control, custom layouts beyond logo/color, multi-language templating). At that point, retiring the custom renderer + Handlebars templates + the Worker rendering path is a meaningful simplification.

PDF-rendering implementation options (to decide in Phase B)

Rendering HTML-to-PDF in or near a Cloudflare Worker requires care:

Option Pros Cons
Cloudflare Browser Rendering (Puppeteer on Workers) Cloudflare-native, pay-per-use, scales to zero, supports full HTML/CSS Metered service, colder starts than a long-lived renderer
External PDF service (DocRaptor, PDFShift, etc.) Outsources the hard part, reliable rendering Vendor lock-in, monthly cost, data-residency concern
Small dedicated container (Cloudflare Container, small VM) Full control, cheap at volume One more thing to maintain
Keep the existing PHP renderer callable as a service Minimal change, reuses proven templates Still running legacy PHP — partial win

Browser Rendering on Workers is the leading candidate because it keeps everything on Cloudflare and preserves the "retire the CodeIgniter monolith" goal.

Consequences

Positive: - Brand fidelity preserved on every customer-facing invoice. - Existing Handlebars templates survive — they were a deliberate investment. - Mailgun stays as-is, consistent with its shared-service role (auth emails, onboarding, notifications).

Negative / to-mitigate: - More moving parts than "let Stripe send the invoice" — webhook reliability matters. - Must ensure the PDF Adventive generates matches the final Stripe invoice state (after any last-minute adjustments). - Invoice email deliverability is Mailgun's responsibility, not Stripe's — sender reputation must stay healthy. - If the Worker fails to render, we need a documented fallback (retry queue + dead-letter + human alert).

Follow-ups

  • Decide the PDF-rendering implementation (table above).
  • Design the webhook handler with idempotency + retry + DLQ.
  • Decide whether to attach the rendered PDF to the Stripe invoice or host it externally and link from Stripe.
  • Inventory existing Handlebars invoice template + confirm it can consume a Stripe invoice object shape directly (or define a mapping layer).
  • Monitor Stripe changelog for hosted-invoice-template customization — revisit this ADR quarterly.