Invoice Module
Full billing system with clients, items, categories, transactions, and recurrences. Supports Stripe and Interac payments, automatic interest on overdue invoices, email reminders, and PDF generation.
Features
- Invoices with automatic numbering (
CLIENTYEAR##, e.g.0120250l) - Public payment page via secure token (Stripe, Interac, or manual)
- Statuses:
draft→sent→partial/overdue→paid/cancelled - Reusable items catalogued by categories
- Transactions recorded automatically (Stripe webhook) or manually
- Recurrences: automatic invoice generation at configurable intervals
- Interest: automatic interest on overdue invoices (configurable rate and grace period)
- Email reminders: automatic reminders before and after the due date
- PDF: invoice and payment receipt
- Client dashboard: "my invoices" portal for logged-in users
Installation
1. Environment variables
Copy variables from .env.example into your .env:
2. Stripe Webhook Configuration
Get your webhook URL:
- Production:
https://yourdomain.com/zen/api/webhook/stripe - Development:
http://localhost:3000/zen/api/webhook/stripe
Configure in Stripe Dashboard:
- Go to Stripe Dashboard > Developers > Webhooks
- Click "Add endpoint"
- Enter your webhook URL
- Select events:
checkout.session.completedpayment_intent.succeededpayment_intent.payment_failed
- Click "Add endpoint"
Add webhook secret to .env:
- Click on the endpoint in Stripe Dashboard
- Copy the "Signing secret" (starts with
whsec_) - Add to
.env:STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
Test webhook (optional):
stripe listen --forward-to localhost:3000/zen/api/webhook/stripe
3. Database tables
Tables are created automatically with npx zen-db init. For reference, here are the module tables:
| Table | Description |
|---|---|
zen_invoices |
Invoices |
zen_invoice_items |
Invoice line items |
zen_invoice_reminders |
Sent reminder history |
zen_invoice_interac |
Interac details per client |
zen_transactions |
Received payments |
zen_recurrences |
Recurring billing configurations |
zen_items |
Reusable item catalogue |
zen_categories |
Item categories |
This module depends on
zen_clients(clients module).
Admin interface
| Page | URL |
|---|---|
| Invoice list | /admin/invoice/invoices |
| Create invoice | /admin/invoice/invoices/new |
| Edit invoice | /admin/invoice/invoices/edit |
| Item list | /admin/invoice/items |
| Create item | /admin/invoice/items/new |
| Edit item | /admin/invoice/items/edit |
| Category list | /admin/invoice/categories |
| Create category | /admin/invoice/categories/new |
| Edit category | /admin/invoice/categories/edit |
| Transactions | /admin/invoice/transactions |
| Add transaction | /admin/invoice/transactions/new |
| Recurrences | /admin/invoice/recurrences |
| Create recurrence | /admin/invoice/recurrences/new |
| Edit recurrence | /admin/invoice/recurrences/edit |
Public pages (no authentication)
| URL | Description |
|---|---|
/invoice/:token |
Invoice payment page |
/invoice/:token/pdf |
Invoice PDF viewer |
/invoice/:token/receipt |
Payment receipt PDF |
Each invoice gets a unique 64-character hex token on creation. This token is the only access method for the client.
Admin API (authentication required)
Invoices
| Method | Route | Description |
|---|---|---|
GET |
/zen/api/admin/invoices |
Invoice list (paginated) |
GET |
/zen/api/admin/invoices?id={id} |
Invoice by ID |
POST |
/zen/api/admin/invoices |
Create an invoice |
PUT |
/zen/api/admin/invoices?id={id} |
Update an invoice |
DELETE |
/zen/api/admin/invoices?id={id} |
Delete an invoice (rejected if paid) |
List parameters:
| Parameter | Default | Description |
|---|---|---|
page |
1 |
Current page |
limit |
20 |
Results per page |
search |
— | Text search |
status |
— | Filter by status |
client_id |
— | Filter by client |
sortBy |
created_at |
Sort field |
sortOrder |
DESC |
ASC or DESC |
Invoice statuses:
| Status | Description |
|---|---|
draft |
Draft — not sent to client |
sent |
Sent — awaiting payment |
partial |
Partially paid |
overdue |
Overdue (past due date) |
paid |
Paid in full |
cancelled |
Cancelled |
Items
| Method | Route | Description |
|---|---|---|
GET |
/zen/api/admin/items |
Item list |
GET |
/zen/api/admin/items?id={id} |
Item by ID |
POST |
/zen/api/admin/items |
Create an item |
PUT |
/zen/api/admin/items?id={id} |
Update an item |
DELETE |
/zen/api/admin/items?id={id} |
Delete an item |
Categories
| Method | Route | Description |
|---|---|---|
GET |
/zen/api/admin/categories |
Category list |
GET |
/zen/api/admin/categories?id={id} |
Category by ID |
POST |
/zen/api/admin/categories |
Create a category |
PUT |
/zen/api/admin/categories?id={id} |
Update a category |
DELETE |
/zen/api/admin/categories?id={id} |
Delete a category |
Transactions
| Method | Route | Description |
|---|---|---|
GET |
/zen/api/admin/transactions |
Transaction list |
GET |
/zen/api/admin/transactions?id={id} |
Transaction by ID |
GET |
/zen/api/admin/transactions?invoice_id={id} |
Transactions for an invoice |
GET |
/zen/api/admin/transactions?client_id={id} |
Transactions for a client |
POST |
/zen/api/admin/transactions |
Record a payment |
PUT |
/zen/api/admin/transactions?id={id} |
Update a transaction |
When creating a transaction, pass
send_confirmation_email: truein the body to send a confirmation email to the client.
Recurrences
| Method | Route | Description |
|---|---|---|
GET |
/zen/api/admin/recurrences |
Recurrence list |
GET |
/zen/api/admin/recurrences?id={id} |
Recurrence by ID |
POST |
/zen/api/admin/recurrences |
Create a recurrence |
PUT |
/zen/api/admin/recurrences?id={id} |
Update a recurrence |
DELETE |
/zen/api/admin/recurrences?id={id} |
Delete a recurrence |
Client API (user authentication required)
GET /zen/api/invoices/me
Returns invoices linked to the current user's client account. Invoices with draft status are never exposed.
Parameters: page, limit, status, sortBy, sortOrder
Response:
{
"success": true,
"linkedClient": {
"id": 1,
"client_number": "01",
"company_name": "Acme Inc.",
"first_name": "Jean",
"last_name": "Tremblay",
"email": "jean@exemple.com"
},
"invoices": [...],
"total": 5,
"totalPages": 1,
"page": 1,
"limit": 20
}
If the user account is not linked to a client,
linkedClientisnullandinvoicesis[].
Interest on overdue invoices
Interest is calculated automatically every 5 minutes (cron) on invoices with statuses sent, partial, and overdue whose due date has passed.
Formula (simple interest):
daily_rate = monthly_rate / 30.4375
daily_interest = principal × daily_rate
principal=total_amount - paid_amount- Calculation starts after the grace period (
ZEN_MODULE_INVOICE_INTEREST_GRACE_DAYS) - Interest is cumulative (added to the invoice's
interest_amountfield) - One update per day per invoice (internal deduplication)
Automatic reminders
The invoice-reminders cron runs every 5 minutes and sends emails:
- Pre-due reminder: X days before the due date (configured per invoice via
first_reminder_days) - Overdue reminder: after the due date
- Admin notification: email sent to
ZEN_MODULE_INVOICE_OVERDUE_EMAILwhen an invoice becomes overdue
One reminder of each type is sent per day per invoice.
Recurrences
The invoice-recurrences cron runs every 5 minutes (between 8am and 5pm) and automatically creates new invoices based on the configured frequency. Generated invoices are created with draft status and can be sent manually.
Invoice numbering
Format: CLIENT_NUMBER + YEAR + SEQUENCE
Example: client 01, year 2025, 3rd invoice → 01202503
The sequence resets each year per client.