05 — Appendix¶
References¶
Stripe documentation¶
| Resource | URL | Relevance |
|---|---|---|
| Stripe Billing overview | https://stripe.com/docs/billing | Primary reference for subscriptions, prices, invoices |
| Stripe Invoicing API | https://stripe.com/docs/api/invoices | Invoice object, line items, finalization, status |
| Stripe Subscriptions API | https://stripe.com/docs/api/subscriptions | Subscription creation, metered billing, billing cycle |
| Stripe Prices API | https://stripe.com/docs/api/prices | Price types: licensed, metered, tiered, graduated |
| Stripe Meters API | https://stripe.com/docs/billing/subscriptions/usage-based | Usage-based billing via Meters (new) vs. legacy usage records |
| Stripe Usage Records API | https://stripe.com/docs/api/usage_records | Legacy metered billing path (deprecated but functional) |
| Stripe InvoiceItem API | https://stripe.com/docs/api/invoiceitems | Adding line items to upcoming invoices |
| Stripe Smart Retries | https://stripe.com/docs/billing/revenue-recovery/smart-retries | Automated failed payment retry |
| Stripe Dunning configuration | https://stripe.com/docs/billing/revenue-recovery/dunning | Dunning email schedules, Stripe-managed comms |
| Stripe Customer Portal | https://stripe.com/docs/billing/subscriptions/customer-portal | Self-service card update, invoice history |
| Stripe Webhooks | https://stripe.com/docs/webhooks | Event delivery, signature verification, retries |
| Stripe Webhook best practices | https://stripe.com/docs/webhooks/best-practices | Idempotency, ordering, failure handling |
| Stripe Credit Notes | https://stripe.com/docs/billing/invoices/credit-notes | Issuing refunds/adjustments on finalized invoices |
| Stripe Tax | https://stripe.com/docs/tax | Future enablement path (not in scope at cutover) |
| Stripe event types | https://stripe.com/docs/api/events/types | Full list of webhook event types |
Cloudflare documentation¶
| Resource | URL | Relevance |
|---|---|---|
| Cloudflare Workers | https://developers.cloudflare.com/workers/ | Runtime for billing Worker |
| Cloudflare Browser Rendering | https://developers.cloudflare.com/browser-rendering/ | PDF rendering from HTML (leading candidate) |
| Cloudflare R2 | https://developers.cloudflare.com/r2/ | Invoice PDF storage (replaces S3 CDN) |
| Workers Cron Triggers | https://developers.cloudflare.com/workers/configuration/cron-triggers/ | Daily reconciliation job |
| Workers Analytics Engine | https://developers.cloudflare.com/analytics/analytics-engine/ | Structured observability from billing Worker |
| Wrangler CLI | https://developers.cloudflare.com/workers/wrangler/ | Deploy, dev, secret management |
Framework and library documentation¶
| Package | URL | Purpose |
|---|---|---|
| Hono | https://hono.dev | TypeScript HTTP framework for billing Worker |
| Stripe Node SDK | https://github.com/stripe/stripe-node | Stripe API client (Workers-compatible) |
| Handlebars | https://handlebarsjs.com | Invoice PDF template engine |
| Mailgun.js | https://github.com/mailgun/mailgun.js | Mailgun API client (Workers-compatible) |
| Vitest | https://vitest.dev | Unit + integration testing |
Related internal projects¶
| Project | Path | Relationship |
|---|---|---|
| Admin Dashboard Cloudflare Migration | ../Admin Dashboard Cloudflare Migration/ |
Sibling — billing ops surface in new admin-api; this project defines what billing endpoints admin-api needs |
| Public API Cloudflare Migration | ../Public API Cloudflare Migration/ |
Predecessor — auth model, deployment pipeline, observability approach to mirror |
| Adventive Brand Guide | https://brandguide.adventive.com/dashboard | Invoice PDF design reference |
External services¶
| Service | Reference | Purpose |
|---|---|---|
| Mailgun | https://app.mailgun.com | Invoice email delivery (stays) |
| Acodei | https://acodei.com | QuickBooks sync from Stripe events (unchanged) |
Inputs¶
Files analyzed during this planning engagement:
| File | Description | Source | Date |
|---|---|---|---|
application/controllers/Billing.php |
Invoice trigger, payment trigger, 6-step dunning, Mailgun send, month-end Excel export (960 lines) | adventive-admin repo | 2026-04-23 |
application/models/Billing_model.php |
All billing proxy methods, pricing tier queries, billing history (933 lines) | adventive-admin repo | 2026-04-23 |
application/libraries/Mailgun.php |
Custom curl-based Mailgun client, sendTemplatedEmail, invoice send paths | adventive-admin repo | 2026-04-23 |
application/config/adventive.php |
billing_service URL, billing email sender, HubSpot BCC, invoice CDN URL | adventive-admin repo | 2026-04-23 |
application/controllers/ManagedServiceJob.php |
Managed service job CRUD | adventive-admin repo | 2026-04-23 |
application/models/ManagedService_model.php |
Managed service job queries (505 lines) | adventive-admin repo | 2026-04-23 |
| Stripe account state | Products (5), Prices (5), Subscriptions (0), Invoices (10 sampled) | Stripe MCP connector | 2026-04-23 |
Open questions¶
The following items require a human decision before or during implementation. Items marked BLOCKING must be resolved before the indicated phase begins.
| # | Question | Blocking phase | Owner | Notes |
|---|---|---|---|---|
| OQ-1 | Stripe Smart Retries configuration: how many retries, at what intervals? | B.3 (BLOCKING — must configure before first live cohort) | Jeffrey | Stripe default is 4 attempts over ~4 weeks. Adventive's current manual dunning runs 6 steps over 30 days. Align Smart Retries schedule with current customer expectations before retiring Adventive dunning. |
| OQ-2 | Browser Rendering API access: has the Cloudflare account been enabled for Browser Rendering? | B.2 (BLOCKING — must confirm before PDF rendering build) | Patrick | Browser Rendering is available on Workers Paid but must be explicitly enabled in the Cloudflare Dashboard under Workers & Pages → Browser Rendering. |
| OQ-3 | Historical invoice export: which format and storage location? | B.4 | Jeffrey | 14 years of billing_invoice history. Options: (a) export to R2 as JSON + original PDFs, (b) retain read-only access to legacy DB after billing_service retirement, (c) export to BigQuery. Must decide before B.8 decommissions billing_service. |
| OQ-4 | Cohort 1 customer list: which accounts are flat-subscription-only (no metered impressions)? | B.6 (BLOCKING) | Jeffrey + Patrick | Cohort 1 gate is pricing-model simplicity. Need a query against account_plan_usage to identify accounts with zero impression tiers (sub-only). |
| OQ-5 | billing_service tech stack: what PDF renderer does billing_service use, and is the Handlebars template accessible? | B.2 | Patrick | The PDF renderer in billing_service is unanalyzable from the admin repo. The template must be extracted to seed the billing Worker's src/render/templates/invoice.hbs. |
| OQ-6 | Acodei field mapping compatibility: does Acodei handle Stripe Subscription invoices the same as manual invoices? | B.3 | Jeffrey | Acodei captures Stripe events. Subscription-generated invoices (billing_reason=subscription) have a different event shape than manual invoices (billing_reason=manual). Confirm Acodei's field mappings cover both before first live cohort. |
| OQ-7 | Customer Portal (B.9): opt-in or deferred? | B.9 | Jeffrey | Customer Portal enables self-service card update and invoice history. Low implementation cost (Stripe configuration only). Decision needed: enable immediately at B.9 with customer comms, or defer until there's a specific support-cost trigger. |
| OQ-8 | Metering signal frequency: daily push or end-of-period summary? | B.4 | Jeffrey + Patrick | Daily impression counts from Redshift are more accurate and reduce cutover risk, but require a pipeline change. End-of-period summary matches the current billing_service behavior and is lower implementation cost. Both are compatible with Stripe metered Prices. |
| OQ-9 | Custom arrangement inventory: how many accounts have non-standard pricing not captured in account_plan_usage? | B.6 (BLOCKING for Cohort 3) | Jeffrey + Patrick | Cohort 3 includes custom-arrangement customers. Need a per-account audit of billing_service invoice history vs. account_plan_usage + account_plan_sub to identify discrepancies. |
| OQ-10 | New GitHub repository: where does billing-worker live? | B.1 | Jeffrey | Assumed adventive/adventive-billing-worker on GitHub (private). Confirm organization name, visibility, and branch protection rules. |
| OQ-11 | Stripe webhook signing secret: staging vs. production endpoints registered separately? | B.1 (staging), B.3 (production) | Patrick | Staging should point to Stripe test-mode endpoint; production to live-mode endpoint. Two separate signing secrets. Confirm who registers them and who has access to Stripe Dashboard Developers tab. |
| OQ-12 | Cloudflare account ID and API token provisioning for billing Worker CI/CD | B.1 | Patrick | GitHub Actions needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as repository secrets. Same token used by admin-api CI if on same Cloudflare account. |
Glossary¶
| Term | Definition |
|---|---|
| billing_service | A separate internal Adventive API at billing.{domain}/api/billing/ that owns all Stripe API calls, rollup logic, and invoice PDF generation. The admin repo is a thin proxy to this service. Not to be confused with the billing Worker (the new replacement). |
| billing Worker | The new Cloudflare Worker that replaces billing_service's Adventive-side responsibilities: webhook ingestion, PDF rendering, Mailgun delivery, and reconciliation. The billing Worker does NOT replace Stripe — it complements Stripe. |
| Browser Rendering | Cloudflare's headless-browser API that renders HTML to PDF. The leading candidate for replacing billing_service's PDF renderer. Limit: 2 simultaneous renders per account at a time. |
| Credit Note | A Stripe object that reduces the amount owed on a finalized invoice. Used for refunds, corrections, or goodwill adjustments. Does not create a new invoice — adjusts the existing one. |
| Dual-run period | The phase during a cohort migration when both billing_service (legacy) and the billing Worker (new) are live simultaneously for the same customer. The billing Worker is authoritative; billing_service is a shadow check. One billing cycle minimum. |
| Dunning | The process of communicating with customers after payment failure to recover the revenue. Currently owned by Adventive (6-step manual sequence in Billing.php). Post-migration: fully owned by Stripe (Smart Retries + Stripe dunning email schedules). |
| Graduated pricing | A Stripe tiered price model where each tier's rate applies only to the quantity within that tier (not to the full quantity). Adventive's current impression pricing is graduated. |
| Hyperdrive | Cloudflare's MySQL connection pooling layer. Used by the billing Worker for reconciliation reads of the legacy billing_invoice table during the dual-run period. |
| idempotency key | A unique key that prevents duplicate operations when a request is retried. For usage events: {account_id}:{period}:{usage_type}. For Stripe API calls: passed as Idempotency-Key header. |
| InvoiceItem | A Stripe object that adds a line item to a customer's next invoice. Used for managed service fees and custom line items. InvoiceItems are created as-accrued and automatically included when the subscription invoice finalizes. |
| licensed (usage_type) | A Stripe Price billing model where the subscription charges a flat amount per billing cycle, regardless of usage. Used for flat monthly subscription fees. |
| metered (usage_type) | A Stripe Price billing model where usage is reported via Usage Records (or Meters), and the subscription charges based on actual usage at period end. Used for ST/RM/CV impression pricing. |
| Meter | The new Stripe API for usage-based billing (replaces legacy Usage Records API). Adventive should evaluate Meters vs. legacy Usage Records at B.4 — Meters offer idempotency guarantees natively. |
| Point of no return (B.8) | The phase at which legacy billing_service is retired and the billing Worker is the sole billing runtime. After B.8, rollback requires restoring from DB backup and restarting billing_service — not a quick revert. Extra scrutiny required before B.8. |
| R2 | Cloudflare's S3-compatible object storage. Stores invoice PDFs generated by the billing Worker (replaces billing.adventivecdn.com S3/CloudFront CDN). |
| reconciliation | Comparing Stripe invoice totals against the legacy billing_invoice DB (during dual-run) or against Acodei/QuickBooks (post-migration) to detect discrepancies. Zero tolerance: no drift is acceptable before a cohort is marked migrated. |
| Smart Retries | Stripe's machine-learning-based payment retry system. Automatically retries failed payments at times statistically more likely to succeed. Must be explicitly configured on the Stripe account. |
| Stripe-Signature | HTTP header set by Stripe on every webhook delivery. Contains an HMAC-SHA256 signature over the raw request body. Billing Worker must verify this header before processing any event. |
| subscription | A Stripe object that manages recurring billing for a customer. The billing Worker creates one Subscription per Adventive customer with the appropriate flat + metered Prices attached. |
| Usage Record | A data point submitted to Stripe for a metered Price, reporting the quantity of usage for a subscription item in a period. Used to calculate the metered portion of a subscription invoice. |
Decisions log (cross-reference)¶
All formal decisions are in decisions/. Summary of locked-in decisions referenced throughout this document:
| Decision | Summary | File |
|---|---|---|
| Stripe Billing scope | Stripe owns billing engine; Adventive owns PDF + email + metering signal | decisions/2026-04-23-stripe-billing-scope.md |
| Custom invoice PDF retained | Custom PDF non-negotiable; Cloudflare Browser Rendering leading candidate | decisions/2026-04-23-custom-invoice-pdf-retained.md |
Change log¶
| Version | Date | Author | Change |
|---|---|---|---|
| 1.0 | 2026-04-23 | Jeffrey Lambert | Initial planning deliverable — repo analysis complete; Stripe inventory complete; all chapters populated; PDF generated |