Recurring Payments and Dunning

Everything you need to go from a fresh Perfex CRM install to money arriving in your account automatically — and to getting it back when a card declines.

This guide is written to be read once, top to bottom, the day you install the module. Every screen it mentions is a screen you can open right now; every default it quotes is the default the installer writes.

The module dashboard: MRR and recovery figures, a needs-attention panel, and cron health

1. What this does

Perfex CRM already creates recurring invoices. It does not collect them: the host vendor's own documentation says the recurring feature re-creates the document. Somebody still has to chase the money.

This module is the collection half. It stores a payment authorization the customer gives you once, charges it off-session when an invoice comes due, records the result as a real Perfex payment, and — when the charge fails — runs a retry and e-mail sequence until the money arrives or you decide to stop.

Two modes

You choose the mode per subscription. They can both run on the same install at the same time.

ModeWho owns the billing clockUse it when
Auto-charge a recurring invoice Perfex. Its recurring cron keeps generating the invoices exactly as it does today; the module collects each one as soon as it appears. You already run recurring invoices and you only want them paid without chasing.
Subscription plan The module. It builds the invoice from a plan you defined (interval, amount, trial, setup fee, taxes, items) and charges it on schedule. You are selling a subscription product and want plans, trials and cancellation semantics.

In both modes the outcome is identical where it matters: a numbered Perfex invoice, a Perfex payment row carrying the gateway transaction id, and an audit trail on the Charge Attempts screen.

What happens when a payment fails

A decline is not one thing. The module reads the gateway's decline code and picks a different answer for each kind:

Retrying a hard decline is not merely useless: card networks bill for the attempts and issuers read repeated retries as fraud probing. That distinction is the reason this module exists.

New in version 1.1

Four additions. Three of them change how you bill or what your customers are told, and all three are off when you install and off when you upgrade — each has its own switch that you turn on yourself. The fourth is a report that reads what you already have. Until you turn a switch on, the module charges, retries and duns exactly as 1.0 did.

AdditionWhat it isThe switch, and where it is
Late fees A penalty that accrues against an overdue invoice and is billed on a separate invoice. The overdue invoice itself is never edited. See section 8. autobill_latefee_enabled, default off. On the Late Fees screen's own settings page, behind the Late fee settings button.
Renewal and expiry tracking A ledger of dated things Perfex does not model — domains, SSL certificates, hosting accounts, licences — with reminders before the date. See section 9. autobill_renewals_enabled, default off. On the module's Settings screen.
Suspension and termination Two more dunning terminal actions, and two more subscription statuses. It suspends billing, never a customer's login. See section 7.8. Chosen per dunning policy. No policy uses either action until you pick one, and the seeded Default policy does not.
AR aging report Who owes you what, and for how long, by customer and currency. Read-only. See section 11.4. No switch. It bills nothing and e-mails nothing — it reads invoices you already have — so it is the one addition that is there after an upgrade without you doing anything.

Upgrading from 1.0

Upgrading is the same action as installing: download the new archive from your CodeCanyon downloads page and upload it under Setup → Modules → Upload Module. Perfex compares the version header and runs the 1.1 migration once. Your settings, subscriptions, stored payment authorizations, dunning policies and charge history are kept.

Upgrading changes nothing until you switch a feature on yourself. The migration adds schema and defaults. It does not create an invoice, record a payment, send an e-mail, alter a subscription's status, or write a single row your customers could see. What you will notice afterwards are two new entries in the module menu, and neither of them acts: AR Aging, which is a read-only report, and Late Fees, which is shown to staff who may configure the module because the switch that enables late fees lives on it. Everything else in 1.1 stays invisible until you opt in.

What the migration does, and only this:

Those three sweeps do nothing on an install that has opted into nothing. Late fees and renewals each read their own switch and return immediately while it is off. Terminations has no switch of its own, because there is nothing to switch off until a dunning policy is set to suspend, then terminate; on an upgraded install none is, so it looks for subscriptions past their waiting period and finds none.

An option that already exists is never rewritten, so a buyer who upgrades, opts in, and later re-runs the migration keeps their choice. Existing dunning policies keep the terminal action they already had.

The five switches, and what they ship as:

OptionShips asWhat stays off while it is off
autobill_latefee_enabled0 Nothing accrues, no fee is calculated, no fee invoice is raised.
autobill_latefee_auto_invoice0 An accrued fee waits for a staff member to raise its invoice by hand.
autobill_renewals_enabled0 The renewal sweep does not run: no reminder is e-mailed, no renewal invoice is raised, whatever an individual renewal row is set to.
autobill_suspend_notify_customer0 Suspension and termination are recorded and your staff are notified, but the customer is not e-mailed.
autobill_aging_include_late_fees0 One column on a read-only report is hidden. Nothing else.

Two of the late-fee rate options ship at zero as well — autobill_latefee_percent at 0.00 and autobill_latefee_fixed_minor at 0 — so even someone who flipped the master switch by hand in the database would compute a fee of nothing until a rate is set.

This is checked rather than promised. As its last step the migration runs three assertions: that each of the five switches above is declared off in its own seed table, that both rate options are declared at zero there, and that every row it inserted on this run reads back from the database exactly as it was written. If any of the three fails, it aborts with a message naming the offending option instead of finishing. Perfex only records the new version number after a migration returns, so an aborted upgrade leaves the install on 1.0 to be retried — never half-migrated, and with every switch still off.

What it does not do

Read this list before you buy or before you promise something to a customer. Everything here is a deliberate boundary, not a bug:

This is an independent third-party module for Perfex CRM. It is not published by, affiliated with or endorsed by the Perfex CRM authors, and it is not affiliated with Stripe, GoCardless or Paystack.

2. Requirements

RequirementDetail
Perfex CRM3.4.x (the module declares Requires at least: 3.4.*)
PHP8.1, 8.2, 8.3 or 8.4
DatabaseThe MySQL/MariaDB server your Perfex already runs on. Tables are created with your install's own character set and collation.
HTTPSRequired. Gateways will not deliver webhooks to a plain-HTTP endpoint, and card capture redirects back to your site.
A working Perfex cronMandatory. See the warning below.
Outbound HTTPSYour server must be able to reach api.stripe.com, api.gocardless.com or api.paystack.co. Locked-down hosting that blocks outbound connections cannot charge anything.
Working e-mailPerfex's own SMTP settings. Every dunning notice, receipt and confirmation link is sent through Perfex's mail system.
A gateway accountAt least one of Stripe, GoCardless or Paystack, with API keys. See Gateway setup.

Nothing is collected without the Perfex cron. Automatic charges, retries, dunning e-mails, card-expiry warnings, webhook catch-up and the daily reporting snapshot all run inside the standard Perfex cron job. If the cron is not running, the module is silent: no charges, no e-mails, no errors — which is exactly what makes a dead cron so expensive. Set it up first (step 1 of the checklist), and keep an eye on the Cron Health panel on the dashboard.

Setup, Settings, Cron Job: the CRON COMMAND line and the Run Cron Manually link

3. Installation

  1. In Perfex, go to Setup → Modules.
  2. Click Upload Module and choose the ZIP file from your purchase download. (If your host rejects the upload because of file-size limits, unzip the archive and upload the autobill folder into modules/ over FTP instead — the folder name must stay exactly as it is in the archive.)
  3. The module appears in the list. Click Activate.
  4. Reload the admin area. A new sidebar entry named Recurring Payments appears between Sales and Subscriptions, with Dashboard, Subscriptions, Plans, Payment Methods, Charge Attempts, Dunning, Reports, AR Aging and Settings under it. Two more entries are conditional: Late Fees is shown to staff who may manage the module's settings — the switch that enables late fees lives on that screen, so hiding it would put the feature out of reach — and to everybody else only once late fees are on. Renewals does not appear at all until renewals are enabled.
Setup, Modules: the module row with the Activate action

What activation creates

CreatedDetail
13 database tablesAll prefixed autobill_ after your own table prefix: payment methods, plans, schedules, charge attempts, gateway events, dunning policies, the dunning log, coupons, usage records, MRR snapshots, cron state, late fees and tracked renewals. The last two are created empty and stay empty until you switch their feature on.
33 settingsWritten into Perfex's own options table, every one with a working default. The ones you would change are on the module's Settings screen, except the eleven late-fee options, which are edited on the Late Fees screen's own settings page. Every setting that turns a version 1.1 behaviour on is written at its off value — see Upgrading from 1.0.
11 e-mail templatesSeeded into Setup → Email Templates, inside the Invoice group, all named with the Recurring Payments: prefix — Dunning 1 (Payment Failed), Dunning 2 (Payment Failed Again), Dunning 3 (Final Notice), Card Expiring Soon, Payment Authentication Required, Payment Receipt, Payment Authorization Cancelled, Upcoming Payment Notice, and the three added in 1.1: Service Suspended, Service Terminated and Renewal Reminder. All eleven are plain Perfex templates — edit the wording, the subject and the language versions exactly as you would any other. The last three are only ever sent once you have turned on the feature behind them.
1 dunning policyNamed Default, with the schedule described under Dunning. It is used by every subscription that does not name another policy.
7 staff capabilitiesAvailable immediately in Setup → Staff → Roles. See Permissions.
3 payment gatewaysThree entries appear in Setup → Settings → Payment Gateways: Stripe - Card for Recurring Payments, GoCardless - Direct Debit for Recurring Payments and Paystack - Card for Recurring Payments. They are inactive and empty until you enter credentials, and they are separate from any Stripe or PayPal gateway you already had.

Re-activating is safe. Every table creation is guarded, settings are only written when they do not already exist, an e-mail template is only seeded when its slug is missing, and the default policy is only created when the policy table is empty. Deactivating and activating again never resets your configuration and never duplicates anything.

What "Deactivate" keeps

Deactivating stops the module without destroying anything:

Re-activating picks everything up where it left off. Removing the module is a separate action with its own rules — see Uninstalling.

4. First run checklist

Five things, in this order. The last one tells you whether the first four worked.

4.1 Set up the cron

  1. Go to Setup → Settings → Cron Job. The Command tab shows the exact command for your install, in the form wget -q -O- https://your-perfex-site/cron/index (with your cron key appended when your app-config.php defines one).
  2. Add that command to your server's crontab so it runs every 5 minutes. Perfex throttles itself internally, so running it often is safe and makes charges and retries happen promptly.
  3. For an immediate first test, administrators can click Run Cron Manually on the same screen.
  4. Open the module's Dashboard and look at the Cron Health panel. Once the cron has run, it lists the module's eleven jobs with their last run, last success, run count and error count.

The eleven jobs, in the order they run: catch-up, due charges, retry queue, dunning e-mails, terminations, expiring cards, unhandled webhooks, late fees, renewals, reconcile, MRR snapshot. One failing job never stops the others — it records its error and the rest continue.

Three of those are new in 1.1, and on an install that has opted into nothing all three run, record a successful run, and do nothing. Late fees and renewals each read their own switch first and return immediately while it is off. Terminations has no switch of its own: it looks for subscriptions that a dunning policy suspended under the suspend, then terminate action and that have sat there longer than the waiting period, and until you point a policy at that action there are none to find.

4.2 Enter gateway credentials

Go to Setup → Settings → Payment Gateways and open the entry you need: Stripe - Card for Recurring Payments, GoCardless - Direct Debit for Recurring Payments or Paystack - Card for Recurring Payments. Enter the keys, tick Active, and list the currencies that gateway may take.

Per-gateway keys, webhook events and sandbox notes are on the Gateway setup page.

4.3 Create the webhook endpoint

Webhooks are how the gateway tells you what really happened — a bank debit that confirmed three days later, a 3-D Secure challenge the customer finally passed, a dispute, a card the network auto-updated. Without a webhook the module still charges, but it will not learn about anything that resolves after the API call returns.

  1. Open Settings → Diagnostics in the module. Each gateway block shows a read-only Webhook URL field — copy it. The shape is https://your-perfex-site/autobill/autobill_webhook/stripe (and …/gocardless, …/paystack).
  2. In the gateway dashboard, create an endpoint pointing at that URL and subscribe it to the events listed on the Gateway setup page.
  3. Paste the endpoint's signing secret back into that gateway's settings in Perfex. Paystack is the exception: it signs webhooks with your secret key, so there is no separate secret to paste and the Diagnostics screen does not ask for one.

An endpoint without its signing secret is worse than no endpoint: every delivery will be rejected as unverified, and the module will never confirm those payments. Diagnostics flags a missing secret in red.

4.4 Check your currencies

This is the single most common reason a correctly configured install charges nothing, so it gets its own step. Perfex hides a payment gateway from an invoice whose currency the gateway does not list. If your invoices are in EUR and the gateway's Currencies field says only USD, then:

Diagnostics compares your system currencies against each gateway's list and prints "Not enabled for: …" when they disagree.

4.5 Read the Diagnostics screen

Settings → Diagnostics is the screen to open whenever "nothing is charging". It collects everything that can silently stop collection in one place.

Settings, Diagnostics tab: counters, per-gateway credential and currency checks, and the engine self-check
What it saysWhat it meansWhat to do
Unprocessed webhooks Gateway events that were received and stored but not yet processed. A small number is normal between cron runs — the "unhandled webhooks" job drains them. A number that never falls means those events keep failing; they are retried up to five times and then abandoned with one staff notification.
Stuck attempts Charge attempts that were opened more than an hour ago and never reached a final state. Usually a webhook that never arrived. Bank debits legitimately sit in transit for days. The daily reconcile job raises a staff notification once an attempt has been unresolved for more than 26 hours; resolve those from the gateway dashboard.
Base currency Your Perfex base currency, for comparison with the gateway lists below. Nothing, unless a gateway does not list it.
Enabled / Not configured (per gateway) Whether that gateway is switched on in Perfex's payment-gateway settings. A gateway that is not active cannot be offered to customers, and a plan invoice built while it is inactive is created with no payment mode set on it at all — see section 6.3.
API credentials: Set / Missing Presence only. Keys are never decrypted, printed or logged — the screen is safe to screenshot. Missing means no charge can be sent. Enter the keys.
Webhook signing secret: Set / Missing Whether the endpoint secret has been pasted in. Not shown for Paystack, which has none. Missing means confirmations will never arrive. See step 4.3.
Webhook URL The exact endpoint to register in the gateway dashboard. Copy it from here rather than typing it.
Enabled currencies The gateway's own currency list, plus any of your system currencies missing from it. Add the missing codes in the gateway settings, or stop invoicing in them.
Engine self-check The charging engine's own view: per-gateway configuration, cron age against your alert threshold, the eleven jobs with run and error counts, unprocessed events, stuck attempts, subscriptions waiting for a payment method, and stored methods grouped by status. "never ran" or a stale age means the cron is not reaching the module. Subscriptions needing a payment method need the customer, not you — see the portal.

4.6 Optional: review the general settings

The late-fee options are not on this screen. They live on the Late Fees screen's own settings page, behind the Late fee settings button, so that one feature has one write path — see section 8.3.

Settings, General tab: charging, recovery, GoCardless, Paystack and data options
SettingDefaultWhat it does
Auto-charge every recurring invoice when the customer has a saved default payment method Off On: every recurring invoice Perfex generates is collected automatically as soon as the customer has a stored default method — no per-invoice subscription needed. Off: only the recurring invoices you explicitly attached.
Send receipt e-mail on successful chargeOn Perfex already sends its own payment confirmation; leave this on only if you want the module's receipt as well.
Warn when a customer's currency is not enabled on the gatewayOn Surfaces the currency trap described in 4.4.
Card-expiry warning e-mails (days before)30,7 One warning per listed day, per card. Empty sends none.
Authentication link lifetime (hours)72 How long a 3-D Secure confirmation link stays usable.
Catch-up window (days)7 After downtime, how far back the module looks for work it missed.
Warn when the cron has not run for (hours)26 Past this, the dashboard shows a red banner and staff are notified.
GoCardless advance noticeGoCardless sends it Locked. See the limitations in section 1.
Record GoCardless payment in Perfex whenPayment is confirmed "Confirmed" marks the invoice paid when GoCardless confirms collection; "paid out" waits until the money reaches your bank — later, but more conservative.
Paystack card-verification amount5000 minor units Paystack has no setup-only flow, so saving a card without an invoice runs a small verification charge. 5000 = 50.00 NGN.
Renewals — the whole block is inert while the first row is off. See section 9.
Send renewal reminders and process renewalsOff The switch for renewal tracking. While it is off the renewal sweep does not run: no reminder is e-mailed and no renewal invoice is raised, whatever an individual renewal is set to. The Renewals menu entry is hidden until it is on.
Default reminder days for new renewals60,30,14,7,1 The starting value on the renewal form. Each renewal can override it.
Renewal invoice line descriptionEmpty The line wording on an auto-raised renewal invoice. {label} and {identifier} are substituted. Left empty, the module uses its own translated wording, which is why the shipped value is empty rather than an English sentence.
Suspension & termination — see section 7.8.
E-mail the customer when a subscription is suspended or terminatedOff While off, suspension and termination still happen and your staff are still notified — the customer is not e-mailed. Turn it on and the Service Suspended and Service Terminated templates are used.
Terminate a suspended subscription after this many days30 Only used by a dunning policy whose terminal action is Suspend the subscription, then terminate it. No policy uses that action until you choose it. It sets how long the suspension lasts, not whether termination happens — to suspend without ever terminating, give the policy the Suspend the subscription action instead.
AR aging report — see section 11.4.
Show accrued late fees on the aging reportOff Adds one column. Accrued fees are shown on their own and are never added into the outstanding total.
Create invoices as staff memberFirst active staff member Which staff member owns the invoices the plan mode generates.
Delete ALL data when the module is uninstalledOff See Uninstalling.

5. Mode 1 — auto-charging your existing recurring invoices

Nothing about your recurring invoices changes. Perfex still generates them on its own schedule, with your numbering, your items and your terms. The module simply collects each one.

5.1 The customer saves a payment method

This step can only be done by the customer, in the client portal. No screen in the admin area accepts card details, and that is not an oversight: keeping card data off your server is what keeps your PCI obligations small.

  1. The customer logs into the client portal and opens Billing & Cards in the customer-area menu.
  2. They click Add payment method and choose how they want to pay — credit/debit card, or bank debit where GoCardless is configured. Only gateways that can settle the customer's currency are offered.
  3. They tick the authorization statement ("I authorize your company to automatically charge my saved payment method for recurring invoices until I cancel this authorization"). It is mandatory.
  4. They are sent to the gateway's own hosted page to enter the details, and come back to your site afterwards.
Client portal: choosing a payment method type and accepting the authorization text

What is stored on your server: the gateway's reusable token, the brand, the last four digits, the expiry month and year, and a snapshot of the authorization the customer accepted (the exact wording, the version, their IP address, their browser and the timestamp). The card number and the security code never touch Perfex.

Staff can see the stored methods — never the numbers — on Payment Methods, filter them by customer, gateway or status, set a customer's default, revoke one at the gateway, or delete one. Revoking or deleting a method releases the subscriptions that used it, and they then wait for a new one. A suspended subscription is released too, but keeps its suspended status: detaching a card is not a decision about collection, and it does not undo one.

Payment Methods screen: stored authorizations per customer with brand, last four digits, expiry and status

5.2 Create the schedule

  1. Go to Subscriptions → New Subscription.
  2. Choose the mode Auto-charge recurring invoice.
  3. Pick the recurring invoice. The list only offers recurring invoices that are not cancelled, not draft, and do not already have a subscription — one recurring invoice, one subscription.
  4. Pick one of that customer's saved payment methods. If they have none, save anyway: the subscription is created with the status Needs Payment Method and starts working by itself the moment the customer adds one.
  5. Optionally set Collect this many days before the invoice due date. Leave it at 0 for cards; bank debit schemes need a few days of notice.
  6. Optionally choose a dunning policy other than the default, and a cycle limit.
New subscription form with the mode selector, the recurring-invoice picker and the payment-method picker

The alternative is the blanket switch: turn on Auto-charge every recurring invoice… in Settings and every recurring invoice belonging to a customer with a stored default method is collected automatically, with the subscription created for you the first time it fires.

5.3 What happens when the invoice generates

Perfex generates recurring invoices during its cron, at the hour set in Setup → Settings → Cron Job → Invoice. The moment a child invoice is created, the module takes over:

  1. It finds the subscription attached to the parent recurring invoice.
  2. It skips the charge entirely if the subscription is cancelled, completed or paused — or if the customer asked to cancel at period end and this is that period end, in which case the subscription is closed instead of billed.
  3. It resolves the payment method: the one on the subscription, or the customer's default if that one is no longer usable. With none, the subscription flips to Needs Payment Method and nothing is charged.
  4. It checks the clock. Charges are meant to land in the customer's local small hours (2 a.m. by default, set per dunning policy). Inside a one-hour window of that time it charges immediately; outside it, the invoice is queued for the next occurrence of that hour and the regular cron sweep collects it.
  5. It charges the amount still left to pay on the invoice — a part payment already recorded is never collected twice.

5.4 Where the result appears

Every charge, successful or not, produces one row on Charge Attempts: the invoice, the customer, the gateway, the attempt number, the amount, the status, the decline code and message where there is one, the gateway transaction id, the next retry time, and the timestamps.

Charge Attempts screen filtered by status, showing attempt numbers, decline codes and retry times
Attempt statusMeaning
SucceededMoney collected. A Perfex payment exists on the invoice with the gateway transaction id, and the invoice status is recalculated by Perfex as usual.
FailedDeclined. The invoice is untouched — same number, same amount — and dunning has taken the case.
Requires AuthenticationThe bank asked for 3-D Secure. The customer has been e-mailed a confirmation link.
In TransitNormal for bank debit: submitted and travelling. It settles days later, over a webhook.
Pending / ProcessingSent, but the outcome is not known yet — usually a network problem mid-request. The module re-drives it safely under the same idempotency key.
CancelledA queued retry that was stood down because the invoice got paid another way.
Charged BackA dispute arrived over a webhook. The Perfex payment is deliberately left alone — reversing money is a human decision — and your staff are notified.

Staff with the Charge Manually capability get a Retry now action on failed and awaiting-authentication attempts, and a Charge now button on the subscription itself.

6. Mode 2 — subscription plans

6.1 Create a plan

Plans → New Plan:

FieldNotes
Plan name, descriptionThe description becomes the invoice line's long description when the plan has no items.
Currency and amountThe recurring price. A subscription may override the amount for one customer.
Setup feeAdded as its own invoice line on the first cycle only.
Billing intervalDay(s), Week(s), Month(s) or Year(s), plus "Repeat every N" — so "every 3 months" or "every 2 weeks" are both plans.
Trial (days)Nothing is charged during the trial; the first charge falls on the day the trial ends. A subscription can override this per customer.
Pricing modelFlat rate. The plan screen sells flat-rate plans only.
TaxesApplied to every line of the generated invoice.
Invoice itemsPick existing Perfex items and they become the invoice lines every time the plan bills, with their own rates and taxes. With no items, the invoice gets a single line named after the plan, at the plan's amount.
ActiveInactive plans stay attached to existing subscriptions but are not offered for new ones.
Visible in client portalReserved for a future release; there is no customer self-signup page.
Plan form: interval, trial, setup fee, taxes and invoice items

6.2 Subscribe a customer

Subscriptions → New Subscription, mode Subscription plan: choose the customer, the plan, a start date, an optional trial override, a quantity, a cycle limit (0 = unlimited), a dunning policy and a payment method.

The billing anchor is the day the first charge is owed: the trial end date when there is a trial, otherwise the start date. The subscription is created as Trialing, Active, or Needs Payment Method when no usable method is attached yet.

Customer and mode are fixed once a subscription exists — everything downstream (invoices, attempts, authorizations) is already tied to them. Everything else stays editable: payment method, amount, quantity, cycles, dunning policy and, for plan subscriptions, the next charge date.

6.3 What the generated invoice looks like

When a plan subscription comes due, the module builds a normal Perfex invoice before it charges anything:

The invoice's allowed payment mode is restricted to the module's own gateway when that gateway is active and enabled for the invoice currency, so the customer's "Pay now" button uses the same rails. When it is not — the gateway is switched off, or the invoice's currency is not on its list — the module sets no payment mode on the invoice at all, and Perfex reads an empty list as "offer nothing": the customer sees no Pay now button and the PDF carries no payment block.

That is a state to fix rather than to live with. Activate the gateway and add the currency to its list, or open the invoice and tick the payment modes you want offered. The Diagnostics screen tells you which of the two it is, per gateway, before a customer runs into it.

The document is created first, and the period advances at that moment — not when the money arrives. That is what guarantees a decline can never produce a second invoice for the same period. Retries re-use the invoice that already exists.

6.4 Cycles, pausing and cancelling

Subscription detail: period, next charge, cycles, payment method and the action buttons
ActionWhat it does
Charge now Collects the subscription's open invoice immediately. If a plan subscription has no open invoice, the next one is built from the plan and charged straight away. Requires the Charge Manually capability. If the engine refuses (velocity cap, currency block, unconfigured gateway) the screen says so instead of claiming a charge was made.
Pause Stops collection, optionally until a date. Nothing is charged while paused. Perfex keeps generating recurring invoices in mode 1 — pausing stops the collection, not the invoicing.
ResumePuts a paused subscription back to work.
Cancel at period end The subscription keeps running and stays chargeable until its current paid period closes. At the exact moment the next period would be billed, it is closed instead. The customer keeps what they paid for; you never bill a period they did not want.
Cancel immediately Closes the subscription now, clears the next charge and stops all queued retries. Invoices that are already issued stay issued — cancelling a subscription is not a credit note.

Cycles. Each billed period increments the cycle counter. With a cycle limit set, the subscription is marked Completed as soon as the last cycle is billed, and nothing further is charged. With a limit of 0 it runs until somebody cancels it.

What closes a subscription automatically: its cycle limit is reached; its plan or its customer is deleted; a dunning policy's terminal action cancels it; or the parent recurring invoice is deleted in mode 1.

Subscriptions list filtered by status and mode

7. Dunning — what happens after a decline

Dunning is the sequence of retries and messages between "the charge failed" and "the money arrived or we stopped". A policy defines that sequence; every subscription uses one, and the seeded Default policy is used by anything that does not name another.

7.1 What the default policy does, day by day

The seeded values: retries on days 1, 3, 5, 7, 10, 14; maximum 7 attempts; give up after 21 days; charge at 02:00 in the customer's local time; e-mails on days 0, 3, 7, 14, 21; terminal action Mark subscription past due; notify staff on.

DayThe moduleThe customer
0Attempt 1 — the original charge — fails. The subscription becomes Past Due. First notice, on the next cron run: friendly, names the amount and the invoice, says when the next attempt is, and links to the page where they can update their payment method.
1Attempt 2.
3Attempt 3.Second notice: firmer, names the decline reason in plain language.
5Attempt 4.
7Attempt 5.Second notice.
10Attempt 6.
14Attempt 7 — the last one the cap allows. When it fails, the terminal action runs immediately.Second notice.
21The give-up day: if the terminal action has not already run, it runs here. Final notice: this is the last automatic message.

Three details that matter more than they look:

Escalation is mapped over your offsets rather than hard-coded: the first offset gets the soft notice, the last gets the final warning, everything between gets the formal reminder. Trim [0,3,7,14,21] down to [0,7] and you still get soft → final.

7.2 When the sequence does not apply

The retry schedule is for soft declines. The classifier described in section 1 can take a case out of the sequence at any point: a hard decline stops retries and asks for a new card, a 3-D Secure decline stops retries and sends a confirmation link, a dead mandate stops everything and notifies staff. In those cases the specific message replaces the generic step that was due, so the customer gets one accurate e-mail rather than two contradictory ones.

For GoCardless there are no module retries at all. The e-mail schedule and the terminal action are the whole sequence, and the terminal action fires on the give-up day.

7.3 Editing a policy

Dunning lists your policies with how many subscriptions use each, and the dunning log underneath. Open one, or create a new one:

FieldNotes
Retry schedule (days after failure) Comma-separated whole days, counted from the failed charge. Day 0 is the original charge, so retries start at 1.
E-mail schedule (days after failure) Comma-separated. Day 0 — the same day — recovers the most.
Maximum attempts Counts the original charge. Capped at 8: beyond that, issuers start reading the retries as fraud and the card networks bill for the excess.
Give up after (days)1–365. The day the terminal action runs and collection stops.
Charge at hour (customer local time)0–23. Retries and deferred charges land at this hour in the customer's timezone.
When all retries failThe terminal action — see below.
Notify staffSends one staff notification when collection ends without payment.
Default policyMaking this the default clears the flag on every other policy. There is always exactly one, and the last remaining policy cannot be deleted.

7.4 The live preview

Dunning policy editor with the day-by-day preview timeline and its warnings

The Preview button re-draws the timeline from the values currently in the form without saving anything, so you can see the sequence before you commit to it. It draws every day in the sequence, marks each attempt and each e-mail, and greys out the steps that will never run. It also warns you about the mistakes that are invisible when the three limits are read separately:

Preview writes nothing, so it is available to anyone who can read the screen; saving needs the create or edit capability.

7.5 Terminal actions

ActionWhat it does
Mark subscription past due (default) Leaves the subscription past due, stops the retries, and asks Perfex to recalculate the invoice's own status (unpaid → overdue). The safest choice: nothing is destroyed and a later payment revives everything.
Pause subscriptionStops collection and future periods until you resume it.
Cancel subscriptionCloses the subscription. Issued invoices stay.
Suspend portal access (aggressive) Deliberately softened: it cancels the subscription and notifies staff. The module never switches off a customer's Perfex login — that login is not ours, disabling it would also lock them out of the one page where they could fix their card, and nothing would ever switch it back on. A human decides about the login.
Suspend the subscription (new in 1.1) Moves the subscription to Suspended. Collection stops, and it is reversible — a payment on the invoice brings it back by itself. See 7.8.
Suspend the subscription, then terminate it (new in 1.1) The same suspension, plus a deadline: if the invoice is still unsettled after Terminate a suspended subscription after this many days (30 by default), the subscription is moved to Terminated, which is final. See 7.8.

Exactly one terminal action runs per invoice, whichever path reaches it first, and it is recorded in the dunning log next to the e-mails.

Dunning policies list with usage counts and the dunning log of sent steps

The dunning log records every step: invoice, step number, channel, template, timestamp and whether it was sent successfully. Filter it by invoice id when a customer asks what you sent them. A mail server that is down is recorded on the step and the sequence continues — one failed e-mail never stalls the rest.

7.6 Card-expiry warnings

The cheapest recovery is the one that costs no failed charge at all. Once a day the module looks for stored cards approaching their expiry date and warns the customer, using the thresholds in Settings (30,7 by default: one e-mail a month before, one a week before).

One e-mail per threshold per card, and at most one per sweep — a card five days from expiry that was never warned gets the 7-day notice only, not both at once. Bank debit mandates have no expiry date and are not part of this sweep. Cards that the card network auto-updates are picked up over a webhook; if the network hands back a card on a different brand, the module treats the stored authorization as no longer describing what it would charge, marks it as needing re-consent and tells your staff.

7.7 Velocity protection

Independently of any policy, the module refuses to send more than 8 charge attempts against the same stored payment method within 14 days, across all invoices. Past that it stops, treats the case as "contact the customer", and notifies your staff. This ceiling exists because acquirers are fined for hammering a dead card, and no policy setting can raise it.

7.8 Suspension and termination

Version 1.1 adds two terminal actions and, with them, two subscription statuses. Neither is used by any existing policy: you choose one on the policy, and the seeded Default policy still ends at Mark subscription past due.

This suspends billing, not access. The module does not switch off the customer's Perfex login, does not disable their contact, and does not close their portal account. That was a version 1.0 decision and 1.1 keeps it: the portal is the one place the customer can replace the card that failed, and a module that locked them out would be locking them out of the fix. If you want to withdraw the service itself, that is your action to take, in whatever system delivers it.

The subscription statuses go from seven to nine:

StatusWhat it meansHow it ends
Suspended Collection has stopped: the due-charge sweep only serves active and trialing subscriptions, so a suspended one is not picked up by it, and every queued retry against the invoice that caused the suspension is disarmed at the same moment. It is not cancelled — nothing is written to the cancellation fields, and on a plan subscription the next-charge marker is left where it was, so lifting the suspension is cheap. Read the note under this table before you lift a long one. Three ways. A payment that settles the invoice — taken by the module, by core's Pay button, or typed in by your staff — puts it back to Active, or to Needs Payment Method if the stored card is no longer on the subscription; a part payment does not. The Lift suspension button on the subscription, which needs the Edit capability. Revoking or deleting the stored payment method is not a third route: the card is detached, but the suspension stands until the invoice is settled or staff lift it.
Terminated The end of the line. Reached only from Suspended, only through the suspend, then terminate action, and only after the waiting period. It does not. Nothing in the module reinstates a terminated subscription. If the invoice is paid afterwards, the subscription stays terminated and your staff are notified so a human can decide whether the customer is coming back.

Because Terminated is final, the Service Terminated e-mail deliberately carries no "update your payment method" link: it would imply that paying brings the subscription back, and it does not. The Service Suspended e-mail does carry one, because there paying does.

Three details worth knowing before you pick one of these actions:

An invoice that is paid, cancelled or deleted while its subscription is suspended is never terminated — termination is a collection outcome, and there is nothing left to collect.

Two things to know before you suspend, and before you lift one.

On a plan subscription the next-charge date stays where suspension found it. If it is already in the past when you lift the suspension, the next sweep bills the periods that elapsed meanwhile — the same convention pause and resume follow, and it is written to the log. On a long suspension that can be several charges at once, so look at the charge date before you lift it.

If the subscription rides on one of your Perfex recurring invoices rather than on a plan of ours (mode 1), Perfex's recurring-invoice generator is core's, not the module's, and suspending here does not stop it. Stop or pause that recurring invoice in Perfex as well.

The customer is not told by default. autobill_suspend_notify_customer ships off. While it is off, the status change is made, the dunning log records it and your staff are notified, but no e-mail reaches the customer. Turn it on in Settings and the two seeded templates are used. The waiting period before termination is the same screen's Terminate a suspended subscription after this many days, which ships at 30.

8. Late fees

Late fees are off. Nothing accrues, nothing is calculated and no fee invoice is raised until you switch them on, and the switch is on the Late Fees screen's own settings page, behind the Late fee settings button. Switching them on does not backdate anything: the first sweep after you enable them accrues one period, not the months an invoice has already been overdue.

8.1 The fee is a second invoice, never an edit

The rule the whole feature is built around: the overdue invoice is never written to. Not its lines, not its total, not its status. It is a numbered document the customer already holds a PDF of, and version 1.0 refused to touch one on exactly those grounds.

So the fee accrues in the module's own ledger — one row per overdue invoice — and when it is time to bill it, it becomes a separate Perfex invoice: its own number from your own numbering sequence, its own date, its own due date from your invoice due after setting, its own PDF. One line, at the accrued amount, in the same currency as the original, with no tax and no discount applied. The overdue invoice's number appears in that line's description and in both the client note and the admin note, so whoever reads either document can connect them.

The link between the two exists only on the module's side. That has two consequences worth stating plainly:

Nothing in the module charges a fee invoice by itself. It is not a recurring invoice and it is not attached to a subscription, so no sweep opens a charge attempt on it and no stored card is debited for it.

Tick the payment modes before you send it. A fee invoice is created with an empty allowed-payment-mode list, and Perfex reads an empty list as "offer nothing" — the customer would see no Pay now button and the PDF would carry no payment block. Open the fee invoice, choose the modes you want offered, and save. After that it behaves like any other invoice: if one of those modes is a gateway from this module, the customer pays it from the portal in the ordinary way.

8.2 What accrues, and what does not

8.3 The settings

Late Fees → Late fee settings, behind the Manage Settings capability. These eleven options are not on the module's general Settings screen: one feature, one write path.

SettingDefaultWhat it does
Charge late fees on overdue invoicesOff The master switch. While it is off nothing accrues and nothing is invoiced automatically. The one action that still works with it off is Invoice now on a fee that accrued while it was on — see 8.4.
Fee typePercentage of the outstanding balance Percentage, or a fixed amount.
Percentage per accrual0.00 Used when the fee type is percentage. Accepted range 0–100. It ships at zero, so enabling the feature without setting a rate computes a fee of nothing.
Fixed amount per accrual (minor units)0 Used when the fee type is fixed. Minor units: 500 is 5.00 in a two-decimal currency, and 500 in a currency with no decimals.
Cap total fees at this percentage of the invoice0 (no cap) Measured against the original invoice's total, across everything accrued so far, so no sequence of cron runs can exceed it. Accepted range 0–1000.
Grace period (days past the due date)7 Nothing accrues until the invoice is overdue by more than this. Accepted range 0–365.
How often it accruesOnce only Once only, daily, weekly or monthly.
Maximum number of accruals12 After this many accruals the fee closes and can be invoiced. 0 removes the limit — only do that with a cap set, or the fee never closes and never becomes an invoice.
Raise the fee invoice automaticallyOff A second switch, also off. While it is off, fees accrue and wait for a staff member to press Invoice now.
Minimum accrued fee before invoicing (minor units)0 — no minimum Applies to automatic invoicing only. 0 invoices any amount above nothing; a fee that accrued to zero is never invoiced whatever this is set to. Useful for not raising a document for a rounding error.
Invoice line descriptionLate payment fee for invoice {invoice_number} What the customer reads on the fee invoice. {invoice_number} is replaced with the overdue invoice's number. It cannot be saved empty.

8.4 The screen and its actions

Late Fees lists every accrual with its overdue invoice and due date, the customer, the outstanding balance the fee was calculated on, the amount accrued, how many times it has accrued, the date of the last accrual, and the fee invoice once one exists. Filter it by status or search by invoice number or customer.

StatusMeaning
AccruingStill adding. Not yet billable.
Ready to invoiceAccrual has ended; the fee is waiting for its document.
InvoicedIts own invoice exists.
WaivedWritten off by a staff member. Accrual stops.
CancelledDropped — the debt behind it was voided, or a staff member cancelled the fee. It leaves the aging report.
ActionWhat it doesCapability
Invoice now Raises the separate fee invoice for whatever has accrued so far. It works on a fee that is still accruing as well as on a closed one, and it keeps working after you have switched the feature off again — the accrual already happened and a staff member is asking for its document. Two staff pressing it at the same second still produce one invoice. Create
Waive Stops the accrual and writes off the fee, recording who did it and when. An already issued fee invoice is not deleted by this — cancel or credit that invoice yourself. Edit
Cancel Stops the accrual and drops the row from the aging report. Edit
Run accrual now Runs the sweep immediately rather than waiting for the next cron pass, and reports how many rows accrued, closed and were invoiced. Because a fee accrues at most once per period, a manual run cannot double-charge. It refuses with a notice while the feature is off. Create

The sweep itself is the late fees cron job, once per cron pass, in three steps: accrue today's period, close the accruals that are over, then raise invoices for the ones that are closed — in that order, so a fee whose accrual ends today gets its invoice today rather than tomorrow. Both the accrual pass and the invoicing pass are bounded per run, so a large ledger drains across runs instead of stalling the cron.

9. Renewal and expiry tracking

A ledger of dated things Perfex does not model: domains, SSL certificates, hosting accounts and licences. It remembers the date, warns you and your customer before it, and can raise the renewal invoice.

Not contracts, estimates or proposals. Perfex core already sends its own expiry reminders for all three, on its own schedule and with its own settings. Tracking them here as well would mean your customer is told twice about one date, so this screen deliberately does not offer them.

Renewals are off. While Send renewal reminders and process renewals is off in Settings, the renewal sweep does not run at all — no reminder is e-mailed and no invoice is raised, whatever an individual row is set to — and the Renewals entry does not appear in the sidebar. Rows already tracked stay tracked and can still be rolled forward by hand.

9.1 What a tracked renewal holds

FieldNotes
NameWhat the customer would call it — the domain, the certificate, the licence.
IdentifierOptional reference, up to 191 characters.
TypeDomain, SSL certificate, Hosting, Licence or Other.
CustomerRequired. The reminder goes to that customer's first active contact who receives invoice e-mails, and to their first active contact with an address if none is flagged.
Start date, Expiry dateThe expiry date is what everything is measured from.
Term (months)1–600. How far forward a renewal rolls the expiry date.
Renewal cost and currencyWhat a renewal invoice would be for. Zero means no invoice can be raised for this row.
Remind this many days before expiryA comma-separated list. Defaults to the list in Settings (60,30,14,7,1); each row can override it.
Linked subscriptionOptional, and only a subscription belonging to the same customer.
Auto-renew, Auto-invoiceBoth off. See 9.3.
Internal notesStaff-only.

Statuses are Active, Expiring, Expired, Renewed and Not tracked.

9.2 What the sweep does

Once per cron pass, when the feature is on, the renewals job:

  1. reads rows that are Active or Expiring and expire within the next 400 days — a ceiling that keeps one pass bounded, so an install tracking thousands of rows still reaches the ones that are actually due;
  2. works out the most urgent reminder threshold each row has crossed and not yet fired;
  3. claims that threshold before anything is sent, so one reminder per threshold is guaranteed by the record rather than by the e-mail — a failed send is logged and not retried, because a second copy of the same reminder is worse than a missing one;
  4. e-mails the customer with the Recurring Payments: Renewal Reminder template, notifies your staff, and moves the row from Active to Expiring — unless auto-renewal takes the row over instead, which is 9.3;
  5. on the expiry date itself, moves the row to Expired and notifies your staff once. An expired row leaves the sweep, which is what stops the notice repeating.

Start the reminder sequence again on a row clears which thresholds have been sent, so every threshold still ahead of the expiry date can fire once more.

9.3 Auto-renewal, and its three locks

A renewal invoice is only ever raised when all three of these are true: the install-wide switch is on, and the row's Auto-renew box is ticked, and its Auto-invoice box is ticked. Both boxes ship unticked, and with either of them unticked the row only sends reminders.

When all three line up, then at the first threshold the row crosses, the module:

A Renewed row is out of the sweep. Somebody has to put it back for the next term, and that is deliberate: it makes an unattended loop of invoices impossible rather than unlikely.

Three things this does not do:

Mark renewed, the staff button, rolls the term forward and restarts the reminder sequence. It raises no invoice — if you want one, raise it in Sales.

10. The client portal

The module adds one item, Billing & Cards, to your customer-area menu.

Client portal billing page: subscriptions, stored payment methods and the past-due banner

10.1 What a logged-in customer can do

When any of their subscriptions is past due, a banner at the top asks them to update their payment method, linking straight to the capture flow.

10.2 The two guest links

Both links are reached from an e-mail, without a portal login, and are authorized by an unguessable 64-character secret in the URL — the same approach Perfex itself uses for invoice and estimate links.

LinkWhat it opensHow long it lasts
Subscription self-service
/autobill/clients_autobill/subscription/<hash>
A standalone page for one subscription: plan, amount, status, next payment. The customer can pause it for 1–3 months, resume it, or cancel it. Cancelling always passes through a retention step first — the pause offer is shown before the confirm button exists — and a customer-initiated cancellation is always at period end: they keep what they have paid for and nothing further is charged. Does not expire. The link is the subscription's own identifier and stays valid for as long as the subscription row exists. A closed subscription still opens the page, read-only.
3-D Secure re-authentication
/autobill/clients_autobill/authenticate/<token>
The bank's confirmation step for one charge attempt. Stripe runs the challenge inside its own secure window on the page; Paystack owns the whole flow and the customer is redirected to it. Single use, and it expires after the Authentication link lifetime in Settings (72 hours by default). The token is stored hashed, so a database copy does not hand anyone a working link.
Guest subscription page with the pause offer shown before the cancel confirmation

An unknown link, an expired link and an already-used link all produce the same neutral "this link is no longer valid" page with a 410 status. Telling them apart would tell an attacker which tokens exist.

Guest 3-D Secure page: confirm payment, with the invoice amount and number

The confirmation page needs JavaScript, because the bank runs its check inside its own window. With JavaScript switched off, the page explains that and links the customer to the invoice's own server-rendered payment page, which works without it.

Confirming 3-D Secure does not by itself record money. The gateway's webhook is the only authority on whether the payment succeeded; the confirmation page only burns the link and moves the attempt out of "awaiting authentication" so dunning stops chasing it. This is why the webhook endpoint in step 4.3 is not optional.

11. Reports and the dashboard

11.1 The dashboard

The module's first screen answers three questions: how is recurring revenue doing, what needs a human, and is the machinery running.

11.2 What the numbers mean

TermPlain languageWhere it comes from
MRR
(monthly recurring revenue)
What your live subscriptions are worth in a month. A yearly plan of 1,200 counts as 100 a month; a weekly plan of 10 counts as about 43. Every live subscription's amount, normalised to a monthly figure (month → amount ÷ interval; year → ÷ 12 × interval; week → × 52/12; day → × 30), summed per currency. Live means active, trialing or past due. Currencies are never converted into each other — you get one MRR per currency, which is honest rather than convenient.
ARRThe same figure annualised.MRR × 12.
Churn Subscriptions that ended in the period, and the monthly revenue that left with them. Counted from cancellations recorded inside the snapshot window.
Involuntary churn Customers you lost to a payment failure rather than to a decision — the ones dunning is supposed to save. Churned subscriptions whose cancellation reason was written by the dunning engine. It is an approximation with a deliberate bias towards under-counting: Perfex stores no state history, so subscriptions still parked in past due are not counted as churned yet.
Recovered this month Money you would have chased by hand: payments that only succeeded because an earlier attempt had failed. Charge attempts with an attempt number greater than 1 that settled in the current calendar month. Attempt 1 is a normal collection and is deliberately excluded.
New / expansion / contraction How MRR moved: arrivals, price rises on customers you kept, price drops on customers you kept. Measured between consecutive daily snapshots of the same currency, so the identity MRR = previous + new + expansion − contraction − churned always holds exactly.

Where the figures live. Once a day the cron writes one snapshot row per currency. The dashboard shows the newest snapshot and tells you the date it is "as of"; before the first snapshot exists it computes MRR live instead. The Reports screen reads snapshots only — so the movement chart and the cohort table need a few days of cron runs before they say anything, and the screen says so rather than drawing an empty chart.

11.3 The Reports screen

Reports screen: MRR movement, success rate by gateway, top decline reasons and cohort retention

11.4 The AR aging report

New in 1.1, and the one addition with no switch: it bills nothing, sends nothing and writes nothing. It reads the invoices you already have. AR Aging has its own entry in the module menu and needs only the View capability.

Every unpaid, partially paid and overdue invoice, rolled up by customer and by currency, and split by how far past its due date it is, in the five bands the screen itself labels Not yet due, 1-30 days, 31-60 days, 61-90 days and Over 90 days — day 90 falls in the 61-90 band, so the last band starts at day 91.

Filter by currency, customer, age band and a customer search; 25 rows to a page. Every filter and every page link is a plain link, so a filtered view is a URL you can paste into an e-mail.

Export CSV writes the whole filtered set, not the page you are looking at. Amounts are plain decimals with a dot separator so a spreadsheet can sum the column, and any text cell that starts with =, +, - or @ is prefixed with an apostrophe — a customer name is not something you want executing as a formula when a colleague opens the file.

One optional column: switch on Show accrued late fees on the aging report in Settings (off by default) and a further column shows fees that have accrued but do not yet have an invoice of their own. They are shown on their own and are never added into the outstanding total, because they are not yet a receivable.

12. Sale agent on generated invoices

Perfex puts a Sale Agent field on every invoice and offers it as a filter on four of its own reports, under Sales → Reports. An invoice with an empty agent is excluded from all of them.

From version 1.2 this module fills that field in, taking it from the record the invoice comes from:

InvoiceSale agent it receives
Subscription charge, plan mode The staff member who created the subscription
Subscription charge, adopted from an existing invoice The staff member who created the subscription; failing that, the agent already on the invoice it was adopted from
Late fee The agent on the invoice the fee belongs to
Renewal The agent of its linked subscription, if it has one
Anything with none of the above The fallback below, or empty

A subscription a customer started themselves in the client portal has no staff member behind it. For those, set Default sale agent for generated invoices under Setup → Settings → Recurring Payments. It ships as None, which writes no agent — the behaviour of every version before 1.2.

A per-record agent always wins over the fallback, so setting one does not overwrite attribution that a real staff member earned.

Upgrading from 1.0 or 1.1? Invoices already in your database are not touched. Only invoices created after the upgrade carry an agent. If you need the history corrected, that is a manual edit in Perfex — this module will not rewrite issued invoices.

“Added from” stays empty, and cannot be fixed. Perfex sets that field itself and forces it to empty for anything created by the cron job, which is every invoice this module generates. No setting changes it. It is core behaviour, not a limitation of this module — but it is core behaviour you will notice, so it is written down here rather than left for you to discover.

13. Permissions

The module registers seven capabilities under its own feature name in Setup → Staff → Roles (and on each staff member's own permissions tab). Administrators always have all of them.

Setup, Staff, Roles: the module's seven capabilities
CapabilityWhat it gates
View (Global) Every read-only screen: dashboard, subscriptions, plans, charge attempts, dunning policies and log, reports. Without it the module's menu does not appear at all. It is also the capability that decides who receives the module's staff notifications.
CreateCreating subscriptions, plans and dunning policies.
Edit Editing subscriptions (including pause, resume and cancel), plans and policies; setting a customer's default payment method; revoking a stored method.
DeleteDeleting plans, dunning policies and stored payment-method rows.
Charge Manually The two buttons that move money on demand: Charge now on a subscription and Retry now on a charge attempt.
View Payment Methods The Payment Methods screen. It is required for every action on that screen, so a staff member with Edit but not this one cannot touch stored authorizations at all.
Manage Settings The Settings screen — general options, licence, diagnostics and the e-mail template list — and its entry in the menu.

Version 1.1 adds no new capability. The screens it adds reuse these seven: Late Fees, Renewals and AR Aging are read with View; raising a fee invoice, running the accrual sweep and creating a renewal need Create; waiving or cancelling a fee, editing a renewal and lifting a suspension need Edit; deleting a renewal needs Delete; and both settings screens need Manage Settings. A role that could not see the module yesterday cannot see anything new today.

Every action that changes state is submitted as a POST and carries Perfex's CSRF token, so a crafted link cannot make a staff member cancel a subscription or delete a policy by accident.

14. Uninstalling

Decide the data question before you remove the module: the setting is read at uninstall time, and once the module is gone you cannot change it.

Settings, General, Data section: create-invoices-as staff member and the delete-data toggle
Keep data (default)Delete data
Setting Delete ALL data when the module is uninstalled = No = Yes, saved before uninstalling
The 13 module tablesKept. Subscriptions, stored authorizations, charge history, late fees, tracked renewals and snapshots survive; re-installing resumes where you left off.Dropped.
Module settingsRemoved in both cases.
Gateway settings and keysRemoved in both cases.
The 11 e-mail templatesRemoved in both cases, including any wording you edited.
Staff permissions for the featureRemoved in both cases.
Perfex invoices and paymentsNever touched. They are core Perfex data, and the payments the module recorded stay exactly where they are. That includes the separate invoices raised for late fees and renewals: they are ordinary Perfex invoices and they survive uninstalling either way.

Uninstalling does not cancel anything at the gateway. Stored cards and direct debit mandates keep existing in your Stripe, GoCardless or Paystack account. If you want them gone, revoke them from the Payment Methods screen (which detaches each token at the gateway) or from the gateway dashboard, before you remove the module.

If you only want to stop the module temporarily, Deactivate instead of removing — see the end of section 3.

15. Troubleshooting

Sixteen questions, and the screen that answers each. If yours is not here, try the FAQ.

Nothing is being charged at all

Nine times out of ten this is the cron. Open Settings → Diagnostics and look at the engine self-check: never ran or a large age in hours means the Perfex cron is not reaching the module. Fix it in Setup → Settings → Cron Job (section 4.1), then use Run Cron Manually and refresh. If the cron does run but a specific job shows errors, its last error message is on the dashboard's Cron Health panel.

The gateway is missing from the invoice, or a charge was refused before it started

The invoice's currency is not in that gateway's currency list. Perfex removes a gateway from any invoice whose currency it does not list, and the module refuses to charge rather than sending a guaranteed failure. Settings → Diagnostics prints "Not enabled for: …" for exactly this; fix the list in Setup → Settings → Payment Gateways. You get one staff notification per gateway and currency per day, not one per invoice.

Webhooks are not arriving

Check, in this order:

An attempt is stuck in Pending, Processing or In Transit

In Transit is normal for bank debit and can last several days — that is the scheme, not a fault. Pending or Processing for more than an hour usually means the gateway answered and the webhook never arrived. The module re-drives such attempts safely under the same idempotency key, and after 26 hours the daily reconcile job raises a staff notification listing how many are unresolved. Resolve those from the gateway dashboard: find the transaction, see whether it took money, and act on the invoice accordingly. Reconciliation does not query the gateway APIs for you.

Dunning e-mails are not being sent

Settings, Email Templates tab: the seeded templates with their slugs and status

Late fees are switched on but nothing has accrued

Work down this list, on Late Fees → Late fee settings:

Then press Run accrual now rather than waiting for the cron: it reports how many rows accrued, closed and were invoiced, which tells you which of the four it was.

A late fee accrued, but no invoice was raised for it

Two likely reasons, both by design. Raise the fee invoice automatically ships off, so fees wait for a staff member to press Invoice now. And automatic invoicing only picks up accruals that have ended — a fee set to accrue daily, weekly or monthly with no cap and no maximum accrual count never ends, so it can never be billed. Give a repeating fee a cap or a maximum number of accruals. Also check the minimum accrued amount before invoicing, which applies to automatic invoicing only.

A subscription is Suspended or Terminated and I need it back

Suspended is reversible two ways: record a payment against the invoice and it returns to Active by itself, or press Lift suspension on the subscription, which needs the Edit capability. If it comes back as Needs Payment Method instead of Active, it has no usable stored method — the customer adds one from the portal and it starts collecting again.

Terminated is final on purpose. Nothing in the module reinstates it, including a payment; if the invoice is paid, your staff get a notification saying exactly that. A returning customer needs a new subscription.

A renewal reminder was not sent

Check, in this order: is Send renewal reminders and process renewals on in Settings — while it is off the sweep does not run at all. Is the row's status Active or Expiring — Expired, Renewed and Not tracked rows are out of the sweep. Has that threshold already fired? One reminder is sent per threshold, ever; Start the reminder sequence again clears the history so every threshold still ahead can fire once more. And a renewal expiring more than 400 days out is not read at all until it comes inside that horizon.

A customer says the 3-D Secure link no longer works

Those links are single-use and expire after the Authentication link lifetime in Settings (72 hours by default). Two ways out: the customer can simply pay the invoice from the portal or from their invoice link, which achieves the same thing; or a staff member with Charge Manually can press Retry now on the attempt, which produces a fresh charge and, if the bank asks again, a fresh link and a fresh e-mail.

Paystack charges fail after the customer changed their e-mail address

A Paystack authorization is bound to the e-mail address it was created with. The module therefore charges with the address stored on the payment method at capture time and never with the contact's current address — changing an e-mail in Perfex does not break anything. What does break it is the authorization itself being replaced or removed at Paystack, or a method stored without its original address: the attempt fails with a clear message on the Charge Attempts screen. The fix is always the same — ask the customer to add the payment method again from the portal, which creates a fresh authorization.

A subscription sits at "Needs Payment Method"

It has no usable stored method: the customer never added one, or theirs expired, was revoked at the gateway, or was removed. Only the customer can fix it, in the portal. You do not have to do anything afterwards: when they add a method, every stranded subscription of theirs adopts it automatically and starts collecting again. The Needs attention panel on the dashboard and the engine self-check both count these.

Perfex stopped generating the recurring invoice

That is core behaviour, not the module. Check Setup → Settings → Cron Job → Invoice for "Create new invoice from recurring invoice only if the invoice is with status paid?" — if it is set to Yes and the previous invoice is still unpaid while dunning works on it, no new invoice is created. Also check the recurring invoice's cycle limit and that its status is not cancelled or draft.

Could a customer be charged twice?

The module is built so the answer is no, on four independent levels: only one cron run can be charging at a time; every attempt carries a deterministic idempotency key (inv_{invoice}_att_{n}) so a repeat of the same attempt makes the gateway replay its original answer instead of taking money again; a gateway transaction id can be claimed by exactly one attempt, and that claim happens before the payment is written into Perfex; and every webhook delivery is de-duplicated on the gateway's own event id. If you still suspect a duplicate, open Charge Attempts and filter by that invoice — every attempt against it is listed with its transaction id.

The invoice was paid by bank transfer, cash or another gateway

Nothing to do. Any payment recorded against the invoice in Perfex — by the module, by the customer through core's Pay button, or typed in by your staff — closes the open dunning on that invoice, stands down the queued retries, and puts a past-due subscription back to Active. A partial payment does not: the invoice still owes money, so the sequence continues from where it was rather than restarting.

Customers still reach portal links while the module is deactivated

Expected. A deactivated Perfex module keeps its routes, so the module answers those requests itself with 503 Service Unavailable instead of showing a broken page — and gateways treat that as "retry later" and re-deliver the webhooks once you activate again. Nothing is charged and nothing is lost while it is off.