Service Status Hooks
Nine hooks from a sold service opening to its record going: setting up, status changes, suspending, terminating and deleting.
Overview
A service lives in two places at once: as a row in our database and as a real account at the end of a provider module. Most hooks here speak about the first.
Knowing that split matters: when a status changes, our record changed. Whether the account on the server truly closed is a separate question, and usually the outcome of a job that was queued.
Reference
Changing the service about to be created
Runs before the service row is written to the database.
user_id, product_id, options, status, duedate. What you change here goes straight to the database.Hook::add('filter:service.save_data', 10, function (&$data) {
// Put your own tracking key inside options; do not add a column.
$data['options']['acme_batch'] = Acme::currentBatch();
});Learning that a service opened
Runs after the service row was written. The account at the provider may not exist yet at this point.
Hook::add('action:service.created', 10, function ($id, $data) {
// The row exists, the account may not: do not wait on provisioning here.
Crm::opened((int) $id, (int) ($data['user_id'] ?? 0));
});Stopping a status change
Runs before the service status changes, handing you both the old state and the target.
active, suspended, cancelled, inprocess.Hook::add('gate:service.status_change', 10,
function ($service, $status, $oldStatus) {
// Check only the reopening of a cancelled service.
if ($oldStatus === 'cancelled' && $status === 'active'
&& !Acme::reactivationAllowed($service))
return 'A cancelled service reopens with an operator\'s approval.';
return null;
});Following a status change
Runs after the status changed. The value you get is the final one, after any override by the module.
Hook::add('action:service.status_changed', 10,
function ($serviceId, $status, $oldStatus, $service) {
// Do no work again where it landed back on the same status.
if ($status === $oldStatus) return;
Crm::statusMoved($serviceId, $oldStatus, $status);
});Stopping a suspend
Runs before a service or add-on is suspended. Stopping it has a cost: the job turns into a cancel signal.
service or addon. One hook carries both.status, duedate, module, owner_id.Hook::add('gate:service.suspend', 10,
function ($target_type, $target_id, $service, $user_id) {
if ($target_type !== 'service') return null; // leave add-ons alone
// MIND: a veto stops the suspend but turns the job into a CANCEL signal.
if (Acme::vipAccount($user_id)) return 'VIP account: review by hand.';
return null;
});Following a suspend
Runs after the service was suspended. The actual closing on the server was queued.
service or addon.0 means it was never queued: on a service without a module nothing happens on the server side.Hook::add('action:service.suspended', 10,
function ($target_type, $target_id, $user_id, $reason, $module_queue_id) {
// A queue id of 0 means nothing was done on the server.
if ($module_queue_id === 0) Ops::note('suspend-no-module', $target_id);
});Following a termination
Runs after the service was terminated. This hook carries the server side: which module, which server.
hosting or server.Hook::add('action:service.terminated', 10,
function ($target_id, $service_type, $module, $server_id, $module_queue_id) {
// Capacity came free on that server: update your own counter.
Capacity::released((int) $server_id, $service_type);
});Stopping a service delete
Runs before the service record is deleted. Deleting is not the same as closing the account on the server: it removes our record alone.
Hook::add('gate:service.delete', 10, function ($service) {
// Deleting a live service leaves an orphaned account on the server.
if (($service['status'] ?? '') === 'active')
return 'A live service cannot be deleted; terminate it first.';
return null;
});Following a delete
Runs after the service record was deleted.
Hook::add('action:service.deleted', 10, function ($id, $service) {
Acme::forgetService((int) $id, $service['name'] ?? '');
});Pitfalls
Returning a non-empty text at the suspend gate does not stop the work, it turns it into a cancel. A rule written as "do not suspend this one" can end with the service cancelled outright. Write here knowing what follows.
The suspend and terminate hooks hand you a queue id. Zero means the module job was never created: the service has no module, or the server is unknown. Our record changed and nothing happened on the server.
The status event hands you the value after any override by the module. The target you saw at the gate and the value in the event can differ. Reacting to a transition, read the value from the event rather than the intent at the gate.
Deleting a service removes our record and nothing else. The account on the server stays where it is, now visible from nowhere. Where the server side should close too, terminate first and delete after.
Related Articles
- Service Lifecycle Hooks
- Order Flow Hooks
- Scheduled Task Hooks
Merci pour votre retour !
Notre équipe d'assistance est disponible 24h/24 pour tout ce que vous ne trouvez pas ci-dessus.