Code snippets, filters and background job details for developers extending Lightspeed POS for WooCommerce. Everything here is for custom code: for setup and everyday fixes, start with Get started with Lightspeed POS and the troubleshooting pages for X-Series and R-Series.
Most hooks apply to one series only, so each one below says which. The plugin also ships its hooks reference in the docs/hooks/ folder of the plugin download.
When you’d use this
- Add WooCommerce shipping or fees to R-Series sales, or change what a sale sends.
- Change which order statuses create R-Series sales.
- Turn on partial refund sync for X-Series.
- Tune polling and batch sizes for a large X-Series catalog.
- Inspect or run background jobs from the command line.
Code snippets
Ready-made snippets are in Kestrel’s public snippets repository at github.com/kestrelcommerce/snippets, under woo-extensions/lightspeed/. Follow our guide to safely adding custom code to WooCommerce before using one, and test on a staging site. All of them are for R-Series.
| Snippet | What it does |
|---|---|
add-woo-shipping-to-lightspeed-orders.php | Adds WooCommerce shipping to the Lightspeed sale as a line for a placeholder "Woo Shipping" item, so the sale and order totals match |
add-woo-fees-to-lightspeed-orders.php | Adds WooCommerce order fees to the sale as a line |
add-woo-order-notes-to-lightspeed-receipt.php | Prints the customer’s order note on the Lightspeed receipt |
skip-lightspeed-sync-for-unlinked-products.php | Skips syncing an order that contains any product not linked to Lightspeed, instead of syncing a partial sale. The order gets the snippet’s note and the plugin’s "Not synced to Lightspeed" note. |
set-custom-sale-price.php | Sets the WooCommerce sale price from another named Lightspeed price level (case-sensitive) when it’s lower |
update-woo-tax-class.php | Maps a Lightspeed tax class to a WooCommerce tax class on import, for example Zero rate |
debugging-new-products-not-importing.php | Logs each item the product changes job sees, to check whether a late creation time makes new items miss the import window, and shows how to widen it |
R-Series filters
Change the sale before it’s sent
kestrel_lightspeed_pos_r_series_sale_sync_payload filters the complete sale payload (array $data, WC_Order $order) right before it’s sent to Lightspeed. It also runs for refund payloads.
add_filter( 'kestrel_lightspeed_pos_r_series_sale_sync_payload', function( $data, $order ) {
// Inspect or change $data before it's sent to Lightspeed.
return $data;
}, 10, 2 );
Add sale lines
wclsi_build_sale_lines filters the sale lines (array $sale_lines, float $shipping_total, int $ls_tax_id, WC_Order $order). $sale_lines['SaleLine'] holds the lines sent; $sale_lines['unlinked_items'] holds the order items left off because they aren’t linked. wclsi_build_partial_refund_lines does the same for partial refunds (array $sale_line, int $ls_tax_id, WC_Order_Refund $refund, WC_Order $order).
When you add a line, adjust the payment to match with wclsi_calc_sale_payment (float $payment, float $shipping_total, float $shipping_tax), which by default leaves shipping out, or wclsi_fees_to_remove_from_ls_payment (array $fees_to_remove, WC_Order $order), which lists the fee items taken off the payment. The shipping and fees snippets show the pattern.
Choose which order statuses create sales
wclsi_sales_sync_statuses filters the statuses (without the wc- prefix) that create a sale. It defaults to the Order statuses setting, or WooCommerce’s paid statuses.
add_filter( 'wclsi_sales_sync_statuses', function( $statuses ) {
$statuses[] = 'on-hold';
return $statuses;
} );
Map taxes and customers
wclsi_set_ls_tax_ratefilters the Lightspeed tax category ID used for an order ($ls_tax_category_id,WC_Order $order), for example to map a rate the export didn’t match.wclsi_customer_foundfilters the Lightspeed customer ID found for an order’s billing email (int $ls_customer_id,WC_Order $order).wclsi_customer_poll_intervalsets how often, in seconds, the plugin reads Lightspeed customers for matching (default86400, one day).
Widen the look-back for new products
wclsi_get_look_back_time sets how many seconds before the last load the New Lightspeed products job looks for new items (default 60). Lightspeed sometimes timestamps an item later than it was created, so it can miss the window. Support commonly uses about four hours:
add_filter( 'wclsi_get_look_back_time', function( $seconds ) {
return 4 * HOUR_IN_SECONDS;
} );
Storefront stock checks
kestrel_lightspeed_pos_r_series_pull_inventory_on_add_to_cartdecides whether to fetch Lightspeed stock when a product is added to the cart (bool $pull_inventory,int $product_id,int $quantity,int $variation_id). It’sfalsefor detected crawlers. Returnfalseto skip the call.kestrel_lightspeed_pos_r_series_storefront_api_timeoutsets the timeout, in seconds, for Lightspeed calls made while serving a shopper.wclsi_inventory_sync_errorfilters the message shoppers see when the stock check fails.
X-Series filters
Change the sale before it’s sent
kestrel_lightspeed_pos_x_series_sale_sync_payload filters the complete sale payload (array $sale_data, WC_Order $order) right before it’s sent to Lightspeed. If you remove register_sale_payments, the payment is recorded on a later sync instead.
add_filter( 'kestrel_lightspeed_pos_x_series_sale_sync_payload', function( $sale_data, $order ) {
// Inspect or change $sale_data before it's sent to Lightspeed.
return $sale_data;
}, 10, 2 );
Sync partial refunds on X-Series
Partial refunds aren’t synced by default. kestrel_lightspeed_pos_x_series_sync_partial_refunds (default false) turns on an experimental sync that records each partial refund as a return sale linked to the original sale. The plugin reads it once while WordPress loads plugins, so add it in a must-use plugin or another plugin’s main file; a theme’s functions.php loads too late. Test it on a staging site first.
add_filter( 'kestrel_lightspeed_pos_x_series_sync_partial_refunds', '__return_true' );
The order notes say "Partial refund synced to Lightspeed (Return Sale ID: …)" or "Lightspeed partial refund sync failed: …".
Tune polling and batch sizes
For large catalogs or slow servers:
kestrel_lightspeed_pos_x_series_polling_products_poll_sizeandkestrel_lightspeed_pos_x_series_polling_customers_poll_size: how many items each polling run checks (default5, minimum 1).kestrel_lightspeed_pos_x_series_pull_products_batch_sizeandkestrel_lightspeed_pos_x_series_pull_customers_batch_size: how many items each import batch processes (default50, minimum 1).kestrel_lightspeed_pos_x_series_image_download_batch_size: how many images each image download run fetches (default10).
add_filter( 'kestrel_lightspeed_pos_x_series_pull_products_batch_size', function( $batch_size ) {
return 25;
} );
Keep the activity log longer
kestrel_lightspeed_pos_x_series_activity_log_retention_period sets how long activity log records are kept (a DateInterval, default two months), and kestrel_lightspeed_pos_x_series_activity_log_cleanup_interval how often the cleanup runs (default one month).
add_filter( 'kestrel_lightspeed_pos_x_series_activity_log_retention_period', function( $period ) {
return new DateInterval( 'P6M' );
} );
Background jobs and WP-CLI
Both series run their background work through WooCommerce’s Action Scheduler, which needs WP-Cron (or a system cron calling wp-cron.php) to run.
R-Series jobs use the wclsi group:
| Hook | What it does |
|---|---|
wclsi_poll | Checks Lightspeed for changed items and updates linked products |
check_for_new_ls_prods | Checks Lightspeed for new items |
wclsi_ls_attribute_sets_poll | Checks Lightspeed for new matrix attribute sets |
wclsi_ls_customer_ids_poll | Reads Lightspeed customers for matching, daily |
wclsi_taxonomy_poll | Imports Lightspeed manufacturers as brands, daily |
wclsi_sync_changes_to_lightspeed | Sends a queued WooCommerce product change to Lightspeed |
X-Series job hooks start with kestrel_lightspeed_pos_x_series_.
# R-Series: pending, failed, and run now
wp action-scheduler list --status=pending --group=wclsi
wp action-scheduler list --status=failed --group=wclsi
wp action-scheduler run --group=wclsi
R-Series database tables
The R-Series integration keeps its link data in these tables (with your database prefix): wclsi_items, wclsi_item_shops, wclsi_item_prices, wclsi_item_images, wclsi_item_e_commerce, wclsi_item_categories, wclsi_item_attribute_sets, wclsi_customers and wclsi_tax_lookup.
In wclsi_items, item_id is the Lightspeed item ID (item_matrix_id for matrix items), wc_prod_id the linked WooCommerce product, wclsi_import_date and wclsi_last_sync_date the import and last update times, and wclsi_is_synced whether the product is linked. To list items loaded but never imported:
SELECT * FROM wp_wclsi_items WHERE wc_prod_id IS NULL;
The Import date and Last update columns in the Product Importer show Lightspeed’s own timestamps, not your server’s time.
Common issues
My filter has no effect
Check the series: an r_series or wclsi_ hook does nothing on an X-Series connection, and the reverse. Filters that change sales only run when sales or order sync is on.
The shipping snippet makes the sale unbalanced
The snippet adds a sale line and raises the payment by the shipping total. Check that the placeholder item’s ID is set and that no other code changes the payment through wclsi_calc_sale_payment.
