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:
- Stripe computes and finalizes the invoice (we keep Stripe's calculation engine).
- Stripe fires
invoice.finalizedwebhook. - Adventive Worker receives the webhook, pulls the full invoice object from Stripe.
- Worker renders the Adventive-branded PDF from the Stripe invoice data using Handlebars.
- 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.
- Mailgun delivers the invoice email using the existing Handlebars email template.
- 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.