Everyone

Service Lifecycle (Create → Suspend → Terminate)

Full lifecycle of a hosted service from order payment through termination, including the jobs, schedulers, and status transitions involved.

Last updated 1776211200

Overview

Every hosting service moves through a predictable lifecycle managed by Commerce jobs and scheduled commands. No manual steps are required under normal operation — provisioning, suspension, and termination all happen automatically.

Lifecycle Diagram

Order Paid
    │
    ▼
CreateHostingAccountJob ──▶ status: active
                                │
                    ┌───────────┴───────────┐
                    │                       │
            Invoice Overdue          Invoice Paid (on time)
                    │                       │
                    ▼                   (no action)
        SuspendHostingAccountJob
                    │
                status: suspended
                    │
          ┌─────────┴──────────┐
          │                    │
    Payment Received    Still No Payment
          │            (past termination
          │             grace period)
          ▼                    ▼
UnsuspendHostingAccountJob  TerminateHostingAccountJob
          │                    │
    status: active       status: terminated

Stage 1 — Account Creation

Trigger: InvoiceService::markPaid() is called when a hosting invoice is paid.

What happens:

  1. CreateHostingAccountJob is dispatched to the queue.
  2. The job calls the server's provisioning module createAccount().
  3. On success: the service record is updated with username, panel_account_id, and status = active.
  4. The action is written to provisioning_log.

If the job fails after 3 retries, the service stays in pending and an admin alert is triggered.

Stage 2 — Suspension (Non-Payment)

Trigger: commerce:check-overdue-services (runs daily at 01:00).

The command finds all active services whose linked invoice is overdue beyond the configured grace period.

What happens:

  1. SuspendHostingAccountJob is dispatched per affected service.
  2. The job calls suspendAccount() on the module.
  3. On success: status = suspended.
  4. Client receives a suspension notice email.

Stage 3 — Unsuspension (Payment Received)

Trigger: Overdue invoice is paid — InvoiceService::markPaid().

What happens:

  1. UnsuspendHostingAccountJob is dispatched.
  2. The job calls unsuspendAccount() on the module.
  3. On success: status = active.
  4. Client receives an unsuspension confirmation email.

Stage 4 — Termination (Extended Non-Payment)

Trigger: commerce:check-termination-services (runs daily at 02:00).

The command finds all suspended services past the termination grace period.

What happens:

  1. TerminateHostingAccountJob is dispatched.
  2. The job calls terminateAccount() on the module — the account is deleted from the panel.
  3. On success: status = terminated.
  4. Client receives a termination notice email.

Termination is irreversible. All data on the panel is deleted. Ensure grace periods are configured appropriately in Commerce settings.

Job Reliability

All provisioning jobs share the same retry configuration:

Setting Value
Max attempts 3
Retry backoff 60 seconds
Failure behavior Logged to provisioning_log, admin alerted

Scheduled Commands

Command Schedule Purpose
commerce:check-overdue-services Daily 01:00 Dispatch suspend jobs for overdue services
commerce:check-termination-services Daily 02:00 Dispatch terminate jobs for long-overdue services

Both commands require the Laravel scheduler to be running. Verify with:

php artisan schedule:list

Manual Overrides

An admin can manually trigger any lifecycle action from Admin → Services → [service] → Actions. This dispatches a new job immediately, bypassing the scheduler. Useful for:

  • Force-creating an account stuck in pending.
  • Manually suspending a service outside the normal invoice cycle.
  • Recovering after a failed automatic job.

Related