Billing Server
Package:
@leadmetrics/server-billing· Path:apps/servers/billing· Start:pnpm dev
The billing server is a standalone Node.js process that handles all money-related automation: invoice generation, overdue detection, subscription lockout, credit resets, and payment reminder emails. It runs as a BullMQ worker pool plus a cron scheduler — no HTTP server, no exposed ports.
Workers
Three BullMQ workers start on boot and run for the lifetime of the process.
1. Invoice Worker (billing__invoice_generation)
Processes billing__invoice_generation jobs. For each job:
- Reads the
SubscriptionandPlanrecords for the tenant. - Calculates the invoice amount (plan price + applicable GST).
- Creates an
Invoicerecord (status: "pending",dueAt: end of month). - Sends an invoice email to the tenant’s billing contact.
Queue: billing__invoice_generation | Concurrency: 5
2. Overdue Worker (billing__overdue_checker)
Processes billing__overdue_checker jobs. For each run:
- Marks
Invoicerecords asoverduewherestatus = "pending"anddueAt < now. - Locks subscriptions where all pending/overdue invoices have passed
dueAt + gracePeriodDays— setsSubscription.status = "locked". - Unlocks subscriptions that have no remaining unpaid invoices (payment received after lockout) — sets
Subscription.status = "active".
Queue: billing__overdue_checker | Concurrency: 1
3. Credits Reset Worker (billing__credits_reset)
Processes billing__credits_reset jobs. For each tenant:
- Reads the tenant’s plan credit allocation.
- Resets
CreditBalance.balanceto the plan’s monthly allocation. - Writes a
CreditLedgerentry recording the reset.
Queue: billing__credits_reset | Concurrency: 1
Cron Scheduler
Four node-cron jobs fire automatically. All times are UTC.
| Job | Default schedule | What it does |
|---|---|---|
| Monthly billing | 5 0 1 * * (1st of month, 00:05) | Enqueues one invoice job per active paid subscription |
| Daily overdue check | 0 1 * * * (01:00 daily) | Enqueues an overdue-checker job |
| Hourly reminders | 0 * * * * (top of every hour) | Sends payment reminder emails for overdue invoices |
| Monthly credits reset | 10 0 1 * * (1st of month, 00:10) | Enqueues credits-reset jobs for all active tenants |
Cron expressions are validated at startup — an invalid expression throws immediately so the process never silently fails to bill.
Overriding schedules
All four schedules can be overridden via env vars:
BILLING_CRON_SCHEDULE=5 0 1 * *
OVERDUE_CRON_SCHEDULE=0 1 * * *
REMINDER_CRON_SCHEDULE=0 * * * *
CREDITS_RESET_CRON_SCHEDULE=10 0 1 * *Queue Names
| Constant | Queue name | Used for |
|---|---|---|
BILLING_QUEUE | billing__invoice_generation | Invoice generation jobs |
OVERDUE_CHECKER_QUEUE | billing__overdue_checker | Overdue detection + lockout |
CREDITS_RESET_QUEUE | billing__credits_reset | Monthly credit resets |
Job deduplication: invoice jobs use billing__{tenantId}__{YYYY-MM} as jobId, so re-running the cron on the same month is safe.
Required Environment Variables
DATABASE_URL=postgresql://...
REDIS_URL=redis://...Optional (to override cron schedules or billing defaults):
BILLING_CRON_SCHEDULE=...
OVERDUE_CRON_SCHEDULE=...
REMINDER_CRON_SCHEDULE=...
CREDITS_RESET_CRON_SCHEDULE=...Create apps/servers/billing/.env with at minimum DATABASE_URL and REDIS_URL.
Starting Locally
cd apps/servers/billing
pnpm devThe process logs to stdout. On clean startup you’ll see:
billing server ready
Invoice, overdue, and credits-reset workers started
Billing scheduler startedShutdown
The process handles SIGTERM and SIGINT cleanly:
- Stops all cron tasks.
- Closes all three BullMQ workers gracefully (drains in-flight jobs).
- Closes the Redis connection.
- Disconnects Prisma.
Related Docs
- Credits Overview — what credits are and how they’re allocated per plan
- Tenant Lifecycle & Billing — onboarding → active → locked lifecycle, invoice flow, GST fields
- Credits & Billing (feature) — reservation vs consumption model