Skip to Content
ServersBilling Server

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:

  1. Reads the Subscription and Plan records for the tenant.
  2. Calculates the invoice amount (plan price + applicable GST).
  3. Creates an Invoice record (status: "pending", dueAt: end of month).
  4. 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:

  1. Marks Invoice records as overdue where status = "pending" and dueAt < now.
  2. Locks subscriptions where all pending/overdue invoices have passed dueAt + gracePeriodDays — sets Subscription.status = "locked".
  3. 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:

  1. Reads the tenant’s plan credit allocation.
  2. Resets CreditBalance.balance to the plan’s monthly allocation.
  3. Writes a CreditLedger entry recording the reset.

Queue: billing__credits_reset | Concurrency: 1


Cron Scheduler

Four node-cron jobs fire automatically. All times are UTC.

JobDefault scheduleWhat it does
Monthly billing5 0 1 * * (1st of month, 00:05)Enqueues one invoice job per active paid subscription
Daily overdue check0 1 * * * (01:00 daily)Enqueues an overdue-checker job
Hourly reminders0 * * * * (top of every hour)Sends payment reminder emails for overdue invoices
Monthly credits reset10 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

ConstantQueue nameUsed for
BILLING_QUEUEbilling__invoice_generationInvoice generation jobs
OVERDUE_CHECKER_QUEUEbilling__overdue_checkerOverdue detection + lockout
CREDITS_RESET_QUEUEbilling__credits_resetMonthly 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 dev

The process logs to stdout. On clean startup you’ll see:

billing server ready Invoice, overdue, and credits-reset workers started Billing scheduler started

Shutdown

The process handles SIGTERM and SIGINT cleanly:

  1. Stops all cron tasks.
  2. Closes all three BullMQ workers gracefully (drains in-flight jobs).
  3. Closes the Redis connection.
  4. Disconnects Prisma.

© 2026 Leadmetrics — Internal use only