Plugin documentation

Advanced Restaurant for WooCommerce

Turn WooCommerce products into a one-page restaurant menu with a popup for options, PPOM add-ons, opening hours and a delivery day and time at checkout, in the classic checkout and the Checkout block.

Version 1.5 WooCommerce plugin Paid plugin Requires WordPress 6.3 Requires PHP 7.4 Tested up to 7.1 Updated 6 Oct 2026

1. Overview

Advanced Restaurant for WooCommerce turns your WooCommerce products into a one-page ordering menu and adds the things a kitchen needs on top: opening hours, an "Open Now" or "Closed" status, a header message, a "restaurant closed" switch, extras and add-ons from PPOM for WooCommerce, and a delivery day and time selection at checkout. On the Plugins screen the plugin is listed as "Restaurant for WooCommerce Pro", which is the name in its file header; the settings screen is titled "Restaurant Settings".

You add the shortcode [wxp_restaurant] to a page. Each product category with published products becomes a section of the menu. Customers search, filter, add dishes with one click, choose variations and add-ons in a popup, review their order in a basket that slides in from the right, and continue to the normal WooCommerce cart and checkout. At checkout they choose a delivery day and, if you enable it, a time slot, based on your opening hours.

The plugin keeps no menu of its own. Names, photos, prices, short descriptions, stock and categories come from your WooCommerce products. Orders are ordinary WooCommerce orders, so your payment methods, shipping methods, coupons, taxes and emails keep working.

How it works

  1. You put your dishes in product categories, tick Is Non-Vegetarian where it applies, and add the shortcode to a page.
  2. In WooCommerce → Restaurant you set opening hours, switch on delivery day and time selection, choose what the menu shows, and pick colours.
  3. Customers add dishes. A dish without options goes straight into the basket. A dish with variations or PPOM fields opens a popup first. The request goes to WooCommerce's AJAX endpoint (?wc-ajax=wxp_add_to_cart), the server prices any add-ons, and the basket on the page is refreshed.
  4. At checkout the customer picks a delivery day (and time). The choice is validated against the slots that are available at that moment, saved to the order, and shown on the thank-you page, in order emails and on the order screen.

At a glance

One-page menu

Sticky category list on screens 768px wide and up, a category drop-down below that, live search and a Veg Only or Non Veg Only filter.

Popup for options

Variable products open a popup with one drop-down per attribute. Simple products can carry PPOM for WooCommerce fields: select, radio, checkbox, text and textarea, with fixed or percentage prices.

Sliding basket

Items, quantities, options and prices, a remove button for each line, and Cart and Checkout buttons. It can be switched off.

Opening hours

From and to times for each day of the week, an "Open Now" or "Closed" badge with today's hours, a header message and a closed message.

Delivery day and time

A required Delivery Day (and optional Delivery Time) at checkout, on the classic checkout and in the Checkout block, with an ASAP option while you are open.

Twelve colours

Colour pickers for the menu, popup, basket and messages, and switches for the basket, search bar and Veg filter. No code needed.

What it does not do

  • It does not stop ordering when you are closed. Opening hours and the "Restaurant is closed" switch change the badge, the messages and the delivery fields. Customers can still add items and check out (the one exception: with Display Date Selection on and no delivery day available, checkout is refused; see Delivery day and time at checkout). With the closed switch on, the delivery fields are hidden and not required.
  • It does not limit the number of orders per slot, add delivery fees or zones, block holidays, or offer separate pickup and delivery choices. The fields are called Delivery Day and Delivery Time for every order.
  • It does not support opening hours that run past midnight (see Opening hours) or more than one opening period per day.
  • It does not show PPOM fields on variable products, and it ignores PPOM field types other than select, radio, checkbox, text and textarea.
  • It does not let customers change quantities in the basket. They can remove a line, or change quantities on the cart page.
  • It is built for one menu per page. The markup uses fixed element IDs and the script reads the first menu's data only.
  • It does not create kitchen displays, order-status pages or printing. It has no block, widget, REST route or WP-CLI command.

How it relates to Restaurant for WooCommerce (free)

Restaurant for WooCommerce is the free plugin with the same shortcode (see its documentation). This plugin replaces it; it does not extend it.

  • Can both be active? WordPress allows it, but only this plugin works. After all plugins have loaded, the free plugin checks for this plugin's class WooExperts_Restaurant_Pro (or the function wxp_restaurant_pro()), and if it exists, the free plugin does not start. It registers no shortcode, menu item, script or style. On the Plugins screen, administrators see the free plugin's notice: "Restaurant for WooCommerce Pro is active, so the free Restaurant for WooCommerce plugin is not used. You can deactivate it." Network activation counts too.
  • Same shortcode. [wxp_restaurant] and categories="..." work the same way. The same WooCommerce → Restaurant menu item (slug wxp-restaurant) is used, and the same product tick box and meta key, _is_nonveg, so your diet flags carry over in both directions.
  • No data moves. The free plugin has no settings, so there is nothing to import. Customisations made for the free plugin (CSS variables such as --wxp-accent, and theme templates in your-theme/restaurant-for-woocommerce/) do not apply to this plugin. This plugin has its own layout and templates.
  • Only in this plugin: opening hours, the "Open Now" or "Closed" status, header and closed messages, the closed switch, PPOM add-ons, delivery day and time selection, the settings screen (switches for basket, search and filter, and colours), and the licence and update client.
  • Different front end. This plugin's menu is not the redesigned menu of the free plugin 1.2. The comparison below lists the differences that follow from the code.
TopicAdvanced Restaurant for WooCommerce 1.5 (this plugin)Restaurant for WooCommerce 1.2 (free)
Adding a dishAn Add button; each click adds oneAdd button that becomes a quantity stepper
PopupOnly for dishes with options (variations or PPOM fields)For every dish: photo, description, variations
Variation stock in the popupNot checked; WooCommerce rejects the add and the message is shownOut-of-stock combinations are disabled
BasketSide panel with a remove button per lineDrawer with quantity steppers, line totals and subtotal
Products hidden from the catalogListedNot listed
"Hide out of stock items" (WooCommerce)Not followed; out-of-stock dishes show "Sold out"Followed
Order of dishesNewest first (WooCommerce's default product query order)WooCommerce sort order, then title
Child categoriesListed as sections of their own; a parent section also lists its children's dishesParent first, children indented; no repeats
External and grouped productsShown as "Sold out" (WooCommerce reports them as not purchasable)A button: external products link to their own address, grouped and other types to the product page
Security token in a cached pageShows "Your session has expired" until the page is reloadedGets a fresh token and retries
ScriptsjQuery, fancyBox and WooCommerce's BlockUIOne script, no jQuery
Add-to-cart checks by other pluginswoocommerce_add_to_cart_validation is not run (see What WooCommerce still checks)Run

2. Requirements

ItemRequirementNotes
WordPress6.3 or laterTested up to 7.1. From the plugin header.
PHP7.4 or laterFrom the plugin header.
WooCommerce8.0 or laterTested up to 11.1. The header carries Requires Plugins: woocommerce; WordPress 6.5 and later uses it at activation (support was added in WordPress 6.5.0, checked in the WordPress 7.1.2 source). The plugin starts only if the WooCommerce class exists, and shows no notice of its own when it does not. Then the shortcode is not registered, and WordPress prints it as plain text.
Checkout block delivery fieldWooCommerce 8.9 or later recommendedThe block field uses WooCommerce's Additional Checkout Fields API and the field location order. The plugin registers it only when the function woocommerce_register_additional_checkout_field() exists. In the WooCommerce 11.1.2 source the older location name additional is marked deprecated since 8.9.0 in favour of order, so use 8.9 or later if you use the Checkout block. The classic checkout does not depend on it.
PPOM for WooCommerceOptionalNeeded only for add-ons. The plugin checks for the class PPOM_Meta and reads its fields property (both present in PPOM for WooCommerce 34.0.8). Get it from wordpress.org. Without it, nothing changes except that no add-ons are offered.
Scripts that must loadjQuery and WooCommerce's BlockUIThe menu script depends on jquery, the bundled fancyBox 3.5.7 and the BlockUI script that WooCommerce registers (handle wc-jquery-blockui, or jquery-blockui on older WooCommerce).
HPOS and Cart/Checkout blocksDeclared compatibleThe plugin declares compatibility with custom_order_tables and cart_checkout_blocks. Order data is written through WooCommerce order objects.
Site timezoneSet it correctlyOpening hours, the status badge and delivery slots use the timezone in Settings → General (wp_timezone()).
Outgoing connectionswpexpertshub.com over HTTPSOnly for the licence and update client (see Privacy). The menu itself makes no external request.
LicenceOptional for useThe plugin's features work without an active licence. The licence unlocks automatic updates.

3. Installation

  1. Make sure WooCommerce is installed and active.
  2. Open Plugins → Add Plugin, click Upload Plugin, choose the plugin zip you downloaded from wpexpertshub.com, and click Install Now, then Activate. The menu label is the one in WordPress 7.1.2; older versions word it slightly differently.
  3. If the free Restaurant for WooCommerce plugin is installed, deactivate it. It stays idle while this plugin is active, and its shortcode is the same.
  4. Open Plugins → WpExperts Hub Licences, paste your licence key and click Activate licence (see The licence and updates).
  5. Add [wxp_restaurant] to a page, then open WooCommerce → Restaurant to set opening hours, delivery fields, menu options and colours.

The plugin's row on the Plugins screen carries two links: Settings (to WooCommerce → Restaurant) and Licence (to the licence screen, for users who can manage_options).

The licence and updates

The plugin bundles the WpExperts Hub licence and update client (licence/class-wpxh-licence-client.php, version 2.0.0). One screen, Plugins → WpExperts Hub Licences, manages every WpExperts Hub plugin on the site that bundles the client. Each plugin has its own card. This plugin's card is titled "Advanced Restaurant for WooCommerce". It is shown to users who can manage_options.

Activate a licence

  1. Find your licence key in the purchase email. The screen says it is also shown in My Account → Downloads on wpexpertshub.com.
  2. Open Plugins → WpExperts Hub Licences, paste the key into the Licence key field of this plugin's card and click Activate licence. Only letters, digits and hyphens are kept from what you type.
  3. The card changes its badge from "Not active" to "Active" and shows the key masked (the first four and last four characters; a key of 10 characters or fewer is shown in full). The server's answer is shown in a message above the form.

Deactivate a licence

Click Deactivate licence on the card. The screen advises: "Deactivate before moving the plugin to another live site." The plugin tells the server, and removes the key and the status from this site even if the server cannot be reached or has already removed the key, so a site is never stuck. In that case the message reads "Licence removed from this site."

Lost your key

While no licence is active, a section "Can't find your key? Email it to me" opens a field. Enter the email address used for the purchase and click Send licence key. The plugin asks the server to email the key to that address. If the address is not valid you see "Please enter the email address used for the purchase."

Updates

  • Updates come through the normal WordPress updates screen (Dashboard → Updates) and the Plugins screen. The client adds this plugin to the list WordPress builds when it checks for plugin updates.
  • The update information is cached for 12 hours. If the server cannot be reached, the previous information is kept and the plugin tries again after one hour.
  • When the server gives no download address (no active licence on this site), the plugin row under an available update says "Automatic update is unavailable for this plugin. Activate your licence to enable updates." with a link to the licence screen. The comments in the client say the server returns the package address only for sites with an active licence; the server is outside this plugin, so that part is not checked here.
  • On the Plugins screen, administrators see "Activate your WpExperts Hub licence to receive updates for: ..." with a Manage licences link while any plugin that uses the client has no active licence.
  • If the server later says a licence is no longer active for this site, the card shows "Not active" and the key stays in the field so you can activate it again.
  • The plugin works without a licence. The code never checks the licence status before running a menu, settings or checkout feature; the licence only affects updates.

What the client sends, and when, is listed under Privacy.

What activation creates

Nothing. The plugin registers no activation hook and creates no table, user role, cron event, page or upload folder. The settings option _wxp_restaurant_opts is first written when you save WooCommerce → Restaurant. The licence options are written when you activate a licence.

Updating

Your settings, licence status and order data are kept. Notes for sites coming from earlier versions (from the changelog for 1.5):

  • Colours are now printed as inline CSS on the menu page. Earlier versions wrote a stylesheet and fonts to wp-content/uploads/rfw. Version 1.5 no longer writes there, and the code does not delete an existing folder.
  • New orders store the delivery day as Y-m-d. Orders stored with the old text format still display correctly.
  • The licence client was replaced. Existing activations are kept.
  • The text domain is now restaurant-for-woocommerce-pro. Translation files made for the old text domain no longer apply.

Deactivating and deleting

The plugin has no uninstall.php and no deactivation hook. Deactivating or deleting it removes no data: the settings option, the licence options, the _is_nonveg product meta and the delivery details on orders all stay. Deactivate your licence first (see above) if you delete the plugin or move it to another site.

Multisite

The plugin starts on plugins_loaded (priority 5), so WooCommerce is found when it is network-activated. Settings and licence options are stored per site (get_option). The licence screen is added with admin_menu, so it appears under each site's Plugins menu; nothing is added to the network admin.

4. Quick start

  1. Check that Settings → General has the right timezone.
  2. Put your dishes in product categories. Tick Is Non-Vegetarian on meat and fish dishes.
  3. Create a page with the shortcode [wxp_restaurant] (add categories="starters,mains" to pick and order sections) and publish it. Use the shortcode once per page.
  4. Open WooCommerce → Restaurant → Menu Settings. Enter From and To times for each day (24-hour, for example 10:00 and 22:00; 0 closes a day) and click Save Changes.
  5. Open the Checkout Fields tab, tick Display Date Selection (and Display Time Selection if you want time slots), keep the interval at 15 or change it, and save. Enter opening hours first: with no open days, customers cannot check out.
  6. Place a test order. Check the Delivery Day and Delivery Time fields at checkout, then the thank-you page, the email and the order screen.
  7. Optional: install PPOM for WooCommerce and attach a field group to a simple product to offer add-ons.

5. Features

ShortcodeResult
[wxp_restaurant]Every product category that has published products.
[wxp_restaurant categories="starters,mains,drinks"]Only these category slugs, in this order. Slugs go through sanitize_title(); a slug that matches no category is ignored. Added in 1.5.

The shortcode also accepts a menu attribute (default lunch). It changes nothing on the page; it is only passed to the wxp_restaurant_menu_category_args filter as $menu. In the admin area (for example in some page builders' editors) the shortcode prints nothing.

  • Categories. Without the attribute, the plugin asks WordPress for product categories that are not empty. In WooCommerce 11.1.2 they come back in the order of Products → Categories. Child categories are sections of their own, in the order WooCommerce returns them (not grouped under their parent). Each name carries a count in brackets. The server prints the category's product count as WordPress stores it (products assigned directly to that category). When the page loads, the script replaces the count in the category list with the number of dishes in that section (a parent section includes its children's dishes) and updates it while a customer searches or filters. The count in the small-screen drop-down is not changed by the script.
  • Dishes. For each category the plugin asks WooCommerce for products with the status Published, with no limit on the number. WooCommerce's default product order applies, which is newest first. A product in a child category is also listed in its parent's section (WooCommerce includes child categories in a category query by default).
  • No other filter. Catalog visibility is not checked, so products set to "Hidden" are listed. WooCommerce's "Hide out of stock items" setting is not consulted: an out-of-stock dish stays on the menu and shows Sold out.
  • If there are no categories to list, the menu area shows the header, the search and filter controls and the script's message "No items found that match your search/filter."
  • Screens 768px wide and up: a category list on the left (sticky while you scroll) and the dishes on the right. Clicking a category scrolls to its section over 0.8 seconds. The active category follows the section in view.
  • Screens up to 767px wide: the category list is replaced by a drop-down, Select Category, that sticks to the top. Categories with no matching dish are disabled in it.
  • Each section starts with a heading with the category name. Each dish shows a vegetarian or non-vegetarian mark, its name, price (as WooCommerce prints it, sale prices included), short description (HTML kept) and, if the product has one, its photo (floated left, up to 120px wide, 86px on small screens).
  • Above the dishes: the header (see Header and closed message), the Veg filter and the search box. The last two can be switched off.

Search and Veg filter

  • The search box (placeholder "Search within menu") filters as you type. It matches the dish name and the short description, ignoring upper and lower case (accents are not ignored). A clear button empties it.
  • Veg Only and Non Veg Only are two tick boxes that work like radio buttons: ticking one unticks the other. Search and filter combine.
  • Sections and categories with no match are hidden. When nothing matches, the menu shows "No items found that match your search/filter."
  • Both controls are switched off in WooCommerce → Restaurant → Menu Options (see Menu Options).

The Is Non-Vegetarian flag

Edit a product and tick Is Non-Vegetarian in the row of tick boxes next to the product type. Its help text is "Check if item is Non-Vegetarian." The tick box shows for simple and variable products. Every other product is shown with the vegetarian mark.

  • The value is stored in the product meta _is_nonveg as yes or no.
  • It is saved only when you save a product from the classic product editor (the plugin hooks woocommerce_admin_process_product_object). Quick edit, bulk edit, the REST API and imports leave it unchanged. Before 1.5 those saves reset it to "no".
  • To set it in an import or script, write the meta key _is_nonveg with the value yes.

Adding dishes

  • A dish without options (a simple product with no PPOM fields): clicking Add adds one to the basket. Each further click adds one more. While the request runs, an overlay covers the menu.
  • A dish with options (a variable product, or a simple product with PPOM fields): clicking Add or the underlined link Customization under it opens the popup.
  • A dish that cannot be bought shows Sold out instead of the button. That covers out-of-stock products and any product that WooCommerce reports as not purchasable, for example a product without a price. In WooCommerce 11.1.2, external and grouped products always report "not purchasable", so they show as "Sold out" on this menu.
  • A short message tells the customer the result ("Item successfully added to your cart." or the reason it failed). In the popup it appears inside the form and the popup closes 1.5 seconds after a success; on the menu it appears as a message at the bottom of the page for 2.5 seconds.
  • After a successful add, the basket and its count are refreshed, and the theme's mini cart is asked to refresh through the jQuery event wc_fragment_refresh. The plugin does not dispatch the block mini cart's own event.
  • Items and keyboard: the Add button, the category list and other controls are focusable and respond to Enter and Space. Esc closes the basket.

Variable products in the popup

  • The popup shows the product name, its price in brackets (for a variable product the lowest price, prefixed with "From"), one drop-down per attribute (each starting with "Select Option"), a quantity box and the button Add to cart.
  • When you choose a value, the other drop-downs are narrowed to the values that exist together with it. When every attribute is chosen, the price changes to the chosen variation's price. Clear Selection resets the choices.
  • Without a full selection, adding shows "Please select option." For attributes whose variation accepts "Any ..." the customer's choice is sent along and WooCommerce validates it.
  • The variations offered are those WooCommerce returns as available for the product. In WooCommerce 11.1.2 that excludes variations that are disabled (not published) or without a price and, only when "Hide out of stock items" is on, out-of-stock variations. The popup does not read the stock status of a variation, so with that setting off an out-of-stock variation can be selected; WooCommerce then refuses it when the customer clicks Add to cart, and its message ("You cannot add ... to the cart because the product is out of stock.") is shown in the popup.
  • The popup is a fancyBox 3.5.7 window. It locks page scrolling while open.

PPOM for WooCommerce add-ons

With PPOM for WooCommerce active, fields attached to a simple product (directly, or through its categories) appear in the popup. The plugin also treats the product type subscription (from WooCommerce Subscriptions) like a simple product for this. It does not offer PPOM fields on variable products.

PPOM field typeHow it appearsChecks in the browser
selectDrop-down with "Select Option". The option named in the field's selected setting is preselected.Required: a value must be chosen ("Please select option.").
radioRadio buttons.Required: one must be ticked.
checkboxTick boxes.Required: at least one. The field's minimum and maximum ticked options are enforced ("Minimum N options required.", "Maximum N options allowed.").
textSingle-line box.Required: not empty ("This field is required.").
textareaMulti-line box.Required: not empty.
  • A field is used only if its type is one of these five and it has a data_name. Fields of other types are not shown, not priced and not required. The plugin does not read PPOM's conditional-logic settings; every supported field is shown.
  • Option labels show the option price in brackets. The label puts your currency symbol in front of the stored price value, so a percentage price is displayed as, for example, ( +£10% ).
  • The field's title is shown as the label, with an asterisk when it is required. The field's description is shown under the input.
  • Server-side processing. The browser only sends the chosen option IDs or text. The server looks the options up again in the product's PPOM fields, so it takes names and prices from PPOM, not from the browser. Options whose ID is not found are dropped. Text is cleaned with sanitize_textarea_field().
  • Prices. A fixed price is added as it is. A percentage price (for example 10%) is calculated against the product's current price, rounded to the store's decimals. For a text or textarea field, the field's price is added when the customer typed something. Only prices above zero are added. The sum is added to the item price in the cart, per unit.
  • Required fields on the server. A required field (required = on in PPOM) with no value stops the add with "%s is a required field." (the field title). The minimum and maximum of checkbox fields are checked only in the browser.
  • In the cart and order. Each field becomes a cart item data entry (name = field title, value = the chosen options, with "(+price)" after options that cost something). The same entry is saved on the order line as item meta, with the price in the text. Two lines with different options stay separate lines in the cart.
  • On the product page itself, PPOM works as usual. The menu does not use PPOM's own cart handling.

The sidebar basket

  • A tab with the basket icon and the item count sits at the right edge of the window, halfway down. Clicking it slides the basket in from the right (25% of the window, at least 350px; the full width up to 480px wide). The close button, or Esc, closes it.
  • The basket is headed "Your Basket". Each line shows a remove button, the thumbnail, the name and options, the quantity and the unit price. An empty basket says "Your basket looks a little empty."
  • Below the lines are two buttons: Cart and Checkout. The Checkout button also shows the cart total (WooCommerce's cart contents total: after discounts, without shipping and fees; it includes tax only when your prices are entered including tax).
  • Quantities cannot be changed in the basket. Remove a line to take a dish out.
  • With Disable Cart Sidebar ticked, neither the tab nor the basket is printed, and the menu has no link to the cart or checkout. Add one with your theme's header cart, or a button on the page.

Opening hours

Opening hours are entered on WooCommerce → Restaurant → Menu Settings, one From and one To box per day, Monday to Sunday.

  • Format. The label says "24-hour format (HH:MM)". The plugin accepts H, H:MM or HH:MM (also with a dot: 10.30), with hours 0 to 23 and minutes 0 to 59, or exactly 24:00. It is saved as HH:MM. Anything else is saved as empty, which closes the day.
  • Closed days. Enter 0 in a box, or leave a box empty, to close that day. With either box empty or 0, the day counts as closed. By default every day is empty, so every day is closed until you enter hours.
  • One period, same day. A day is open only when its To time is later than its From time on the same day. Hours that run past midnight (for example 18:00 to 02:00) count as closed. To close at midnight, use 24:00.
  • What the hours control. The "Open Now" or "Closed" badge on the menu, and the delivery days and times offered at checkout. They do not block adding to the cart. They do not block checkout either, except that with Display Date Selection on, a customer for whom no delivery day is available cannot place the order (see the warning under Delivery day and time at checkout).
  • The badge is worked out when the page is built, in the site's timezone. There is no script that updates it, so a page served from a cache can show an old status.

Header and closed message

The menu header is printed by the action wxp_menu_header (template templates/header.php). It shows, from top to bottom:

  1. Status. If Restaurant is closed is ticked: "Closed", with an information icon whose tooltip says "We are closed today." Otherwise, if the current time is inside today's hours: "Open Now" and today's hours, for example "10:00 am – 10:00 pm (Today)" (12-hour format with am or pm). Otherwise: "Closed", with an icon whose tooltip shows "Opening Hours" and today's hours, or "We are closed today." when today is closed.
  2. Close Message. Shown only when Restaurant is closed is ticked and the message is not empty.
  3. Header Message. Shown only when Display Header Message is ticked and the message is not empty. It appears whether or not the restaurant is closed.

Both messages accept the same basic HTML as a WordPress post (links, bold, lists) and are cleaned with wp_kses_post(). The two message boxes can be coloured (see Colours). The status badge cannot.

Delivery day and time at checkout

When Display Date Selection is ticked and Restaurant is closed is not, the plugin adds a required delivery choice to checkout. With Display Time Selection also ticked, it asks for a time as well. The fields apply to every order on the site, including orders with no menu items and orders using local pickup.

Classic checkout

  • Two selects, printed after the order notes field (the action woocommerce_after_order_notes, which runs even if you turned order notes off): Delivery Day (field name _wxp_date, required) and, with time selection on, Delivery Time (_wxp_time, required).
  • Delivery Day starts with "Select Option", then the available days. If no day is available it holds only "No delivery days available". Delivery Time is filled by a script (wxp-check.js) with the times of the chosen day, keeping the chosen time if it is still valid after the checkout refreshes. The script loads on the checkout page only when date selection is on.
  • On submit the server checks the posted day and time against the slots available at that moment. If the day is empty or not available, checkout stops with "Please choose an available delivery day." If time is on and the time is empty or not valid for that day: "Please choose an available delivery time."

Checkout block (since 1.5)

  • The plugin registers one select with WooCommerce's Additional Checkout Fields API in the location order, so it appears in the "Additional order information" step of the Checkout block. It is required.
  • With date selection only, the field is Delivery Day (id wxp-restaurant/delivery-day); each option is a day, such as "Tomorrow". With time selection on, the field is Delivery Time (id wxp-restaurant/delivery-slot); each option combines day and time, such as "Today – 6:30 pm".
  • If no slot is available the select holds only "No delivery slots available", and checkout fails with "Please choose an available delivery time."
  • The options are worked out when the page loads; the chosen value is validated again against the slots available when the customer places the order.
  • The block field is not registered at all when date selection is off or the restaurant is closed.

Which days and times are offered

  • Days: the plugin looks at today and the next four days, five calendar days in all. You can change that number with the filter wxp_restaurant_delivery_days. A day is skipped if it is closed, or if it is today and the closing time has passed. A day is also skipped when no slot fits into it (for example an opening window shorter than the interval, because the first slot is the first boundary after opening); this applies in date-only mode too. Closed days are not replaced by later days, so you may be offered fewer than five.
  • Labels: "Today", "Tomorrow", then the weekday and date in the form "Thu Oct 9th" (translated by WordPress).
  • ASAP: offered first, on the same day only, while the current time is inside that day's opening hours. Its saved value is ASAP.
  • Slots (with time selection on) are spaced by the Timeslot Interval, on multiples of the interval in the site's timezone (a 30-minute interval gives :00 and :30). The multiples are counted from the start of the Unix epoch in local time, which matches midnight only for intervals that divide 1,440 minutes evenly (5, 10, 15, 20, 30, 60 and so on); an interval such as 7 or 25 minutes gives slot times that shift from day to day. The first slot of a future day is the first boundary after the opening time, and the last slot may fall exactly on the closing time. On the same day a slot must be at least one interval away from the current time.

Worked examples (interval 15 minutes, open 10:00 to 22:00): at 14:07 today, ASAP is offered and the first slot is 2:30 pm (the 2:15 pm boundary is only 8 minutes away). Tomorrow starts at 10:15 am and ends at 10:00 pm. With a 60-minute interval at 14:07, the first slot today is 4:00 pm (3:00 pm is 53 minutes away) and tomorrow starts at 11:00 am. If you open at 10:05, the first slot tomorrow with a 15-minute interval is 10:15 am.

CarefulIf you tick Display Date Selection while no day has opening hours, there is nothing to choose and customers cannot complete checkout. Enter opening hours first, and test checkout after changing them.

Where the delivery details go

The choice is saved on the order as meta, whichever checkout was used (written through the order object, so it works with HPOS):

  • _delivery_date: the day, as Y-m-d for new orders. The Checkout block also stores WooCommerce's own copy of the field value under the key _wc_other/wxp-restaurant/delivery-slot (or .../delivery-day).
  • _delivery_time: the time as shown at checkout (for example 6:30 pm), or ASAP. It is empty when time selection is off.

They are shown as "Delivery Day" and "Delivery Time":

  • On the thank-you page, in a list printed at the start of the order details (woocommerce_thankyou, priority 1). On the block order confirmation page it appears through WooCommerce's "Additional Information" block (woocommerce/order-confirmation-additional-information), which runs the same hook (checked in WooCommerce 11.1.2). The page needs that block to be present.
  • In order emails that run the woocommerce_email_order_meta action (priority 1): in WooCommerce 11.1.2 these include the new order email to the admin, and the processing, completed, on-hold, refunded, cancelled, failed, invoice and customer note emails, in HTML and plain text. A custom email template that omits the hook will not show them.
  • On the order screen under the shipping address, as a read-only list. The block field itself is hidden from the order screen's editable fields, because saving the order would otherwise overwrite the customer's choice with whatever slots are available now.
  • The day is printed in your site's date format (Settings → General). Orders placed with the old text format (before 1.5) are converted for display.

The WooCommerce → Restaurant screen

Open WooCommerce → Restaurant (slug wxp-restaurant, users who can manage_woocommerce). The screen, titled "Restaurant Settings" with "V1.5 Pro", has five tabs in a left column: Menu Settings, Menu Options, Checkout Fields, Menu Colors and Help / FAQ. Every setting is documented under Settings.

  • All tabs are one form. A Save Changes button on any tab saves every field on every tab.
  • After saving, "Settings saved." appears. If the security check fails or you lack the capability, nothing is saved and no message appears.
  • Unticking Display Date Selection unticks Display Time Selection at once (time needs date).
  • The Help / FAQ tab has short answers on the shortcode, the free and Pro plugin, the diet flag, variable products, PPOM, missing categories and support.

Colours

The twelve colours (see Menu Colors) are printed as one block of inline CSS added after the plugin's stylesheet. They apply to the menu, the popup, the basket and the two message boxes. The CSS is added only on pages that show the menu, and only after you have saved the settings at least once. Until then the stylesheet's own colours apply, which differ from the defaults shown on the screen. After the first save, all twelve values (including defaults) are printed.

Where each colour is used:

  • Primary Color: the highlight of the active category (a gradient from white), the basket tab's background and border, the heading of the basket, the text colour of the count badge, and the background of the mobile category bar.
  • Primary Font Color: text in the menu, popup and basket, placeholders, and the active category's text.
  • Link Color: the "Customization" link, the close icons, the remove icons, and the popup's close border.
  • Sidebar Cart Icon Color: the basket icon. Sidebar Qty Count Color: the count badge's background.
  • Qty Button Background: the quantity box in the popup.
  • Button Color and Button Text Color: the popup's Add to cart button and the basket's Checkout button (the total shown inside Checkout is a slightly lighter shade of the button colour).
  • Header Message Text/Background and Close Message Text/Background: the two message boxes.

What WooCommerce still checks

  • The product must exist and be published. A variable product needs a valid variation of its own.
  • WooCommerce's cart runs its own checks when the item is added: purchasable, in stock, enough stock, and "Sold individually". Failures are shown as the message.
  • This plugin does not run woocommerce_add_to_cart_validation. WooCommerce's cart class does not apply that filter itself; the product-page form handler and WooCommerce's own AJAX handler do (checked in WooCommerce 11.1.2). Other plugins that validate items only through that filter (for example minimum or maximum quantity rules) therefore do not see items added from this menu. Their checks at the cart and checkout pages still apply.
  • After an add or remove, the server recalculates the cart totals, so coupons and taxes are applied as normal.
  • When an add fails, the plugin shows WooCommerce's error notices as the message and clears all pending notices of every type from the session.

Security token and page caching

Requests from the menu carry a WordPress nonce (action wooexperts-menu, sent as check) printed into the page. A WordPress nonce is valid for between 12 and 24 hours by default (checked in WordPress 7.1.2), so keep the page's cache at 12 hours or less. When the token has expired, for example because a cache served an old copy of the page, the server answers "Your session has expired. Please reload the page and try again." This plugin does not retry with a fresh token; reload the page. The basket, the count and the header status are built when the page is generated and are refreshed only after a cart change (and the status not at all), so exclude the menu page from page caching, or keep its cache short.

Compatibility

  • HPOS: declared; delivery details are written to order objects.
  • Cart and Checkout blocks: declared; the delivery choice works in the Checkout block (see above).
  • Mini carts: after a cart change the plugin triggers wc_fragment_refresh (jQuery), which refreshes fragment-based mini carts in themes. It does not dispatch wc-blocks_added_to_cart, so the Mini-Cart block shows the change on its next load.
  • Themes: the plugin enqueues its style and script when the shortcode is rendered. The readme and product page do not name tested themes.
  • Translation: text domain restaurant-for-woocommerce-pro. The plugin folder contains no translation files.

6. Settings

All settings are saved together as one array in the option _wxp_restaurant_opts (not loaded automatically at start-up). The Key column shows the array key; the form field name is wxp-res[key]. Ticking a check box saves 1; leaving it empty saves 0. Until you save for the first time, the defaults below apply.

Menu Settings

SettingKeyDefaultWhat it does
Display Header MessagenoticeOffCheck to display the header message on the menu page.
Header Messageg-noticeEmptyText shown above the menu, for offers or delivery news. Basic HTML is kept (wp_kses_post). Shown only when Display Header Message is on and the text is not empty.
Restaurant is closedis-closedOffShows the status "Closed" on the menu and hides the delivery day and time fields at checkout (they are then not required). It does not stop customers from ordering.
Close Messageclose-noticeEmptyText shown in the header while Restaurant is closed is on. Basic HTML is kept.
Opening Hours: From / To, Monday to Sundaydays (days[monday][from], days[monday][to], and the same for the other six days)Empty (closed)24-hour times. Accepted: H, H:MM, HH:MM or with a dot, hours 0 to 23 and minutes 0 to 59, or exactly 24:00. Saved as HH:MM. 0, empty or an invalid value closes the day. To must be later than From on the same day. The hint boxes show 10:00 and 22:00 for Monday to Friday, and 12:00 and 23:00 for Saturday and Sunday; they are examples, not defaults.

Menu Options

SettingKeyDefaultWhat it does
Disable Cart Sidebardisable-sidebarOffCheck to remove the sidebar basket (the tab and the panel) from the menu page. Customers then have no basket on the page.
Disable Veg/Non-Veg Filterdisable-veg-filterOffCheck to remove the Veg Only and Non Veg Only tick boxes. The diet marks on dishes stay.
Disable Searchbardisable-searchbarOffCheck to remove the search box from the menu page.

Checkout Fields

SettingKeyDefaultWhat it does
Display Date SelectiondateOffCheck to ask for a delivery day at checkout (classic and block). Required for every order while the restaurant is not marked closed. Needs opening hours.
Display Time SelectiontimeOffCheck to ask for a delivery time as well. It works only together with Display Date Selection (the screen says the delivery date field must be enabled): if date selection is off, no delivery field is shown at all.
Timeslot Intervaltime-int15Minutes between delivery time slots. A whole number from 1 to 240. Empty, 0 or text is saved as 15; a negative number is saved as its positive value; a number above 240 is saved as 240. It is also the minimum lead time for slots on the same day. Choose a number that divides 1,440 evenly (5, 10, 15, 20, 30, 60 ...), see Delivery day and time at checkout.

Menu Colors

Each setting is a WordPress colour picker. A value is accepted if it is a hex colour such as #b0d9f9 or #bdf (with the #). Anything else is replaced by the default.

SettingKeyDefaultWhat it does
Primary Colorprimary-color#b0d9f9"Primary color used on menu page, like category sidebar background etc."
Primary Font Colorprimary-font-color#43454b"Primary font color used on menu page."
Link Colorlink-color#ff0000"Link and icon colour used on the menu page and popup."
Sidebar Cart Icon Colorcart-icon-color#b0d9f9"Cart icon color."
Sidebar Qty Count Colorqty-color#ff0000"Cart Qty Count color in sidebar."
Qty Button Backgroundqty-btn-color#b0d9f9"Qty button background color in popup."
Button Colorbtn-color#b0d9f9"Add to cart button color in popup." Also the basket's Checkout button.
Button Text Colorbtn-txt-color#3f3f3f"Add to cart button text color in popup." Also the Checkout button's text.
Header Message Texth-text-color#43454b"Header message text color on menu page."
Header Message Backgroundh-bg-color#bce8f1"Header message background color on menu page."
Close Message Textc-text-color#ffffff"Restaurant close message text color on menu page."
Close Message Backgroundc-bg-color#e86c37"Restaurant close message background color on menu page."

The licence settings are not on this screen. They are on Plugins → WpExperts Hub Licences (see The licence and updates).

7. Developer reference

Shortcode

AttributeDefaultMeaning
categoriesemptyComma-separated product category slugs. Order is kept.
menulunchPassed to wxp_restaurant_menu_category_args only.

Filters and actions the plugin fires

HookParametersWhen
Filter wxp_restaurant_menu_product_args$args, $slug, $attsBefore the products of one section are queried with wc_get_products(). Default $args: status = publish, limit = -1, category = array( $slug ).
Filter wxp_restaurant_menu_category_args$args, $menu, $attsBefore categories are fetched with get_terms(). Default $args: taxonomy = product_cat, hide_empty = true; with the categories attribute also slug and orderby = slug__in.
Filter wxp_restaurant_delivery_days$days (int, default 5), $settings (array)The number of calendar days, starting today, that are checked for delivery slots. The result is passed through absint().
Filter wxp_restaurant_delivery_slots$slots (array), $settings (array)After the slots are built. $slots maps Y-m-d to array( 'label' => 'Today', 'times' => array( 'ASAP' => 'ASAP', '2:30 pm' => '2:30 pm', ... ) ). Used for the classic selects, the Checkout block options and server validation; it is called several times per request, so keep it consistent.
Action wxp_menu_header$opts (the settings array)While the menu is printed, at its top. The plugin's own callback (priority 10) includes templates/header.php.
Action wxp_save_restaurant_settings$settings (the sanitised array that was saved)After WooCommerce → Restaurant is saved.
Actions wxperts_ajax_add_to_cart, wxperts_ajax_remove_itemnoneRun by the legacy endpoint ?wxperts-ajax=add_to_cart (or remove_item). The plugin's handlers are attached to them.

Offer seven days of slots, and remove Sundays from every slot list:

add_filter( 'wxp_restaurant_delivery_days', function () {
    return 7;
} );

add_filter( 'wxp_restaurant_delivery_slots', function ( $slots, $settings ) {
    foreach ( array_keys( $slots ) as $date ) {
        $dt = DateTimeImmutable::createFromFormat( '!Y-m-d', $date, wp_timezone() );
        if ( $dt && '0' === $dt->format( 'w' ) ) {
            unset( $slots[ $date ] );
        }
    }
    return $slots;
}, 10, 2 );

Exclude two products from every section, and print a line under the header:

add_filter( 'wxp_restaurant_menu_product_args', function ( $args, $slug, $atts ) {
    $args['exclude'] = array( 123, 456 ); // product IDs
    return $args;
}, 10, 3 );

add_action( 'wxp_menu_header', function ( $opts ) {
    echo '<p class="my-menu-note">Kitchen closes 30 minutes before the end of opening hours.</p>';
}, 20 );

AJAX endpoints

Requests go to WooCommerce's endpoint ?wc-ajax=wxp_<action>, as POST, available to logged-out visitors. Each handler verifies the nonce (action wooexperts-menu, POST parameter check).

EndpointPOST parametersWhat it does
wxp_add_to_cartdata: URL-encoded product (ID), quantity (default 1), variation (variation ID or 0), opts[attribute_pa_size]=value for "Any" attributes, and for PPOM opts[0][data_name]=value (an array of values for checkboxes)Adds an item. A product that is not published, or a variable product without a variation, is refused.
wxp_remove_itemid (cart item key)Removes a line. Apart from a failed security check, it always answers res: true, also for a key that does not exist.

The JSON answer has res (bool), message and fragments: div.wxp-cart-in (the basket HTML) and span.cart-count (the count). The script replaces the elements that match each selector. The legacy endpoint ?wxperts-ajax=add_to_cart (and remove_item) still works for pages cached with an old script; it runs on template_redirect at priority 0, defines DOING_AJAX and sends no-cache headers.

Server messages: "This item is not available.", "Please select option.", "%s is a required field." (PPOM), "Your session has expired. Please reload the page and try again.", "Something went wrong!", "Item successfully added to your cart.", plus WooCommerce's own error notices.

JavaScript configuration

assets/js/menu.js (handle restaurant-script, in the footer, depends on jquery, wxp-fancybox-js and the BlockUI handle) receives the global wxpmenu with: wxp_ajax_url (WooCommerce endpoint with %%endpoint%%), wxp_ajax (home URL, for the legacy endpoint), wxp_nonce, wxp_currency (the currency symbol) and the translated strings (wxp_select, wxp_add_to_cart, wxp_added_to_cart, wxp_wrong, wxp_select_opt, wxp_clear, wxp_no_item, wxp_require, wxp_min, wxp_max, wxp_min_one, wxp_opt_req, wxp_opt_allow, wxp_from, wxp_add, wxp_close). Handles: styles restaurant-style and wxp-fancybox-css; scripts restaurant-script, wxp-fancybox-js and, on the checkout page when date selection is on, restaurant-check (assets/js/wxp-check.js, with the object wxp_check_tr). The style, the menu script and fancyBox are registered on every front-end page and enqueued only when the shortcode is rendered; the checkout script is registered and enqueued only on the checkout page.

Templates

  • templates/menu.php receives $items (category slug to products) and $cats (slug to name and count). It is loaded with wc_get_template() using WooCommerce's default template folder, so WooCommerce looks for your-theme/woocommerce/menu.php and then your-theme/menu.php before the plugin's copy (checked in WooCommerce 11.1.2). The plugin does not document an override path. The bundled script and stylesheet expect the plugin's markup (.wxp-menu-container, .wxp-item-btn, #wxpmenu-cart, ...), and the layout was not built for overriding.
  • templates/header.php is included directly by the wxp_menu_header callback and cannot be overridden. Remove the callback or add your own with the same action.
  • templates/dashboard.php is the admin screen, included directly.

PHP classes and functions

The plugin does not declare a public PHP API. These are the pieces the plugin itself uses.

  • wxp_restaurant_pro() returns the main object (class WooExperts_Restaurant_Pro): settings (get_settings(), get_settings_fields(), get_color_defaults()), PPOM access (get_ppom_fields()). It declares a public property $licence, but nothing assigns it; the licence client is a static class that registers itself.
  • WpExpertshub_Restaurant (classes/class-wpexpertshub-restaurant.php): shortcode, menu data, colours, opening hours (is_open(), get_day_range()), delivery slots (get_delivery_slots()), checkout fields, order display.
  • RFWCP_Wexperts_AJAX (classes/ajax.php): the AJAX handlers, basket HTML and PPOM option processing (get_option()).
  • WPXH_Licence_Client_V2 (licence/class-wpxh-licence-client.php): the licence and update client.
  • Constants: WXP_RESTAURANT_PRO_FILE, WXP_RESTAURANT_PRO_VER (1.5), and for the licence server WPXH_LICENCE_SERVER (optional, see Privacy).
  • Checkout block field ids: wxp-restaurant/delivery-day and wxp-restaurant/delivery-slot (class constants BLOCK_DAY_FIELD and BLOCK_SLOT_FIELD).

WordPress and WooCommerce hooks the plugin uses

HookPriorityPurpose
plugins_loaded5Starts the plugin if WooCommerce is present.
before_woocommerce_init10Declares HPOS and Cart/Checkout blocks compatibility.
init10 (0 for the legacy endpoint)Registers the shortcode, the product tick box, the licence client, and the other hooks of this table.
admin_menu10Adds WooCommerce → Restaurant (and the licence client adds Plugins → WpExperts Hub Licences).
admin_init10Saves the settings form.
admin_enqueue_scripts999Loads the colour picker, admin style and script on the settings screen only.
wp_enqueue_scripts10Registers the scripts and styles; enqueues the checkout script on checkout.
plugin_action_links_<plugin>10Adds the Settings link first in the plugin row.
template_redirect0The legacy ?wxperts-ajax= endpoint.
wc_ajax_wxp_add_to_cart, wc_ajax_wxp_remove_item10The AJAX handlers.
product_type_options, woocommerce_admin_process_product_object10The Is Non-Vegetarian tick box and its saving.
woocommerce_get_item_data10Lists PPOM options on cart items and removes item data rows with an empty name and an empty value or display text (for every cart item, from any plugin).
woocommerce_cart_loaded_from_session / woocommerce_before_calculate_totals999 / 20Adds the option prices to the cart item price.
woocommerce_checkout_create_order_line_item10Saves PPOM options on the order line.
woocommerce_after_order_notes10Prints the classic delivery fields.
woocommerce_checkout_process10Validates the classic delivery fields.
woocommerce_checkout_create_order10Saves the classic delivery fields to the order.
woocommerce_init10Registers the Checkout block field (or registers it at once if that hook already ran).
woocommerce_set_additional_field_value10Copies the block field value into _delivery_date and _delivery_time.
woocommerce_filter_fields_for_order_confirmation10Hides WooCommerce's own line for the block field on the order confirmation (the plugin prints its own). It returns false only for this plugin's two field ids.
woocommerce_admin_shipping_fields20Removes the block field from the editable fields on the order screen.
woocommerce_thankyou1Prints the delivery details on the thank-you page.
woocommerce_email_order_meta1Prints the delivery details in emails.
woocommerce_admin_order_data_after_shipping_address1Prints the delivery details on the order screen.

The licence client adds its own hooks: admin_init, admin_notices, pre_set_site_transient_update_plugins, plugins_api (priority 20), http_request_args, http_request_host_is_external, upgrader_process_complete, in_plugin_update_message-<plugin> and the Licence link in plugin_action_links_<plugin>.

Options, meta and stored data

ItemWhereContent
_wxp_restaurant_optsOption (autoload off)Array: notice, g-notice, is-closed, close-notice, days (day to from and to), disable-sidebar, disable-veg-filter, disable-searchbar, date, time, time-int and the twelve colour keys. Read with defaults filled in; time-int is forced to 1 to 240 (15 when invalid) on every read.
_restaurant-for-woocommerce-pro_licence_keyOption (autoload off)The licence key.
_restaurant-for-woocommerce-pro_key_statusOption (autoload off)active, or inactive when the server reports the licence is no longer active here. Deleted on deactivation.
wpxh_licence_check_v2Site transientCached update information: a hash, and the server's answer per plugin. Lives 12 hours (1 hour after a failed request).
wpxh_licence_msg_<user ID>Transient, 120 secondsThe message shown after a licence form action.
_is_nonvegProduct metayes or no.
_delivery_date, _delivery_timeOrder metaDelivery day (Y-m-d) and time (text, or ASAP).
_wc_other/wxp-restaurant/delivery-slot or .../delivery-dayOrder meta (written by WooCommerce)The raw Checkout block value (Y-m-d|time or Y-m-d).
PPOM option entriesOrder item metaKey: the PPOM field title. Value: the chosen options, comma separated, with "(+price)" after options that cost something.
wxp-menu, wxp-priceCart item data (WooCommerce session)wxp-menu is a list of title and options (each name, id, price). wxp-price is the item's price when it was added; no code in the plugin reads it.

The plugin has no custom tables, user meta, cron events, roles or custom capabilities and no REST routes. The settings screen needs manage_woocommerce; the licence screen needs manage_options.

Form field names

Useful for scripts and tests. Menu page: wxp-search-item (the search box), wxp-item-type (the two Veg filter tick boxes, values veg and non-veg) and wxp-cat-select (the category drop-down on small screens). Settings form: the hidden field wxp-res-save (value 1), the nonce field wxp-res-admin (action wxp-res-save) and the values under wxp-res[...]. Checkout: _wxp_date and _wxp_time.

File layout

  • restaurant-for-woocommerce-pro.php: header, bootstrap, settings, PPOM field reader, licence registration.
  • classes/class-wpexpertshub-restaurant.php: menu, opening hours, slots, checkout fields, order display.
  • classes/ajax.php: AJAX handlers and basket HTML.
  • licence/class-wpxh-licence-client.php: licence and update client.
  • templates/menu.php, templates/header.php, templates/dashboard.php.
  • assets/js/menu.js, assets/js/wxp-check.js, assets/js/admin.js, assets/js/jquery.fancybox.min.js (fancyBox 3.5.7).
  • assets/css/front.css, assets/css/admin.css, assets/css/jquery.fancybox.css, assets/fonts/wcc.* (icon font), assets/imgs/loader.svg.
  • readme.txt.

8. Privacy

TopicWhat the plugin does
Data stored by the pluginYour settings (option _wxp_restaurant_opts), the diet flag on products, the delivery day and time on orders (_delivery_date, _delivery_time, and WooCommerce's copy of the block field), and PPOM choices on order lines. Text a customer types into a PPOM text or textarea field (for example a kitchen note) is stored in the cart session and then on the order line; treat it as customer data. Licence data is listed below.
CookiesThe plugin sets none. When a customer adds an item, WooCommerce itself sets its cart cookies (wp_woocommerce_session_*, woocommerce_items_in_cart, woocommerce_cart_hash; names checked in WooCommerce 11.1.2). The script uses no localStorage or sessionStorage.
Data sent from the menu, the checkout and the admin screensNothing leaves your site. Requests go to your own server. The plugin loads no external font, script or image.
Data sent to wpexpertshub.comOnly by the licence and update client, over HTTPS by default (https://wpexpertshub.com/wp-json/wphub-licence/v1/, timeout 20 seconds). See the table below.
RetentionThe plugin deletes nothing by itself. Order data stays with the order. The update cache expires after 12 hours (1 hour after a failed request); the message transient after 120 seconds.
Deactivation and deletionNeither removes anything. The settings, licence options, product meta and order meta stay. "Deactivate licence" removes the two licence options.
Privacy policy text, exporter, eraserThe plugin adds none. WooCommerce's own export and erase tools cover order data it handles.

What the licence client sends

WhenRequestData sent
You click Activate licenceactivateThe plugin slug (restaurant-for-woocommerce-pro), the licence key you entered, and your site URL (site_url()).
You click Deactivate licencedeactivateSlug, the stored key, and the site URL.
You click Send licence keysend-keySlug and the email address you typed.
WordPress saves its plugin update information (its regular update check, or when you open the updates screen), and when "View details" is opened for the plugincheckThe site URL and, for every plugin that uses this client on the site (including this one): slug, stored licence key (empty if none) and installed version. Sent at most once every 12 hours while nothing changes. A change in any of those values, a plugin update, or any licence form action makes the next check go to the server at once. After a failed request the next try is in one hour.

All requests are sent as JSON by wp_remote_post(); WordPress adds its usual User-Agent header with the WordPress version and your site's address (checked in WordPress 7.1.2). Certificate checking is on, except when the licence server's host is a local development host (.local, .test, .localhost, localhost or 127.0.0.1). The server address can be changed with the constant WPXH_LICENCE_SERVER or the filters wpxh_licence_server and wpxh_licence_sslverify. The plugin does not send order, customer or menu data to the licence server. What the server does with the data is outside this plugin.

9. Troubleshooting

ProblemLikely cause and fix
The menu says "Closed" although the restaurant is open.No hours are saved for today (empty means closed, and all days are empty by default), a value was invalid and saved as empty, the To time is not later than the From time, or the hours cross midnight. Check the day in WooCommerce → Restaurant → Menu Settings. Also check the timezone in Settings → General, and that Restaurant is closed is not ticked.
Customers cannot check out: "Please choose an available delivery day."Display Date Selection is on but no day offers a slot: no opening hours are set, or all of the next five days are closed, or it is past closing time on the only open day. Enter opening hours, widen them, raise wxp_restaurant_delivery_days, or untick Display Date Selection.
"Please choose an available delivery time."The time is empty, or the slot is no longer available (for example it was valid when the page loaded and has passed). Choose again. If it happens for everyone, check hours and the Timeslot Interval.
No delivery times for today.Today's slots start at least one Timeslot Interval after the current time, and stop at the closing time. A 60-minute interval late in the evening leaves no timed slot; while you are open, ASAP is still offered, and once the closing time has passed today is not offered at all.
The delivery fields are missing at checkout.Display Date Selection is off, Restaurant is closed is on, or (classic checkout) the theme's checkout template does not call woocommerce_after_order_notes. In the Checkout block the field appears in "Additional order information", and needs a WooCommerce version that supports additional checkout fields (see Requirements).
The Delivery Time select stays empty on the classic checkout.The script wxp-check.js did not run, or a JavaScript error from the theme stopped it. It loads only on the checkout page and only when date selection is on. A script optimiser that removes or delays it has the same effect.
The Checkout block shows "No delivery slots available".No slot is offered right now (see the rows above). The options are built when the page loads, so reload after changing hours.
Delivery details are missing in an email.The order was placed with the fields off or the restaurant closed, so nothing was saved. Or a custom email template does not run woocommerce_email_order_meta.
Add-ons (PPOM fields) do not appear in the popup.PPOM for WooCommerce is not active; the product has no field group (directly or through its categories); the product is a variable product (not supported); or the fields are of a type other than select, radio, checkbox, text or textarea.
"Please select option." or "This field is required." in the popup.A required drop-down, radio group or text field is empty. Choose or fill it. For variable products, every attribute needs a value.
A percentage add-on price looks wrong.The popup shows the stored percentage with your currency symbol in front of it (for example +£10%). In the cart the amount is calculated against the product price and shown as money.
"Your session has expired. Please reload the page and try again."The page carries an expired security token, usually from a cached page. Reload. Exclude the menu page from caching or keep its cache at 12 hours or less (a token is valid for 12 to 24 hours).
Adding an item shows a WooCommerce message about stock.The variation or product is out of stock or has too few units. The popup does not hide out-of-stock variations unless "Hide out of stock items" is on in WooCommerce.
A product hidden from the catalog is on the menu, or an out-of-stock dish is shown as "Sold out" although "Hide out of stock items" is on.This plugin does not read catalog visibility or that setting. Exclude products with the filter wxp_restaurant_menu_product_args (see the example under Developer reference), or move them out of the menu categories.
A dish appears in two sections.It belongs to a child category and its parent is also listed; a parent's section includes its children's dishes. Use the categories attribute to list only the sections you want.
External or grouped products show "Sold out".WooCommerce reports these types as not purchasable, and the menu shows "Sold out" for anything not purchasable.
The basket tab is missing.Disable Cart Sidebar is ticked in Menu Options.
Colour changes do nothing.The colours are printed only after the settings were saved at least once, only on pages that show the menu, and a page or CSS cache may serve the old page. The status badge ("Open Now" or "Closed") is not covered by the colour settings.
Two menus on one page behave oddly.Use the shortcode once per page. The markup uses fixed IDs and the script reads the first menu's data only.
No update appears.The licence is not active (activate it on Plugins → WpExperts Hub Licences), or the plugin's own cached answer is still fresh (up to 12 hours). WordPress's Check again link on Dashboard → Updates does not clear that cache. Any licence action on the licence screen, or a finished WordPress update, does.
The plugin row says "Automatic update is unavailable for this plugin."The server returned no download address for this site, usually because no licence is active. Activate your licence.
The licence will not activate.Read the message above the form (it comes from the server or says what failed). Check the key (only letters, digits and hyphens are kept), that the site can reach https://wpexpertshub.com (a firewall, security plugin or blocked outgoing requests would stop it), and the message "The licence server returned an unexpected response (HTTP ...). Please try again later."
A notice on the Plugins screen says the free plugin is not used.Both plugins are active. Deactivate and delete the free plugin.

10. FAQ

Which plugin do I get with this product?

One plugin, the one documented on this page. The free Restaurant for WooCommerce plugin has the one-page menu, basket, search and Veg filter in a newer design. This plugin adds PPOM add-ons, opening hours, delivery day and time, display switches and colour settings. When this plugin is active, the free plugin stays idle.

Does it work with the WooCommerce Checkout block?

Yes. On the classic checkout customers choose a Delivery Day and then a Delivery Time. In the Checkout block there is a single drop-down, Delivery Day or Delivery Time, in the "Additional order information" step. Both are saved to the same order fields (_delivery_date and _delivery_time).

How are delivery time slots calculated?

The plugin looks at today and the next four days (five calendar days; you can change that with wxp_restaurant_delivery_days) and skips closed days. For each open day it builds slots from your opening hours and the Timeslot Interval. Today includes an ASAP option while you are open, and slots start at least one interval after the current time. Enter 0 as From or To to close a day.

Does the "next five days" mean five open days?

No. It means five calendar days, starting today. A closed day inside that window is skipped and not replaced, so you may be offered fewer than five days.

Does it stop orders when the restaurant is closed?

No. The Closed status and the closed switch change the message and hide the delivery fields. Customers can still order.

How do product add-ons work?

Install the free PPOM for WooCommerce plugin and attach a field group to simple products. Select, radio, checkbox, text and textarea fields appear in the menu popup, required fields are checked, and option prices (fixed or percentage) are added to the item price. Percentages are taken from the product's price.

Can I show only some categories?

Yes. [wxp_restaurant categories="starters,mains,drinks"] uses category slugs, and the sections appear in the order you list them. Without the attribute, all product categories with published products are shown.

Is it compatible with HPOS?

Yes. Order data is read and written through WooCommerce order objects, and compatibility with High-Performance Order Storage and the Cart and Checkout blocks is declared.

How do I activate my licence and get updates?

Your licence key is in the purchase email and in My Account → Downloads on wpexpertshub.com. In WordPress open Plugins → WpExperts Hub Licences, paste the key and click Activate licence. New versions then appear on the normal Dashboard → Updates screen.

Does the plugin work without a licence?

Yes. The code never checks the licence before running menu, settings or checkout features. Without a licence you do not get automatic updates.

How many sites can use one key?

That is decided by the licence server and your purchase, not by the plugin. The licence screen advises you to deactivate the licence before moving the plugin to another live site.

Why does a hidden or out-of-stock product still show up?

This plugin lists published products from your menu categories and does not read catalog visibility or "Hide out of stock items". Exclude products with the wxp_restaurant_menu_product_args filter.

Can customers change quantities in the basket?

No. They can remove a line, or change quantities on the WooCommerce cart page (the basket's Cart button).

Where is the delivery information shown?

On the thank-you page, in order emails, and on the order screen under the shipping address. See Where the delivery details go.

How do I get support?

Email support@wpexpertshub.com with your site details and your order ID.

11. Changelog

1.5 - 27/09/2026

  • Security - New licence client: licence data is exchanged as JSON only (the old client unserialized server responses) and a failed server request can no longer cause a fatal error.
  • Enhancement - One "Plugins → WpExperts Hub Licences" screen manages the licences of all WpExperts Hub plugins (previously only one plugin's licence page could be opened when several were installed). Existing activations are kept.
  • Enhancement - Updates are delivered through the standard WordPress update screen for sites with an active licence; "Email me my key" added.
  • Fix - Fatal error when the add-to-cart request contained a missing/invalid product ID.
  • Fix - "Is Non-Vegetarian" flag was reset to "no" whenever a product was saved outside the product editor (quick edit, REST API, imports).
  • Fix - Settings were saved on the front end hook without a capability check; now saved on admin_init for users with manage_woocommerce and every value is sanitised.
  • Fix - Delivery days that are set to 0 (closed) were still offered for future days, including a 12:00 am slot.
  • Fix - Delivery time could be submitted empty ("Select Delivery Day" had the value 0) and day/time were not checked against available slots.
  • Fix - Delivery day stored as a localised string ("Sun Sep 27th") could not be read back (1970 dates in non-English sites, wrong year around New Year). New orders store Y-m-d; old orders still display.
  • Fix - Timeslot interval 0 caused a division-by-zero fatal error.
  • Fix - PPOM options: fields assigned through several groups or categories were not shown, non-sequential fields broke the popup, percentage prices caused a TypeError, and required fields are now validated on the server.
  • Fix - Variable products with multi-word attributes or global attributes (term slugs) could not be added to the basket.
  • Fix - Add-to-cart failures (out of stock, expired nonce, network error) left the menu blocked; customers now see the error message.
  • Fix - Menu script relied on BlockUI being loaded by the theme; it is now a declared dependency (WooCommerce 10.3+ handle supported).
  • Fix - Removed writing a generated stylesheet and fonts into wp-content/uploads/rfw; colours are now printed inline.
  • Fix - PHP 8.2 dynamic property deprecations; escaping of all output; plain-text e-mails no longer receive HTML.
  • Fix - Mobile: sidebar basket overflowed on narrow screens.
  • New - Delivery day/time field for the Checkout block (Additional Checkout Fields API), saved to the same order meta as the classic checkout.
  • New - Shortcode attribute categories="slug1,slug2".
  • New - Out-of-stock items show "Sold out" instead of an add button.
  • New - Header mini cart is refreshed after adding items from the menu.
  • Tweak - AJAX requests use the WooCommerce AJAX endpoint (the old ?wxperts-ajax endpoint still works).
  • Tweak - WordPress colour picker replaces the bundled Spectrum library.
  • Tweak - Declared compatibility only for HPOS and Cart/Checkout blocks (removed incorrect product block editor, analytics, navigation and marketplace declarations); tested with WordPress 7.1 and WooCommerce 11.1.
  • Tweak - Plugin boots on plugins_loaded, so WooCommerce is detected when network-activated; the free version now stays idle whenever Pro is active.
  • Tweak - Text domain changed to restaurant-for-woocommerce-pro (matches the plugin folder).

1.4 - 20/02/2024

  • Minor tweaks.

1.3 - 08/02/2024

  • Menu scroll fixed.

1.2 - 24/01/2024

  • Compatibility Check.

1.1 - 18/02/2023

  • Compatibility Check.

1.0 - 04/05/2021

  • Initial release.

12. Support

Email support@wpexpertshub.com. Include:

  • Your order ID.
  • The plugin version (1.5 or the version you run), your WordPress version and your PHP version, and your WooCommerce version and theme.
  • The address of the menu page, and whether you use the classic checkout or the Checkout block.
  • What you expected, what happened, and what you already tried. A screenshot of the settings and of any message helps.