WooCommerce integration

Templates

Two templates draw the shop part. Copy one into your theme to change it (a child theme is looked
at first); the plugin’s own is used when there is no copy.

TemplateTheme pathUsed for
picker.phpyourtheme/baza-booking-calendar/woocommerce/picker.phpthe date/time picker on a booking product’s page
cart-summary.phpyourtheme/baza-booking-calendar/woocommerce/cart-summary.phpwhat is booked, under a line of the classic cart and checkout

Each template receives its data as $args (documented at the top of the file). The path can also be
changed with apply_filters( 'baza_bc_woocommerce_template', $file, $name, $args ).

The picker’s skeleton

The script (modules/woocommerce/assets/woo-picker.js) looks up its places by data-bbc-role and
stops with a console error when one is missing. Required roles:

prev, next, title, grid, slots, extras, total, book, note

Tags, classes, order and anything you add around them are yours. The rows the script draws inside
the roles carry fixed classes you can style: bbc-woo-picker__day (with .is-picked),
__slot (.is-picked), __time, __price, __extra, __extras-toggle, __qty, __step,
__veil (the loading overlay, blockUI blockOverlay).

CSS variables on .bbc-woo-picker (the script sets the first four from the theme’s own
Add to cart button): --bbc-woo-accent, --bbc-woo-on-accent, --bbc-woo-radius,
--bbc-woo-ink, --bbc-woo-gap.

The block cart

The Cart and Checkout blocks (and the mini cart) flatten a line’s details to plain text, so the
summary template is not used there: a line gets one row per day and per extra instead. Change those
rows with the baza_bc_woocommerce_item_rows filter.

Filters and actions

HookPurpose
baza_bc_woocommerce_template (filter)Path of a template: ( $file, $name, $args ).
baza_bc_woocommerce_summary_data (filter)What the cart summary is made of: ( array $data { days, extras }, array $input ).
baza_bc_woocommerce_item_rows (filter)Plain rows of a line (block cart, mini cart, order line): ( array $rows { key, value }, array $input ).
baza_bc_woocommerce_product_dates (action)Fired on a booking product’s page where the picker is drawn; ( WC_Product $product ). Draw your own picker here instead (remove the plugin’s with remove_action( ..., [ 'BazaBookingCalendar\BBC_Woo_Cart', 'render_calendar' ] )).
baza_bc_woocommerce_offline_methods (filter)Gateway ids that mean “pays later”: default bacs, cheque, cod.
baza_bc_woocommerce_paid_on_completion (filter)Gateway ids whose order is paid only when completed (processing is not money): default cod.
baza_bc_woocommerce_send_shop_emails (filter)true to send the shop’s own customer emails for booking orders (off by default).
baza_bc_price_journal_recorded (action)A price change was written: ( $booking_id, array $entry ).
baza_bc_internal_booking_meta_prefixes (filter)Meta key prefixes hidden from the booking panel, letters and calendar events (default bbc_, woo_; keys starting with _ are always hidden).

What happens, in code

  1. Product page. BBC_Woo_Cart::render_calendar() enqueues the picker and prints views/picker.php. The picker asks the server (public admin-ajax.php actions, no nonce – read-only except the last, which is what a shop’s own add-to-cart link does):
  • bbc_woo_days (product, month = YYYY-MM) – days with something free;
  • bbc_woo_slots (product, date) – that day’s free hours with prices;
  • bbc_woo_extras (product) – the additional services of the service;
  • bbc_woo_quote (product, slots[], extras[id]=qty) – the total;
  • bbc_woo_add (same) – into the cart.
  1. Pricing. Always the server’s: BBC_Booking_Create::create( $input, [ 'quote' => true ] ) runs the checks and BBC_Booking_Calculate, and makes nothing. Only slot strings, extra ids and counts are read from the browser.
  2. Cart line. WC()->cart->add_to_cart() with line data bbc_booking = { input, total }. woocommerce_before_calculate_totals sets the line’s price (regular price too, no sale; tax status none – the amount is final). woocommerce_check_cart_items asks again at the cart and checkout; a taken time is an error notice, a moved price updates the line.
  3. Order. woocommerce_checkout_create_order keeps the forms on the order (meta _bbc_inputs, not on the line). woocommerce_checkout_order_processed / woocommerce_store_api_checkout_order_processed call BBC_Woo_Cart::create_bookings(): one booking per line through BBC_Booking_Create::create( $form, [ 'woo_order' => true ] ) (no gateway, no payment link), pending and holding its time. Order meta _bbc_booking_ids; booking meta woo_order_id. A line that cannot be made throws, which the checkout shows.
  4. Following the order. BBC_WooCommerce::order_status_changed(): paid -> booking booked (source woocommerce_webhook); on-hold or an offline method still unpaid -> stays pending; cancelled/failed -> pending bookings rejected; refunded -> booked ones rejected. A booking rejected in the plugin cancels an unpaid order when none of its bookings is left.

Booking meta written by the shop part

All hidden from the panel by the woo_ prefix.

woo_order_id, woo_payment_method (gateway id), woo_payment_title, woo_payment_state (paid, waiting, cancelled, failed, refunded), woo_payment_offline (1 for an offline method), woo_waiting_payment (time it went on hold). A WooCommerce coupon also sets promo_sale and coupon_code.

Price journal

BBC_Price_Journal::record( $booking_id, $kind, $reason, $before, $after, $by = [] ) writes one entry (booking meta bbc_price_journal, and the activity log’s “Price changed” row). $by is [ 'type' => admin|client|shop|system, 'user' => id, 'label' => text ]; omitted, it is whoever is signed in, else the system. BBC_Price_Journal::entries( $id ) reads them.

Licence and compatibility

  • Feature key woocommerce (in every plan) opens the gateway, product type, cart and import. Order status sync is not behind it.
  • HPOS and the Cart/Checkout blocks are declared compatible.
  • Import from WooCommerce Bookings: BBC_Woo_Import (WooCommerce -> Import bookings). Idempotent through meta _bbc_imported_from_wc.

Internals (moved from the user article)

A booking is made by the plugin (slots, extras, discounts, taxes) and paid as a WooCommerce order.
In every plan. Settings > Payment gateway > WooCommerce (no keys: the payment methods are the shop’s).

How it runs

  1. The form makes the booking (pending, slot held). baza_bc_woocommerce_pay() (includes/payments/woocommerce.php)
    asks BBC_WooCommerce::create_payment() (modules/woocommerce/class-bbc-woocommerce.php) for an order.
  2. The order has one fee line for the amount the booking decided (baza_bc_gateway_charge()), no shop tax on it,
    meta _bbc_booking_id / _bbc_order_id. The booking keeps woo_order_id. The client is sent to the order’s payment page.
  3. The module follows the order:
OrderBooking
processing, completedbooked, payment recorded (source woocommerce_webhook)
on-hold (bank transfer, cash)stays pending, payment_url removed so the payment-window cron leaves it alone
cancelled, failedpending -> rejected
refundedbooked -> rejected
booking rejected / expiredan unpaid order is cancelled

After paying the client returns to the plugin’s own success page (merc_id), which asks the module whether the order
is paid (baza_bc_payment_verify_return) before it believes it.

Settings of the integration

  • Shop currency must equal the plugin’s currency, or the gateway answers with an error.
  • Balance top-ups cannot be paid this way (the gateway says so).
  • The shop’s own letters to the client for these orders are off (baza_bc_woocommerce_send_shop_emails turns them on).
  • HPOS and the cart/checkout blocks are declared compatible.

The product type “Baza booking” (stage 1 of the direct integration)

A WooCommerce product of the type Baza booking sells one service of the plugin. In the product editor: type
“Baza booking”, tab Booking, field Service. The price (“From X” in the catalogue), the free times and the
extras come from the service. The catalogue button says “Select date” and opens the product page. On the page the
dates will be chosen (next stage: action baza_bc_woocommerce_product_dates); for now an editor sees a hint when
no service is chosen.
Files: modules/woocommerce/class-bbc-woo-product.php (the product class, read once WooCommerce has its own),
class-bbc-woo-product-setup.php (selector, tab, field, price text, product page hook).

Next stages: the date picker on the product page and the cart line (stage 2), the cart/checkout and the booking made
from the order (stage 3), import from Woo Bookings (stage 4).

Not done yet (older notes)

  • Import from Woo Bookings (products, resources, availability rules, bookings with their orders), preview and repeatable.
  • Partial payment (deposit) as the order amount; money returned by a Woo refund into the plugin’s accounts.
  • The booking panel showing the order and its link (the order screen already shows the booking).
  • Run against a real WooCommerce: the flows were exercised against a minimal stand-in for WC_Order only.

Stages 2-4 (done, needs a test on a real shop)

Product page. A product of type Baza booking shows its own compact picker
(modules/woocommerce/assets/woo-picker.*), not the plugin’s week calendar: a month to choose the
day in, that day’s free hours with their prices under it, the total, and Book now. The free days
and hours come from the same functions the client’s account uses (baza_bc_service_open_days(),
baza_bc_service_free_slots(), baza_bc_service_slot_prices()), the total from
BBC_Booking_Create (context quote: asks whether the time is free and what it costs, makes
nothing). Book now adds one cart line carrying the form and the plugin’s price
(BBC_Woo_Cart::add_from_form()). Not in the picker yet: form-builder fields,
promo codes, whole-day services. Additional services are in it: the same list the plugin’s own form offers for the service, with quantities and the server pricing them.

Cart / checkout. The line’s price is the plugin’s (tax of the shop’s off: the amount is final),
no quantity box, dates shown under the name. The time is asked again at the cart and the checkout
(woocommerce_check_cart_items); a price that moved is updated and the client told. The checkout’s
empty billing fields are filled from the form.

Order. When the order is created, one booking per booking line is made (BBC_Booking_Create,
context woo_order: no gateway, no link), pending and holding its time, linked both ways
(_bbc_booking_ids on the order, woo_order_id on the booking). An order status moves all of its
bookings: processing/completed = booked; on-hold stays pending; cancelled/failed rejects pending
ones; refunded rejects booked ones. A booking rejected by the office cancels the order when none of
its bookings is left.

Import from Woo Bookings. WooCommerce -> Import bookings: preview, then import in batches.
Products -> services (name, text, picture, price), resources -> services under them, bookings ->
bookings (days, client, price, order link). Idempotent (_bbc_imported_from_wc), nothing of Woo
Bookings is changed. Optionally switches the imported products to the Baza booking type.

Not carried over: availability rules and person types (set the service’s hours in the plugin),
bookings in in-cart state. Imported bookings are linked to their order for reference only; the
order’s later status changes do not move them.

Paying later (transfer, cheque, cash)

Offline methods (bacs, cheque, cod; filter baza_bc_woocommerce_offline_methods) leave the
booking pending until the money is in. Bank transfer and cheque: on-hold, paid when the shop marks
the order processing/completed. Cash on delivery goes to processing at once, which is not money:
for methods in baza_bc_woocommerce_paid_on_completion (default cod) the booking is confirmed
only when the order is completed. The bookings list shows a badge with the method and the state
(“Pay later, Cash on delivery”, “Paid, Card”, …), kept in the booking’s meta
(woo_payment_method, woo_payment_title, woo_payment_state, woo_payment_offline).

Price journal and shop coupons

Every change of a booking’s price is written by BBC_Price_Journal::record() (booking meta
bbc_price_journal, and the activity log’s “Price changed” row with the reason in its text): what
sort of change, why, the price before and after, and who – an administrator, the client, the shop
or the system. Written today: the discounts given when a booking is made (volume, loyalty,
personal, coupon), a price edited by hand in the booking panel, and a WooCommerce coupon.

Shop coupons are allowed. The coupon takes the line below the plugin’s figure; when the order is
made the difference is written into the booking as a discount (promo_sale, coupon_code), the
amount expected becomes what the order really charges, and the journal says “WooCommerce coupon
CODE: 230.00 -> 207.00 (client, WooCommerce order #N)”. The journal is for the office; the client
is not shown it.

Licence

Feature key woocommerce (licence server lookup key woocommerce, in every plan). It opens the
gateway in the list, the product type, the cart and the import. Following the orders already made
(status sync) is not behind it, so a lapsed licence never leaves a paid order unconfirmed.

Last Modified: 05.10.2026