Affinity Design
Agency Guide

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

  1. Open the Stripe Dashboard → DevelopersAPI keys.
  2. Start in test mode. Copy a secret key that starts with sk_test_.
  3. 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:

CategoryEvents
Invoicesinvoice.created, invoice.finalized, invoice.sent, invoice.paid, invoice.payment_failed, invoice.voided
Customerscustomer.created, customer.updated
Paymentspayment_method.attached, charge.succeeded, charge.refunded
Subscriptionscustomer.subscription.created, customer.subscription.updated, customer.subscription.deleted, subscription_schedule.created, subscription_schedule.updated, subscription_schedule.canceled
Catalogproduct.created, product.updated, product.deleted, price.created, price.updated, price.deleted

References: Stripe Webhooks, Stripe CLI

Step 3 — Configure Billing Defaults

  1. Go to Admin → Config → Billing.
  2. Confirm billing is enabled and the base currency is correct.
  3. 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.
  4. Set invoice defaults (due terms, numbering, footer text).
  5. 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

  1. Go to Config → Products or Admin → Billing → Products.
  2. Add each billable item: name, unit price, billing period, and optional tax category.
  3. Add tax rates in Billing → Tax Rates.

Catalog Sync

Each product and tax rate row has a syncStatus:

StatusMeaning
syncedLocal row has a matching Stripe object
driftStripe exists but immutable billing fields changed — needs replacement or review
errorSync attempted and failed. Check syncError in the UI
not_synced / pendingRow 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:

ModeHow It WorksBest For
charge_automaticallyStripe 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_invoiceStripe 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_onlyLocal 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

  1. Sync a product and tax rate from Admin → Billing.
  2. Create a test client billing contract.
  3. Generate and send a test invoice.
  4. Confirm the local invoice stores stripeInvoiceId and stripeHostedInvoiceUrl.
  5. Pay the hosted invoice with a Stripe test card.
  6. 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:

VariableDescription
CF_AI_GATEWAY_ACCOUNT_IDCloudflare account ID
CF_AI_GATEWAY_SLUGAI Gateway gateway slug
CF_AI_GATEWAY_TOKENGateway auth token for AI requests
CF_AI_GATEWAY_LOGS_TOKENRead-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:

VariableDescription
QUICKBOOKS_ENVIRONMENTsandbox or production
QUICKBOOKS_CLIENT_IDIntuit app client ID
QUICKBOOKS_CLIENT_SECRETIntuit app client secret
QUICKBOOKS_REDIRECT_URIMust match the redirect URI configured in the Intuit app
QUICKBOOKS_WEBHOOK_VERIFIER_TOKENToken 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

On this page