Billing Setup
Connect Stripe, configure webhooks, and set up your product catalog
Overview
Billing 2.0 treats Stripe as the payment source of truth and MongoDB as the operational read model for the admin UI, reporting, and local invoice workflows. Before you can create contracts or send invoices, you need to connect Stripe and configure the product catalog.
Step 1 — Create Stripe API Keys
- Open the Stripe Dashboard → Developers → API keys.
- Start in test mode. Copy a secret key that starts with
sk_test_. - Add it to your backend environment:
STRIPE_SECRET_KEY=sk_test_...
Switch to live mode (sk_live_) only after you have verified test invoices, webhooks, and catalog sync end-to-end.
Reference: Stripe API Keys
Step 2 — Configure Webhooks
The app receives Stripe events at:
https://YOUR_API_HOST/webhooks/stripe
Local Testing with the Stripe CLI
stripe listen --forward-to localhost:8000/webhooks/stripe
Copy the signing secret from the CLI output or the Dashboard webhook endpoint and add it to your environment:
STRIPE_WEBHOOK_SECRET=whsec_...
The webhook handler verifies the Stripe-Signature header before processing any event.
Required Events
Subscribe to the following events in your Stripe webhook endpoint:
| Category | Events |
|---|---|
| Invoices | invoice.created, invoice.finalized, invoice.sent, invoice.paid, invoice.payment_failed, invoice.voided |
| Customers | customer.created, customer.updated |
| Payments | payment_method.attached, charge.succeeded, charge.refunded |
| Subscriptions | customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, subscription_schedule.created, subscription_schedule.updated, subscription_schedule.canceled |
| Catalog | product.created, product.updated, product.deleted, price.created, price.updated, price.deleted |
References: Stripe Webhooks, Stripe CLI
Step 3 — Configure Billing Defaults
- Go to Admin → Config → Billing.
- Confirm billing is enabled and the base currency is correct.
- Set the Billing Timezone (IANA name, e.g.
America/Toronto) — subscription billing-cycle anchors for both monthly and yearly items are computed against this timezone so charges land on the intended calendar day locally instead of shifting a day earlier under a UTC anchor. - Set invoice defaults (due terms, numbering, footer text).
- Configure the send-invoice reminder policy — how many days before/after the due date reminders are sent.
These defaults apply to all new contracts and invoices unless overridden at the contract level.
Step 4 — Create Products & Services
- Go to Config → Products or Admin → Billing → Products.
- Add each billable item: name, unit price, billing period, and optional tax category.
- Add tax rates in Billing → Tax Rates.
Catalog Sync
Each product and tax rate row has a syncStatus:
| Status | Meaning |
|---|---|
synced | Local row has a matching Stripe object |
drift | Stripe exists but immutable billing fields changed — needs replacement or review |
error | Sync attempted and failed. Check syncError in the UI |
not_synced / pending | Row has not completed a successful Stripe sync |
Use the Resync action on any row showing drift, error, or not_synced. The sync creates or updates the corresponding Stripe product, price, or tax-rate object and writes back the Stripe IDs.
New Stripe invoices and subscriptions are created with automatic_tax: { enabled: true }. Local tax snapshots remain on invoice lines for historical reporting and preview consistency.
Step 5 — Choose Collection Modes
When creating a client billing contract, you select one of three collection modes:
| Mode | How It Works | Best For |
|---|---|---|
charge_automatically | Stripe manages payment attempts and Smart Retries. Local reminder sweep skips these contracts to avoid duplicate dunning. | Clients with payment methods on file (card, ACH) |
send_invoice | Stripe creates a hosted invoice page and emails the client a payment link. Local reminders follow the policy in Config → Billing. | Clients who pay by invoice on their own schedule |
manual_only | Local invoice tracking only — no Stripe payment processing. Manual payment recording. | Cash/check clients, or clients billed through an external system |
Verification Checklist
Run syntax checks before deploying:
node --check utils/stripe-billing.js
node --check utils/stripe-subscriptions.js
node --check routes/webhooks.js
node --check routes/admin-billing.js
Run the billing engine tests:
node scripts/test-billing-engine.mjs
Manual Test in Stripe Test Mode
- Sync a product and tax rate from Admin → Billing.
- Create a test client billing contract.
- Generate and send a test invoice.
- Confirm the local invoice stores
stripeInvoiceIdandstripeHostedInvoiceUrl. - Pay the hosted invoice with a Stripe test card.
- Confirm the webhook updates invoice status and client ledger totals.
Accounting — Margin & QuickBooks
The Accounting section under Admin provides a margin view combining local invoice revenue with provider usage costs imported from Cloudflare AI Gateway, and syncs invoices to QuickBooks Online.
Cloudflare AI Gateway Cost Import
Set these env vars on the API service to enable usage cost imports:
| Variable | Description |
|---|---|
CF_AI_GATEWAY_ACCOUNT_ID | Cloudflare account ID |
CF_AI_GATEWAY_SLUG | AI Gateway gateway slug |
CF_AI_GATEWAY_TOKEN | Gateway auth token for AI requests |
CF_AI_GATEWAY_LOGS_TOKEN | Read-scoped Cloudflare API token for importing logs |
Open Admin → Accounting, pick a month/year, and click Import Usage to pull cost rows from Cloudflare AI Gateway into the provider_usage_ledger.
Reference: Cloudflare AI Gateway logs API
QuickBooks Online Sync
Connect the agency QuickBooks company to sync Billing 2.0 invoices into QuickBooks for bookkeeping:
| Variable | Description |
|---|---|
QUICKBOOKS_ENVIRONMENT | sandbox or production |
QUICKBOOKS_CLIENT_ID | Intuit app client ID |
QUICKBOOKS_CLIENT_SECRET | Intuit app client secret |
QUICKBOOKS_REDIRECT_URI | Must match the redirect URI configured in the Intuit app |
QUICKBOOKS_WEBHOOK_VERIFIER_TOKEN | Token for validating Intuit webhook payloads |
The Intuit app requires the com.intuit.quickbooks.accounting OAuth scope.
Configure the Intuit webhook endpoint:
https://YOUR_API_HOST/admin/accounting/quickbooks/webhook
Subscribe to Customer, Item, Invoice, and Payment notifications.
To sync: open Admin → Accounting, connect QuickBooks via OAuth, then click Sync Period Invoices.
Reference: QuickBooks Online API
