Integrate Product Add-Ons
Use this reference for theme overrides, storage adapters, translations, POM Composite, Bookings, Subscriptions, country pricing, payment buttons, and POM-specific PHP extension points.
Override storefront templates safely
Classic forms and WooCommerce's Add to Cart with Options Block use the same PHP templates. Copy only the required template to the active child theme while preserving its relative path below:
your-child-theme/woocommerce-product-addons/pom/
For example:
your-child-theme/woocommerce-product-addons/pom/addons/checkbox.php
The dedicated pom directory prevents an older official-extension override from being loaded with POM's UUID request format. Child-theme files take precedence over parent-theme files; the bundled POM file is the fallback.
Available templates are product-add-ons.php, group.php, field-start.php, field-end.php, and these files below addons/: checkbox.php, radiobutton.php, image.php, select.php, choice-list.php, custom-text.php, custom-textarea.php, file-upload.php, custom-price.php, input-multiplier.php, and datepicker.php.
Compare a copied template's header version and declared variables after every POM Tweaks update. Preserve:
- UUID-backed input
nameandvalueattributes andpom_pao_nonce; .pom-pao-priced-control, field data attributes, and theform-controlclass;- associated labels, descriptions,
aria-describedby, and the field error target; - the signed edit context and file/date reset contracts;
- context-appropriate escaping:
esc_html()for text,esc_attr()for attributes,esc_url()for URLs, andPOM_Tweaks_Product_Addons_Schema::sanitize_description()for description HTML, andwp_kses_post()for approved price-label HTML. Description overrides must preserve the restricted formatting policy described in Format descriptions and price breakdowns.
Template variables are raw presentation data, not trusted markup. $product is a WC_Product; $frontend_model, $context, $field, $addon, and $options contain resolved data; $current_value and defaults contain restored or nonce-verified values. Never print an object or raw array, construct an arbitrary nested template path, or place a previous file URL into a file input.
Add markup around rendered sections
POM supplies stable actions around each render boundary:
| Action pair | Arguments |
|---|---|
pom_tweaks_product_addons_before_container, pom_tweaks_product_addons_after_container |
Frontend model, WC_Product |
pom_tweaks_product_addons_before_group, pom_tweaks_product_addons_after_group |
Context, product, frontend model |
pom_tweaks_product_addons_before_field, pom_tweaks_product_addons_after_field |
Official-format field, context, product |
pom_tweaks_product_addons_before_options, pom_tweaks_product_addons_after_options |
Official-format field, context, product |
pom_tweaks_product_addons_before_selection_summary, pom_tweaks_product_addons_after_selection_summary |
Frontend model |
These actions belong to the loader, so overriding one control template does not remove surrounding extension points. POM also emits the supported official actions documented in Official Product Add-Ons compatibility.
Integrate private file storage
POM stores local uploads outside the public document root. An object-storage adapter must implement upload, authorized download, health, and deletion as one lifecycle:
| Filter | Arguments after the initial value | Accepted result |
|---|---|---|
pom_tweaks_product_addons_store_file |
PHP upload array, validated context | Driver and opaque storage key, WP_Error, or null for local storage |
pom_tweaks_product_addons_download_file |
Private file record | Short-lived authorized redirect URL or null for local streaming |
pom_tweaks_product_addons_delete_file |
Private file record | true after confirmed deletion, false on failure, or null for local deletion |
pom_tweaks_product_addons_private_upload_directory |
Default directory, current blog ID | Absolute writable directory outside the public document root |
pom_tweaks_product_addons_file_storage_health |
Current blog ID and upload state | true for a ready adapter, false when unavailable, or null for local diagnosis |
Do not log temporary paths, private records, filenames, customer values, or signed URLs. Download URLs must be short-lived and object-scoped; POM checks cart, order, guest key, or management authorization before asking the adapter for one. Keep database and object-store backups at the same recovery point and test deletion, restore, privacy erasure, and multisite separation.
pom_tweaks_product_addons_file_ttl adjusts temporary and cart retention between one hour and 30 days. pom_tweaks_product_addons_download_url_ttl adjusts generated-link lifetime up to 30 days. pom_tweaks_product_addons_erase_order_files may retain files during personal-data erasure only when the store has a documented lawful retention requirement.
Open Tools → Site Health → POM Product Add-Ons to verify upload storage. The panel exposes readiness without revealing filesystem paths or object keys.
Use Product Add-Ons inside POM Composite
When both modern tweaks are enabled, a selected POM Composite component can expose the current Product Add-On fields for its product or variation. The builder submits UUID intent and the server resolves the complete definition before quoting and adding the configuration.
The composite owns the final parent and component line prices. Product Add-Ons supplies a validated snapshot and surcharge for the real component; the composite includes that amount once and prevents the normal Product Add-Ons price adjustment from charging it again.
File uploads are not supported inside the composite JSON request. Bookings person and block multipliers are also unavailable there because authoritative booking values are absent from the quote request. A directly configured component with either requirement cannot be saved; a dynamically discovered component is unavailable and rejected by quote and cart services.
PHP container integrations can inspect pom_tweaks_product_addons_component_contract and call pom_tweaks_product_addons_prepare_component. Preserve the returned snapshot and server total. Remote clients should use the POM Composite quote endpoint instead of calling PHP hooks.
For the complete builder, quote, cart, and order workflow, see Build POM Composite products.
Bookings, Subscriptions, and price integrations
- WooCommerce Bookings person and block/night multipliers apply only when their field settings are enabled. Flat fees remain one-time line charges.
- WooCommerce Subscriptions uses the saved charge policy for initial, switch, recurring, and renewal phases. Historical renewals retain their accepted snapshot and price evidence.
- All Products for WooCommerce Subscriptions uses its selected plan and
wcsatt_discount_addonsdecision. Protected add-on amounts remain outside a plan override where the official contract requires it. - Name Your Price becomes the authoritative product base before Product Add-On adjustments, including percentages.
pom_tweaks_product_addons_resolved_ratecan adapt a verified server rate for a currency or dynamic-pricing integration. The browser preview needs a matching client integration; a browser-supplied rate is never authoritative.pom_tweaks_product_addons_charge_context,pom_tweaks_product_addons_order_charge_context, andpom_tweaks_product_addons_coupon_discount_amountadapt their documented bounded server decisions.
When Country pricing zones is active, fixed rates, tier rates, reusable-choice prices, and customer-defined price limits can have zone values. Percentage rates remain global and use the current regional product or booking base. Public responses expose only the applied zone and revisions.
Keep Price Based on Country Pro inactive after POM takes ownership. Review the country-pricing Site Health checks before cutover; POM fails closed when two providers could convert the same add-on.
For customer country changes, Store API evidence, renewals, and end-to-end verification, see Buy with country-zone prices and Check country-zone health and compatibility.
Payment and purchase-flow behavior
Stripe, PayPal Payments, WooPayments, and WooPay product-page express buttons are hidden when the product requires its configurable form. Payment methods remain available in Cart and Checkout. Archive AJAX add-to-cart is replaced by a product-page link for configurable products.
Repeat orders use current catalog rules unless a subscription renewal explicitly retains its historical snapshot. See Cart and order persistence for cart editing, order editing, refunds, renewals, and Order again.
Create a signed direct-purchase link
Use a signed link when a customer should receive a merchant-selected product, add-on text, and amount from a website, email, or document. POM Product Add-Ons must be enabled and the official extension inactive. The product and its add-on configuration must already be published and purchasable.
Create the link with WP-CLI on the destination store:
wp pomtweaks product-add-ons purchase-link --url=https://example.test --product=124 --values='{"123-0":"Custom project","123-1":"150"}'
Here 124 is a concrete variation of product 123. For a simple product, use its own ID. Keys are field UUIDs or exact compatibility field names; an optional addon- prefix is accepted. Select and checkbox values use option UUIDs. Numeric values follow the field's configured price method: an input multiplier of 150 at a rate of 1 adds 150 in store currency. It does not automatically split that amount across subscription payments.
The command prints one complete URL. Copy it unchanged into the external site's link. It opens checkout after adding the selected product and leaves other cart items in place. The buyer needs neither a prior cart session nor an administrator login. The signature travels in the URL and does not depend on the referring website. Ordinary configurable forms still require their normal nonce.
--quantity=1is the default; only positive integers are accepted.--expires=<timestamp>accepts a future UTC Unix timestamp. Omitting it, or passing0, creates no time-based expiry.- Links are reusable. Anyone holding one may use it; they are not customer-specific or single-use.
- Editing the signed product, values, quantity, or expiry invalidates the link. POM also revalidates current catalog rules and rejects a changed quoted price, selected configuration, currency, tax class, or subscription terms at entry. Generate a new link after such changes.
- The added configuration has no cart edit action. Normal checkout taxes, shipping, discounts, stock checks, and payment rules still apply.
- The URL is signed, not encrypted. Do not include sensitive customer information in field values.
- Links belong to the issuing site's signing keys. Rotating the WordPress authentication salt invalidates existing links; there is no individual-link revocation command.
- Exclude requests containing
pom_pao_buyfrom page and CDN caches, including caches that otherwise ignore query parameters. POM marks handled responses as non-cacheable once WordPress loads.
Trusted PHP integrations may call POM_Tweaks_Product_Addons_Purchase_Links::create($product_or_variation_id, $values, $quantity, $expires). It returns the URL or WP_Error. Keep issuance behind the integration's own authorization and resolve amounts on the server; never expose an unsigned public endpoint that signs arbitrary posted prices. Uploads and booking-dependent fields are unsupported by this link format.
To verify a link, open it in a fresh browser session from an external page. Confirm the variation, text, amount, and subscription schedule in checkout. Alter one token character and confirm the request is rejected without adding a line.
Supply a trusted proposal base price
The PHP filter pom_tweaks_product_addons_base_price receives the catalog fallback and trusted cart-item data before native add-on pricing. Return a non-negative numeric base when a server-owned quote replaces the catalog base or includes an item free of charge. The resolved base becomes part of the native cart and order snapshot. Booking, Name Your Price, and container-specific base rules retain their own precedence.
Only use this filter with data prepared by an authorized server integration. Do not copy a browser-supplied base or discount flag into cart data. Normal price adjustments should continue to use the field definitions or pom_tweaks_product_addons_resolved_rate.
Translate customer-facing strings
Groups and reusable choices remain one shared structural configuration. POM registers only customer-facing group names, field names, descriptions, placeholders, local option labels, and reusable-choice names and descriptions.
- With POM Translate, go to Translations → Other translations → Defaults.
- With Polylang, go to Languages → Translations and use the POM Product Add-Ons group.
Each string identity contains the entity UUID and semantic field. Two records may therefore share the same source text and still have different translations. Editing source text keeps translations attached to that semantic identity; cloning creates new UUIDs and new translation entries.
POM Translate takes precedence when both providers contain a value; the identity-specific Polylang value is the fallback. Prices, assignments, availability, and UUIDs do not change between languages.
With Polylang, the default-language product and terms are the canonical structural source. Translations inherit direct and taxonomy assignments, product-specific fields, and exclusions unless the translated product has an explicit complete override in Product data → Add-Ons.
Maintain assignment discovery
The site-scoped assignment index discovers candidate groups without scanning every group on each product request. Full include, exclude, AND, and OR rules still run for those candidates.
Inspect it:
wp pomtweaks product-add-ons assignment-index --url=https://example.test
Rebuild the derived copy when the report says it is invalid:
wp pomtweaks product-add-ons assignment-index --url=https://example.test --rebuild
The command does not edit groups, product assignments, terms, or reusable choices. On multisite, run it separately for each intended --url or --blog_id.
Verify an integration
- Override one child-theme control, confirm classic and Block forms use it, then remove the file and confirm the bundled fallback returns.
- Register one POM render action and confirm it runs once around the intended boundary.
- Upload, download, delete, restore, and erase a private file through the complete adapter lifecycle.
- Configure a Composite component with a priced add-on and confirm the surcharge is included once.
- Compare a Bookings product, a subscription initial cart, and a renewal using their intended multiplier and charge policies.
- Switch language and confirm labels change while UUIDs, assignments, and prices remain stable.
- Switch country zone and confirm fixed amounts change while percentage rates use the regional product base.
- Run the assignment-index inspection twice and confirm its checksum stays stable when assignments do not change.
Troubleshooting
- Ignored theme override: confirm the file is under
woocommerce-product-addons/pom/, the child theme is active, and the official extension is inactive. - Duplicate surcharge in a container: use the component preparation contract and preserve its returned snapshot instead of running normal line pricing twice.
- Untranslated text: confirm the string is registered under the correct identity and the request uses the intended language.
- File adapter health failure: keep uploads unavailable until upload, authorized download, and confirmed deletion all work.
- Stale assignment result: inspect the derived index and rebuild it only if validation reports it invalid.
Related: Storefront fields and pricing, Health and compatibility checks, and Official Product Add-Ons compatibility.