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
- You put your dishes in product categories, tick Is Non-Vegetarian where it applies, and add the shortcode to a page.
- In WooCommerce → Restaurant you set opening hours, switch on delivery day and time selection, choose what the menu shows, and pick colours.
- 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. - 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 functionwxp_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]andcategories="..."work the same way. The same WooCommerce → Restaurant menu item (slugwxp-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 inyour-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.
| Topic | Advanced Restaurant for WooCommerce 1.5 (this plugin) | Restaurant for WooCommerce 1.2 (free) |
|---|---|---|
| Adding a dish | An Add button; each click adds one | Add button that becomes a quantity stepper |
| Popup | Only for dishes with options (variations or PPOM fields) | For every dish: photo, description, variations |
| Variation stock in the popup | Not checked; WooCommerce rejects the add and the message is shown | Out-of-stock combinations are disabled |
| Basket | Side panel with a remove button per line | Drawer with quantity steppers, line totals and subtotal |
| Products hidden from the catalog | Listed | Not listed |
| "Hide out of stock items" (WooCommerce) | Not followed; out-of-stock dishes show "Sold out" | Followed |
| Order of dishes | Newest first (WooCommerce's default product query order) | WooCommerce sort order, then title |
| Child categories | Listed as sections of their own; a parent section also lists its children's dishes | Parent first, children indented; no repeats |
| External and grouped products | Shown 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 page | Shows "Your session has expired" until the page is reloaded | Gets a fresh token and retries |
| Scripts | jQuery, fancyBox and WooCommerce's BlockUI | One script, no jQuery |
| Add-to-cart checks by other plugins | woocommerce_add_to_cart_validation is not run (see What WooCommerce still checks) | Run |
2. Requirements
| Item | Requirement | Notes |
|---|---|---|
| WordPress | 6.3 or later | Tested up to 7.1. From the plugin header. |
| PHP | 7.4 or later | From the plugin header. |
| WooCommerce | 8.0 or later | Tested 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 field | WooCommerce 8.9 or later recommended | The 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 WooCommerce | Optional | Needed 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 load | jQuery and WooCommerce's BlockUI | The 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 blocks | Declared compatible | The plugin declares compatibility with custom_order_tables and cart_checkout_blocks. Order data is written through WooCommerce order objects. |
| Site timezone | Set it correctly | Opening hours, the status badge and delivery slots use the timezone in Settings → General (wp_timezone()). |
| Outgoing connections | wpexpertshub.com over HTTPS | Only for the licence and update client (see Privacy). The menu itself makes no external request. |
| Licence | Optional for use | The plugin's features work without an active licence. The licence unlocks automatic updates. |
3. Installation
- Make sure WooCommerce is installed and active.
- 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.
- 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.
- Open Plugins → WpExperts Hub Licences, paste your licence key and click Activate licence (see The licence and updates).
- 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
- Find your licence key in the purchase email. The screen says it is also shown in My Account → Downloads on wpexpertshub.com.
- 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.
- 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
- Check that Settings → General has the right timezone.
- Put your dishes in product categories. Tick Is Non-Vegetarian on meat and fish dishes.
- Create a page with the shortcode
[wxp_restaurant](addcategories="starters,mains"to pick and order sections) and publish it. Use the shortcode once per page. - Open WooCommerce → Restaurant → Menu Settings. Enter From and To times for each day (24-hour, for example
10:00and22:00;0closes a day) and click Save Changes. - 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.
- 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.
- Optional: install PPOM for WooCommerce and attach a field group to a simple product to offer add-ons.
5. Features
The shortcode and which dishes it lists
| Shortcode | Result |
|---|---|
[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."
The menu page
- 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_nonvegasyesorno. - 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_nonvegwith the valueyes.
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 type | How it appears | Checks in the browser |
|---|---|---|
| select | Drop-down with "Select Option". The option named in the field's selected setting is preselected. | Required: a value must be chosen ("Please select option."). |
| radio | Radio buttons. | Required: one must be ticked. |
| checkbox | Tick boxes. | Required: at least one. The field's minimum and maximum ticked options are enforced ("Minimum N options required.", "Maximum N options allowed."). |
| text | Single-line box. | Required: not empty ("This field is required."). |
| textarea | Multi-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=onin 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:MMorHH:MM(also with a dot:10.30), with hours 0 to 23 and minutes 0 to 59, or exactly24:00. It is saved asHH:MM. Anything else is saved as empty, which closes the day. - Closed days. Enter
0in a box, or leave a box empty, to close that day. With either box empty or0, 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:
- 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.
- Close Message. Shown only when Restaurant is closed is ticked and the message is not empty.
- 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 (idwxp-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.
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, asY-m-dfor 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 example6:30 pm), orASAP. 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_metaaction (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 dispatchwc-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
| Setting | Key | Default | What it does |
|---|---|---|---|
| Display Header Message | notice | Off | Check to display the header message on the menu page. |
| Header Message | g-notice | Empty | Text 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 closed | is-closed | Off | Shows 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 Message | close-notice | Empty | Text shown in the header while Restaurant is closed is on. Basic HTML is kept. |
| Opening Hours: From / To, Monday to Sunday | days (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
| Setting | Key | Default | What it does |
|---|---|---|---|
| Disable Cart Sidebar | disable-sidebar | Off | Check 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 Filter | disable-veg-filter | Off | Check to remove the Veg Only and Non Veg Only tick boxes. The diet marks on dishes stay. |
| Disable Searchbar | disable-searchbar | Off | Check to remove the search box from the menu page. |
Checkout Fields
| Setting | Key | Default | What it does |
|---|---|---|---|
| Display Date Selection | date | Off | Check 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 Selection | time | Off | Check 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 Interval | time-int | 15 | Minutes 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.
| Setting | Key | Default | What it does |
|---|---|---|---|
| Primary Color | primary-color | #b0d9f9 | "Primary color used on menu page, like category sidebar background etc." |
| Primary Font Color | primary-font-color | #43454b | "Primary font color used on menu page." |
| Link Color | link-color | #ff0000 | "Link and icon colour used on the menu page and popup." |
| Sidebar Cart Icon Color | cart-icon-color | #b0d9f9 | "Cart icon color." |
| Sidebar Qty Count Color | qty-color | #ff0000 | "Cart Qty Count color in sidebar." |
| Qty Button Background | qty-btn-color | #b0d9f9 | "Qty button background color in popup." |
| Button Color | btn-color | #b0d9f9 | "Add to cart button color in popup." Also the basket's Checkout button. |
| Button Text Color | btn-txt-color | #3f3f3f | "Add to cart button text color in popup." Also the Checkout button's text. |
| Header Message Text | h-text-color | #43454b | "Header message text color on menu page." |
| Header Message Background | h-bg-color | #bce8f1 | "Header message background color on menu page." |
| Close Message Text | c-text-color | #ffffff | "Restaurant close message text color on menu page." |
| Close Message Background | c-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
| Attribute | Default | Meaning |
|---|---|---|
categories | empty | Comma-separated product category slugs. Order is kept. |
menu | lunch | Passed to wxp_restaurant_menu_category_args only. |
Filters and actions the plugin fires
| Hook | Parameters | When |
|---|---|---|
Filter wxp_restaurant_menu_product_args | $args, $slug, $atts | Before 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, $atts | Before 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_item | none | Run 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).
| Endpoint | POST parameters | What it does |
|---|---|---|
wxp_add_to_cart | data: 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_item | id (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.phpreceives$items(category slug to products) and$cats(slug tonameandcount). It is loaded withwc_get_template()using WooCommerce's default template folder, so WooCommerce looks foryour-theme/woocommerce/menu.phpand thenyour-theme/menu.phpbefore 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.phpis included directly by thewxp_menu_headercallback and cannot be overridden. Remove the callback or add your own with the same action.templates/dashboard.phpis 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 (classWooExperts_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 serverWPXH_LICENCE_SERVER(optional, see Privacy). - Checkout block field ids:
wxp-restaurant/delivery-dayandwxp-restaurant/delivery-slot(class constantsBLOCK_DAY_FIELDandBLOCK_SLOT_FIELD).
WordPress and WooCommerce hooks the plugin uses
| Hook | Priority | Purpose |
|---|---|---|
plugins_loaded | 5 | Starts the plugin if WooCommerce is present. |
before_woocommerce_init | 10 | Declares HPOS and Cart/Checkout blocks compatibility. |
init | 10 (0 for the legacy endpoint) | Registers the shortcode, the product tick box, the licence client, and the other hooks of this table. |
admin_menu | 10 | Adds WooCommerce → Restaurant (and the licence client adds Plugins → WpExperts Hub Licences). |
admin_init | 10 | Saves the settings form. |
admin_enqueue_scripts | 999 | Loads the colour picker, admin style and script on the settings screen only. |
wp_enqueue_scripts | 10 | Registers the scripts and styles; enqueues the checkout script on checkout. |
plugin_action_links_<plugin> | 10 | Adds the Settings link first in the plugin row. |
template_redirect | 0 | The legacy ?wxperts-ajax= endpoint. |
wc_ajax_wxp_add_to_cart, wc_ajax_wxp_remove_item | 10 | The AJAX handlers. |
product_type_options, woocommerce_admin_process_product_object | 10 | The Is Non-Vegetarian tick box and its saving. |
woocommerce_get_item_data | 10 | Lists 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_totals | 999 / 20 | Adds the option prices to the cart item price. |
woocommerce_checkout_create_order_line_item | 10 | Saves PPOM options on the order line. |
woocommerce_after_order_notes | 10 | Prints the classic delivery fields. |
woocommerce_checkout_process | 10 | Validates the classic delivery fields. |
woocommerce_checkout_create_order | 10 | Saves the classic delivery fields to the order. |
woocommerce_init | 10 | Registers the Checkout block field (or registers it at once if that hook already ran). |
woocommerce_set_additional_field_value | 10 | Copies the block field value into _delivery_date and _delivery_time. |
woocommerce_filter_fields_for_order_confirmation | 10 | Hides 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_fields | 20 | Removes the block field from the editable fields on the order screen. |
woocommerce_thankyou | 1 | Prints the delivery details on the thank-you page. |
woocommerce_email_order_meta | 1 | Prints the delivery details in emails. |
woocommerce_admin_order_data_after_shipping_address | 1 | Prints 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
| Item | Where | Content |
|---|---|---|
_wxp_restaurant_opts | Option (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_key | Option (autoload off) | The licence key. |
_restaurant-for-woocommerce-pro_key_status | Option (autoload off) | active, or inactive when the server reports the licence is no longer active here. Deleted on deactivation. |
wpxh_licence_check_v2 | Site transient | Cached 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 seconds | The message shown after a licence form action. |
_is_nonveg | Product meta | yes or no. |
_delivery_date, _delivery_time | Order meta | Delivery day (Y-m-d) and time (text, or ASAP). |
_wc_other/wxp-restaurant/delivery-slot or .../delivery-day | Order meta (written by WooCommerce) | The raw Checkout block value (Y-m-d|time or Y-m-d). |
| PPOM option entries | Order item meta | Key: the PPOM field title. Value: the chosen options, comma separated, with "(+price)" after options that cost something. |
wxp-menu, wxp-price | Cart 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
| Topic | What the plugin does |
|---|---|
| Data stored by the plugin | Your 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. |
| Cookies | The 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 screens | Nothing leaves your site. Requests go to your own server. The plugin loads no external font, script or image. |
| Data sent to wpexpertshub.com | Only 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. |
| Retention | The 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 deletion | Neither removes anything. The settings, licence options, product meta and order meta stay. "Deactivate licence" removes the two licence options. |
| Privacy policy text, exporter, eraser | The plugin adds none. WooCommerce's own export and erase tools cover order data it handles. |
What the licence client sends
| When | Request | Data sent |
|---|---|---|
| You click Activate licence | activate | The plugin slug (restaurant-for-woocommerce-pro), the licence key you entered, and your site URL (site_url()). |
| You click Deactivate licence | deactivate | Slug, the stored key, and the site URL. |
| You click Send licence key | send-key | Slug 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 plugin | check | The 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
| Problem | Likely 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.