Contents
Hooks reference - Plugixa Car Wash
The plugin fires 29 actions and 46 filters. All of them start with
plugixa_carwash_. Three actions are fired only by Pro modules and are marked
PRO.
Pro is built on these same hooks. Payments by Stripe, memberships, gift cards, the travel fee, reminders and webhooks are ordinary listeners on the hooks below, so what they do, your code can do too. Put your code in a small plugin of your own or in a must-use plugin, not in this plugin’s files, so updates do not remove it.
These hooks are the plugin’s own seams. They are not a separately versioned API, and an argument or a payload can change between versions. Pin the version you build against and test after updating.
The ones you are most likely to want
| Hook | Type | What it is for |
|---|---|---|
plugixa_carwash_booking_created |
action | React to a new booking |
plugixa_carwash_booking_status_changed |
action | React to a booking being confirmed, started, completed, cancelled or marked a no-show |
plugixa_carwash_booking_moved |
action | React to a booking moving to another time, bay or team |
plugixa_carwash_payment_recorded |
action | React to a payment or a refund |
plugixa_carwash_quote |
filter | Change the price or the duration of a booking |
plugixa_carwash_validate_booking |
filter | Refuse a booking for a reason of your own |
plugixa_carwash_transition_error |
filter | Refuse a status change for a reason of your own |
plugixa_carwash_public_request_captcha |
filter | Put a CAPTCHA in front of the public routes |
plugixa_carwash_client_ip |
filter | Tell the rate limiter the real visitor address behind a proxy |
plugixa_carwash_user_can |
filter | Narrow or widen what a user may do |
Booking statuses
Several hooks pass a status. These are the values:
| Value | Shown as |
|---|---|
hold |
Awaiting payment: an online payment is in progress |
pending |
Pending: waiting for staff to confirm |
confirmed |
Confirmed |
in_progress |
In progress |
completed |
Completed |
cancelled |
Cancelled |
no_show |
No-show |
Entity slugs
The record hooks pass an entity slug.
| Slug | Record |
|---|---|
booking |
A booking |
customer |
A customer |
location |
A location |
resource |
A bay or a mobile team |
vehicle_type |
A vehicle type |
package |
A package |
service |
A service or add-on |
coupon |
A coupon |
booking_form |
A booking form |
membership PRO |
A customer’s membership |
membership_plan PRO |
A membership plan |
gift_card PRO |
A gift card |
fleet_account PRO |
A company account |
role PRO |
A staff role |
Actions
Bookings and payments
| Action | Arguments | When it fires |
|---|---|---|
plugixa_carwash_booking_created |
array $booking, bool $public, array $quote |
After a booking is saved, its lines are written and its first history entry is recorded. $booking is the booking as the staff screens see it. $public is true for the booking form and false for staff. $quote is what it was priced at. Emails, texts, gift cards and webhooks listen here. |
plugixa_carwash_booking_status_changed |
array $booking, string $from |
After a status change is saved. $booking is after the change; $booking['status'] is the new status. Also fires when the plugin changes a status by itself: an unpaid hold that runs out, or a paid hold that is confirmed. |
plugixa_carwash_booking_moved |
array $booking, array $row |
After a booking moves to another time, bay or team. $booking is after the move, $row is the stored row before it. Times in $row are UTC. |
plugixa_carwash_payment_recorded |
array $payment |
After a payment or refund is recorded and the booking’s payment status is updated. $payment holds id, booking_id, provider, amount (negative for a refund), currency and status. |
plugixa_carwash_payment_return |
array $row, array $params |
When a customer comes back from a payment page and their booking is read. $row is the stored booking, $params the query string, each value cleaned as one line of text. A gateway checks the payment with its own API here. |
plugixa_carwash_job_stage_changed PRO |
array $job, string $stage |
A mobile job moved on. $stage is en_route, arrived, working or done. See Dispatch. |
plugixa_carwash_fleet_statement_issued PRO |
array $statement, array $fleet |
A statement was issued to a company account. See Company accounts. |
plugixa_carwash_stripe_event PRO |
array $event |
A Stripe webhook event arrived and its signature checked out. $event is the decoded Stripe event. See Stripe. |
Tell a manager about every online booking:
add_action( 'plugixa_carwash_booking_created', function ( array $booking, bool $public ) {
if ( ! $public ) {
return; // Staff made it at the desk.
}
wp_mail(
'manager@example.com',
'Online booking ' . $booking['number'],
sprintf(
"%s %s booked %s at %s on %s.",
$booking['first_name'],
$booking['last_name'],
$booking['items'][0]['name'] ?? 'a wash',
$booking['local_time'] ?? '',
$booking['local_date'] ?? ''
)
);
}, 10, 2 );
$booking['start_at'] is UTC. local_date and local_time are the start at
the booking’s location. items lists the lines, each with role (package,
service, addon, fee or discount), item_id, name, price and
duration. payments lists the payments.
React to a no-show:
add_action( 'plugixa_carwash_booking_status_changed', function ( array $booking, string $from ) {
if ( 'no_show' === $booking['status'] ) {
// Flag the customer in your CRM, for example.
}
}, 10, 2 );
Records
Every record table goes through one repository, so these fire for every entity in the slug table above. The booking actions above are more useful for bookings: they carry the whole booking, while these carry the raw row.
| Action | Arguments | When it fires |
|---|---|---|
plugixa_carwash_entity_created |
string $entity, int $id, array $data |
After a record is inserted. A customer created by a first booking fires it too. |
plugixa_carwash_entity_updated |
string $entity, int $id, array $data, array $old |
After a record is updated. $data holds the changed columns. |
plugixa_carwash_entity_field_changed |
string $entity, int $id, string $field, mixed $new, mixed $old |
Once for each column whose value changed in an update. |
plugixa_carwash_entity_assigned |
string $entity, int $id, int $new_user, int $old_user |
When a record’s assigned_user_id changes. No car wash table has that column, so it does not fire today. |
plugixa_carwash_before_delete |
string $entity, int $id, array $old |
Before a record is moved to the trash. |
plugixa_carwash_entity_deleted |
string $entity, int $id, array $old |
After a record is moved to the trash. The row still exists. |
plugixa_carwash_entity_restored |
string $entity, int $id, array $row |
After a record is restored from the trash. |
plugixa_carwash_before_purge |
string $entity, int $id, array $row |
Before a record is deleted for good. The last moment it exists. |
plugixa_carwash_entity_purged |
string $entity, int $id, array $row |
After a record is deleted for good. |
Lifecycle
| Action | Arguments | When it fires |
|---|---|---|
plugixa_carwash_fs_loaded |
none | While the main plugin file is read, after the Freemius SDK is set up. It fires before plugins_loaded, so attach from a must-use plugin. |
plugixa_carwash_loaded |
Container $container |
Once the plugin has booted on plugins_loaded, after every module has registered its hooks. |
plugixa_carwash_activated |
none | On activation, after the tables and the default settings exist. Not on an update. |
plugixa_carwash_migration_failed |
array $failures |
When creating or updating the tables did not finish. Keyed by module id, each with tables and reason. |
plugixa_carwash_deactivated |
none | On deactivation. Modules clear their scheduled events here. |
plugixa_carwash_uninstall |
none | During an uninstall that erases the data, before the tables are dropped. See Privacy and uninstall. |
plugixa_carwash_settings_saved |
array $settings |
After the settings are saved. |
plugixa_carwash_app_toggled |
string $id, bool $enabled |
After an app is switched on or off in the App Manager. |
Admin, REST and mail
| Action | Arguments | When it fires |
|---|---|---|
plugixa_carwash_admin_submenus |
string $parent_slug, callable $render |
While the admin menu is built, after the Dashboard entry. Add your own submenu pages here. |
plugixa_carwash_enqueue_assets |
string $hook_suffix |
On the plugin’s admin page, after the app is enqueued. Enqueue anything that belongs on that page here. |
plugixa_carwash_register_rest_routes |
string $namespace, Container $container |
On rest_api_init, after the plugin’s own routes. $namespace is plugixa-carwash/v1. |
plugixa_carwash_before_send_mail |
string|string[] $to, string $subject, array $options |
Just before the plugin hands an email to wp_mail(). For logging or diverting mail. |
Filters
Pricing and booking rules
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_quote |
array $quote |
array $input, array $chosen |
The priced booking, on the live summary and when the booking is saved, so the price shown is the price charged. $chosen holds location, vehicle, package, services and form. Add a line and call Quote::totals() to re-total. |
plugixa_carwash_validate_booking |
null |
array $input, array $quote, bool $public |
Return a WP_Error to refuse the booking, with your own message and status. |
plugixa_carwash_initial_status |
string $status |
array $input, array $quote |
The status an online booking starts in: confirmed, or pending when bookings are confirmed by hand. A payment module returns hold for a method paid online. Only hold, pending and confirmed are accepted. |
plugixa_carwash_transition_error |
null |
array $row, string $to |
Return a WP_Error to refuse a status change the normal rules allow. $row is the stored booking before the change. |
plugixa_carwash_pick_resource |
int $chosen |
array $free, array $schedule, array $window |
Which free bay or team a new booking gets. The default is the first free one in your order. Return the id of one of $free. |
plugixa_carwash_public_catalogue |
array $catalogue |
array $form |
What the booking form receives: packages, services, payment methods, whether it takes coupons. |
plugixa_carwash_public_input |
array $input |
WP_REST_Request $request |
A public booking request’s parameters after cleaning. Any customer_id the browser sent has been removed; the customer account puts back the signed-in customer here. |
plugixa_carwash_booking_detail |
array $booking |
array $row |
A booking as every screen reads it. The Payments module adds payments here. |
Add a surcharge at one location:
use PlugixaCarWash\Modules\Bookings\Services\Quote;
add_filter( 'plugixa_carwash_quote', function ( array $quote, array $input, array $chosen ) {
if ( 3 !== (int) $chosen['location']['id'] ) {
return $quote;
}
$quote['lines'][] = array(
'role' => 'fee',
'item_id' => null,
'name' => 'Airport parking surcharge',
'price' => 5.00,
'duration' => 0,
);
return Quote::totals( $quote ); // Recomputes subtotal, tax, total and what is due.
}, 5, 3 );
Priority 5 puts the fee before the plugin’s own discounts, so a percentage coupon
applies to it too. To make the booking longer as well, add the minutes to the
line’s duration and to $quote['duration'].
Refuse to complete a wash that has not been paid:
add_filter( 'plugixa_carwash_transition_error', function ( $error, array $row, string $to ) {
if ( null !== $error ) {
return $error; // Somebody else refused it already.
}
if ( 'completed' === $to && 'paid' !== $row['payment_status'] ) {
return new WP_Error(
'my_unpaid',
'Take payment before marking this wash completed.',
array( 'status' => 409 )
);
}
return null;
}, 10, 3 );
The message is shown to the staff member who tried the change.
Payments
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_payment_methods |
array $methods |
none | Every method the site can offer, keyed by id, each with id, label, description and online. |
plugixa_carwash_offered_payment_methods |
string[] $offered |
array $input |
The method ids this booker may use. For methods offered to some customers only, such as charging a company account. |
plugixa_carwash_booking_payment_method |
string $method |
array $input, array $quote |
The method stored on a new booking. |
plugixa_carwash_booking_payment |
array|null $payment |
array $booking, string $token |
After a public booking is made, where to send the customer to pay. Null for a booking paid at the wash. |
plugixa_carwash_payment_start_{method} |
array|WP_Error|null $start |
array $booking, string $token |
One filter per online method id, for example plugixa_carwash_payment_start_stripe. Return array( 'redirect_url' => ... ) or a WP_Error. |
A method you add appears in Settings -> Payments to be switched on. A method
with online set to true must also answer its payment_start filter.
add_filter( 'plugixa_carwash_payment_methods', function ( array $methods ) {
$methods['voucher'] = array(
'id' => 'voucher',
'label' => 'Paper voucher',
'description' => 'Hand your voucher in at the desk.',
'online' => false,
);
return $methods;
} );
Public routes
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_public_request_captcha |
true |
WP_REST_Request $request, string $action |
Whether a public request passes your anti-bot check. Return false, or a WP_Error with your own message and status, to refuse. Nothing is checked by default. |
plugixa_carwash_client_ip |
string $ip |
none | The visitor address the rate limiter counts. By default REMOTE_ADDR only. |
$action is the rate-limit bucket: carwash_book, carwash_cancel,
carwash_read, carwash_account_link, carwash_account_sign_in or
carwash_account. See REST API.
Behind Cloudflare, every visitor arrives from a Cloudflare address and shares one rate limit. Use the address Cloudflare passes on, and only when the site really is behind Cloudflare, because the header can be forged otherwise:
add_filter( 'plugixa_carwash_client_ip', function ( string $ip ) {
$real = isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ? trim( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) : '';
return filter_var( $real, FILTER_VALIDATE_IP ) ? $real : $ip;
} );
Check a CAPTCHA token on new bookings only:
add_filter( 'plugixa_carwash_public_request_captcha', function ( $verdict, WP_REST_Request $request, string $action ) {
if ( true !== $verdict || 'carwash_book' !== $action ) {
return $verdict;
}
$token = (string) $request->get_header( 'x-captcha-token' );
return my_verify_captcha( $token ) // Your provider's check.
? true
: new WP_Error( 'my_captcha', 'Please confirm you are not a robot.', array( 'status' => 403 ) );
}, 10, 3 );
The booking form does not send a CAPTCHA token by itself; how the token reaches the request is up to your integration.
Customer account
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_account_panels |
array $panels |
int $customer_id |
Extra panels on the customer’s account page. Each panel has id, title, an optional subtitle, rows of label and value pairs, an optional meter (value, max, label), an optional code to copy, and actions (id, label, optional confirm and primary). |
plugixa_carwash_account_action |
null |
string $panel, string $action, int $customer_id, array $params |
A button on one of those panels was pressed. Return an array (with a message) when it is yours, a WP_Error to refuse, or null when the panel is not yours. |
Permissions and data access
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_user_can |
bool $allowed |
string $action, string $entity_type, array|null $record, int $user_id |
Every permission check. $action is read, create, edit, delete or export. The Pro roles module answers here. |
plugixa_carwash_capabilities |
array $map |
none | The yes or no map the admin app uses to hide controls. It hides buttons only; the server checks again. |
plugixa_carwash_query_clauses |
array $clauses |
string $entity_type, string $context, int $user_id, string $table |
Extra join, where and params for list queries, to narrow what a user sees. |
plugixa_carwash_search_fragments |
array $fragments |
string $entity_type, string $term, string $context |
Extra OR conditions for the search box, each array( 'sql' => ..., 'params' => array( ... ) ). |
plugixa_carwash_unreadable_fields |
string[] $denied |
string $entity_type, int $user_id |
Fields a user may not read, so they are not offered as list filters. |
plugixa_carwash_filter_output |
mixed $data |
string $entity_type |
Every successful admin REST answer, before it is sent. The one place to remove fields a user may not see. |
Let a role with view rights also see coupons, but never change them:
add_filter( 'plugixa_carwash_user_can', function ( bool $allowed, string $action, string $entity ) {
if ( 'coupon' === $entity && 'read' !== $action ) {
return current_user_can( 'manage_options' );
}
return $allowed;
}, 10, 3 );
Records and entities
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_before_insert |
array $data |
string $entity |
A record’s columns just before it is inserted. |
plugixa_carwash_before_update |
array $data |
string $entity, int $id, array $old |
A record’s changed columns just before the update. |
plugixa_carwash_can_delete |
bool $allowed |
string $entity, int $id, array $old |
Whether a record may be moved to the trash. A last line of defence: it carries no message. |
plugixa_carwash_can_purge |
bool $allowed |
string $entity, int $id, array $row |
Whether a record may be deleted for good. |
plugixa_carwash_{entity}_delete_blockers |
string[] $blockers |
int $id, array $row |
Reasons a record must not be trashed, each a full sentence shown to the user. For example plugixa_carwash_location_delete_blockers. The plugin uses it to protect anything an upcoming booking depends on. |
plugixa_carwash_entity_types |
array $entities |
none | The entities the generic screens and the role builder know about. |
plugixa_carwash_entity_fields |
array $fields |
string $slug |
One entity’s field definitions. |
plugixa_carwash_record_names |
array $names |
string $entity_type, int[] $ids |
Display names for some records, keyed by id. |
plugixa_carwash_load_record |
array|null $record |
string $entity_type, int $id |
The record loaded for a permission check. |
plugixa_carwash_record_writers |
array $writers |
none | create and update callables per entity, used by mass actions. |
plugixa_carwash_filterable_fields |
array $fields |
string $entity_type |
What a list may be filtered by. |
plugixa_carwash_filter_api_bases |
array $map |
none | Which URL segment belongs to which filterable entity. |
plugixa_carwash_export_lists |
array $lists |
none | The lists Export CSV can write, keyed by entity slug: each with its REST collection (api_base) and its columns, keyed by the grid field each belongs to. Bookings and customers out of the box. |
Settings, modules and the admin app
| Filter | Value | Other arguments | What it decides |
|---|---|---|---|
plugixa_carwash_settings_defaults |
array $defaults |
none | Default values of the settings. Modules add their own keys here. |
plugixa_carwash_settings_choices |
array $choices |
none | The option lists the settings screen offers, such as currencies and date formats. |
plugixa_carwash_config_settings_keys |
string[] $keys |
none | Which settings are printed onto the admin page for its first paint. |
plugixa_carwash_admin_config |
array $config |
none | The configuration printed onto the admin page. Anyone who can open the app can read it. |
plugixa_carwash_admin_i18n_strings |
array $strings |
none | The translated strings handed to the admin app. |
plugixa_carwash_bootstrap_payload |
array $payload |
none | What the admin app receives when it starts. |
plugixa_carwash_modules |
string[] $classes |
none | The module classes to load. An add-on can contribute a module here. |
plugixa_carwash_apps |
array $cards |
none | The cards in the App Manager. |
plugixa_carwash_app_page_keep_styles |
string[] $handles |
none | Theme stylesheets kept on the full-page staff app, for example a web font it needs. |
plugixa_carwash_is_app_page |
bool $is |
none | Whether this page is the bare, full-page staff app. For a page builder that hides the shortcode from WordPress. The capability check still applies. |
plugixa_carwash_dev_bar |
bool $enabled |
none | Whether the development strip, with a sign-out link, is drawn at the foot of public pages for signed-in users. On by default when WP_ENVIRONMENT_TYPE is anything but production. |
Scheduled events
These are WordPress cron hooks, not hooks to attach your own code to. They are listed so you can recognise them in a cron manager.
| Hook | Runs | Does |
|---|---|---|
plugixa_carwash_expire_holds |
Every five minutes | Cancels bookings whose online payment was not finished in time. |
plugixa_carwash_send_reminders PRO |
Hourly | Sends reminders. |
plugixa_carwash_webhooks_send PRO |
When a delivery is due | Sends webhooks. |
plugixa_carwash_webhooks_sweep PRO |
Hourly | Picks up missed webhook deliveries and prunes the log. |
plugixa_carwash_membership_sweep PRO |
Daily | Membership housekeeping. See Memberships. |
plugixa_carwash_fleet_statements PRO |
Daily | Company account statements. See Company accounts. |
Switching an app off in the App Manager clears its scheduled events.