WHMCS is built for hosting, but it works as a SaaS billing system once you configure the right primitives. Subscription products, configurable options, metered usage, prorated upgrades, trial periods — all supported. The trick is knowing which WHMCS features map to which SaaS concepts.
I've set up WHMCS for SaaS billing for several non-hosting clients — IPTV platforms, license-key sales, managed services. Here's the configuration playbook.
WHMCS primitives mapped to SaaS concepts
| SaaS concept | WHMCS feature |
|---|---|
| Subscription plan | Product (with billing cycle set to monthly / annual) |
| Tiered pricing | Multiple products in a Product Group, or one product with Configurable Options |
| Per-seat pricing | Configurable Options with "Quantity" type |
| Free trial | Free Setup + first month free coupon, OR custom trial logic via hooks |
| Upgrades / downgrades | Built-in upgrade flow + ChangePackage module function |
| Metered / usage-based | Custom: API to push usage, hook to bill on next invoice |
| Prorated changes | Built-in — WHMCS handles proration calculations |
| Dunning (failed payment recovery) | Built-in automation: reminders, suspension after N days |
| Customer portal | Client area (with custom theme) |
| API for self-service | WHMCS API + custom endpoints in addon modules |
Step 1 — Set up your product structure
For most SaaS, you want three tiers: Starter / Pro / Enterprise. Each is a separate WHMCS product, in a single Product Group.
- Setup → Products/Services → Product Groups → Create New Group — name it "SaaS Plans" or similar.
- Setup → Products/Services → Products → Create New Product for each tier:
- Product Type: Other (if you're using a custom provisioning module) or Server for cPanel-style provisioning.
- Name: e.g., "Pro Plan".
- Pricing: enable monthly + annual cycles. Set annual as ~17% discount over monthly (encourages annual signups).
- Module Settings: hook to your provisioning module (see my provisioning module guide).
Step 2 — Configurable options for per-seat / per-resource billing
If your pricing scales by seats, projects, GB of storage, etc.:
- Setup → Products/Services → Configurable Options → Create New Group.
- Add an option:
- Option Name: "Seats" or "Storage GB".
- Option Type: Quantity.
- Price Per Unit: e.g., $10 per seat per month.
- Minimum / Maximum: set sane defaults.
- Assign the configurable-option group to your products.
Now the customer picks their seat count at checkout. WHMCS calculates the price. On upgrades (e.g., adding 5 seats mid-cycle), WHMCS prorates automatically.
Step 3 — Free trial setup
WHMCS doesn't have a native "trial" concept, but two approaches work:
Approach A: Coupon-based trial (simplest)
Create a 100% off coupon for the first month:
- Setup → Promotions → Create Promotion.
- Type: Percentage. Value: 100. Cycles: 1.
- Lifetime promotion limit: 1 use per client.
- Promotional code:
TRIAL30(or auto-apply via the order form).
Customer pays $0 first month, full price on month 2. They need to have entered a payment method (set in product config: "Require payment method at signup").
Approach B: Real trial via custom hooks
For more control (e.g., 14-day trial, no card required upfront), use the AcceptOrder + InvoicePaid hooks:
add_hook('AcceptOrder', 1, function ($vars) {
foreach ($vars['serviceids'] as $serviceId) {
$service = \WHMCS\Database\Capsule::table('tblhosting')->where('id', $serviceId)->first();
if ($service->packageid == YOUR_TRIAL_PRODUCT_ID) {
\WHMCS\Database\Capsule::table('tblhosting')
->where('id', $serviceId)
->update([
'nextduedate' => now()->addDays(14)->toDateString(),
'amount' => 0, // no charge during trial
]);
}
}
});
On day 14, the renewal cycle fires the normal invoice. If they cancel before, they're never billed.
Step 4 — Dunning configuration (failed-payment recovery)
Failed payment recovery is where SaaS revenue is lost or saved. Configure aggressively:
Setup → Automation Settings:
- Invoice Generation Days In Advance: 14 (gives customer time to update card before charge).
- Send Invoice Reminders: enable. Configure templates for 3 days before due, on due date, and 1/3/7 days overdue.
- Auto-Suspension: 7 days overdue. (Adjust per your business — SaaS typically suspends earlier than hosting.)
- Auto-Termination: 30 days overdue.
- Payment Reminder Days: 1, 3, 7 (gives multiple chances).
Also enable Captured Card Retry Logic if your gateway supports it. Stripe will automatically retry failed charges over 3-7 days using "smart" timing. Configure in Stripe Dashboard → Settings → Smart Retries.
Step 5 — Upgrade and downgrade flows
WHMCS handles upgrades natively. Configure:
Setup → Products/Services → Products → edit → Upgrades:
- Check which other products this product can upgrade to.
- Set proration to Use Customer's Existing Credit Toward Upgrade.
Customer experience: in their client area, "Upgrade/Downgrade" button on the service. They pick a new plan. WHMCS calculates the prorated amount, generates an invoice, charges it. On payment, the ChangePackage function in your provisioning module runs.
Implement ChangePackage properly in your module — see my provisioning module guide.
Step 6 — Usage-based / metered billing
This isn't built in but is doable. Pattern:
- Your SaaS app tracks usage (API calls, GB transferred, etc.).
- A nightly cron job in your SaaS pushes the accumulated usage to WHMCS via API.
- A WHMCS hook on
InvoiceCreationPreEmailadds the usage line item to the invoice before it goes out.
// In your SaaS app (Node, Python, whatever):
// Nightly job pushes usage to WHMCS
POST https://yourwhmcs.com/api.php
{
"action": "AddCredit", // or store in a custom field
"clientid": 42,
"description": "Overage: 1,200 API calls in May",
"amount": "12.00",
...
}
// Or use a more nuanced approach: store in a custom usage table, render on invoice
add_hook('InvoiceCreationPreEmail', 1, function ($vars) {
$invoiceId = $vars['invoiceid'];
$userId = $vars['userid'];
// Find usage for the period this invoice covers
$usage = \WHMCS\Database\Capsule::table('mod_saas_usage')
->where('client_id', $userId)
->where('billed', 0)
->sum('overage_cost');
if ($usage > 0) {
\WHMCS\Database\Capsule::table('tblinvoiceitems')->insert([
'invoiceid' => $invoiceId,
'userid' => $userId,
'type' => 'Other',
'description' => 'Usage overage',
'amount' => $usage,
'taxed' => 1,
]);
// Mark usage as billed
\WHMCS\Database\Capsule::table('mod_saas_usage')
->where('client_id', $userId)
->where('billed', 0)
->update(['billed' => 1, 'invoiceid' => $invoiceId]);
}
});
Step 7 — Customer portal customization
WHMCS's default client area looks like a billing system. For SaaS, customers expect a product dashboard. Two options:
- Custom WHMCS theme — rebuild the visual layer so the WHMCS billing pages feel like part of your product. See my theme structure guide.
- Embed WHMCS pages into your SaaS app — use WHMCS API to render billing data inside your own product UI. Customer never sees WHMCS branding.
For most SaaS, approach 2 is the better experience. WHMCS becomes a billing backend; your SaaS app is the customer-facing product.
How to verify the SaaS setup
- Sign up for the trial. Confirm no charge, trial expiry date is set, your provisioning runs.
- Wait for the trial to expire (or fast-forward the next-due-date manually). Confirm invoice generates, charges card, service continues.
- Upgrade from Starter to Pro. Confirm prorated invoice, charge succeeds, your
ChangePackageruns. - Cancel the card. Confirm the failed-payment reminder sequence fires.
- Re-add a working card from the client area. Confirm the retry succeeds.
- Cancel the subscription from the client area. Confirm
TerminateAccountruns on next cycle.
Run this full drill once when you set up, and again any time you change automation settings.
Common pitfalls
"Configurable options pricing doesn't show up on the order form." The options group isn't assigned to the product. Configurable Options → edit group → check the product checkbox.
"Prorated upgrade charges the wrong amount." WHMCS uses a fixed proration algorithm. If your billing cycles don't align (e.g., customer upgrades on day 7 of a 30-day cycle), the math may surprise customers. Document the proration in your pricing page.
"Trial customers convert at low rates." Either:
- Card not required — fewer convert because there's friction at conversion. Make card required at trial signup.
- Trial is too long — customers forget about it. 14 days > 30 days for most SaaS.
- You're not communicating during the trial. Add hook-based onboarding emails on day 1, 3, 7, 12.
"Customer cancelled but kept being charged." WHMCS distinguishes between "cancellation request" and "cancellation effective." Cancellation requests trigger when the cycle ends, not immediately. To cancel immediately, also set the service status to "Cancelled" via admin or API.
My take — WHMCS for SaaS vs. building from scratch
I've shipped SaaS billing both ways: WHMCS-as-backend and full custom Laravel + Stripe. My rule:
- WHMCS if you have <5,000 customers, standard billing cycles, and don't need exotic billing logic. You'll ship in weeks, not months.
- Custom (Laravel + Stripe Billing) if you have unusual rules (per-API-call billing with complex tiering, multi-product invoices, complex revenue recognition), or need first-class engineering on the billing surface.
The middle ground I've used: WHMCS for billing + invoicing, your custom app for product, glued together by WHMCS API. Best of both for most SaaS in the small-to-mid range.
Going further
I configure WHMCS for SaaS billing — product structure, configurable options, trials, dunning, usage-based billing, custom portal. If you're trying to bend WHMCS into a SaaS-shaped tool, tell me what you're building and I'll send a quote in 24 hours.