Skip to main content

Sales Funnels & Upsells

Overview​

The Upsells module offers extra products at four points in the journey: a bump on the checkout order review, a post-purchase offer on the thank-you page, a "frequently bought together" block on the product page, and a cross-sell block under the cart table.

Each offer records its own impressions, conversions and revenue, and two offers can be run against each other as an A/B test that a daily job settles once enough traffic has been seen.

It is for stores trying to raise average order value without a separate funnel plugin. Offers are stored in the plugin's own table rather than as WooCommerce products.

Availability​

ItemValue
Module keyupsells
TierPremium
Entitlement keyupsells
Admin tabupsells, under Pricing & Promotions
Enabled optionaiowc_module_enabled_upsells (off until turned on)
REST namespaceaiowc/v1

Enabling the module creates the three tables below, seeds the defaults and schedules both jobs through Action Scheduler. Disabling it cancels them.

Settings​

Stored in the bundled option row aiowc_up_settings; legacy per-key options aiowc_up_<key> are migrated on first read, which registerHooks() triggers before any setting is read.

Stored keyDefaultMeaning
enable_order_bumpstrueRegister the checkout order-bump handler.
enable_post_purchasetrueRegister the thank-you-page handler.
enable_fbttrueRegister the frequently-bought-together handler.
fbt_algorithmmanualRecorded and returned by the settings route; suggestions come from matching offers.
default_discount_typepercentageThe discount type a new offer starts with.
ab_test_min_sample100Impressions a variant needs before an A/B test may be settled.

The three enable_* settings are read once, when hooks are registered: turning one off means that handler is never constructed on the request. The cart cross-sell handler has no switch and is always registered.

Offer types​

TypeWhere it appears
order_bumpCheckout, before the submit button.
post_purchaseThe thank-you page, after the order is placed.
fbtThe single product page, under the summary.
cross_sellUnder the cart table.

Each offer carries trigger product ids, offer product ids, a discount type (percentage, fixed or free) and value, a priority, a display position, a conditions JSON blob and a status. Impressions, conversions and revenue accumulate on the offer row.

A/B tests​

A test pairs two offer ids as variant A and variant B with a traffic split, defaulting to 50. AbTestEvaluationJob runs daily and settles a test once each variant has at least ab_test_min_sample impressions, writing the winner id and the completion time onto the test row.

Admin screen​

The Upsells & Order Bumps tab shows three summary tiles and a table of offers, and calls GET /upsells/offers.

Two things about it are worth knowing before relying on it:

  • The offer table does not populate. The route answers with a paginated object — items, total, page, perPage, totalPages — while the admin client types the payload as a plain array and the page falls back to an empty list when it is not one. The result is an empty table whatever the database holds.
  • The "Create Offer" button creates nothing. It shows a message directing the reader to "Products → Add New, set the product type to Upsell offer", and no such product type or product-edit screen exists in the module. Offers can only be created through the REST routes below.

REST API endpoints​

All routes are on aiowc/v1, require manage_woocommerce — plus a REST nonce on cookie-authenticated requests — and answer with the { success, data, message } envelope.

MethodPathPurposeRequired args
GET/upsells/offersPaginated offers; filters type and status, plus page and per_page.–
POST/upsells/offersCreate an offer.–
GET/upsells/offers/{id}Read one offer.–
PUT / PATCH / POST/upsells/offers/{id}Update an offer.–
DELETE/upsells/offers/{id}Delete an offer.–
GET/upsells/ab-testsList A/B tests.–
POST/upsells/ab-testsCreate an A/B test over two offer variants.–
GET/upsells/ab-tests/{id}Results for one test.–
GET/upsells/analyticsImpressions, conversions and revenue across offers.–
GET/upsells/settingsRead the settings above.–
PUT / PATCH / POST/upsells/settingsUpdate the settings above.–

WooCommerce integration​

HookPriorityWhat the module does
woocommerce_review_order_before_submit20Draws the order bumps at checkout.
woocommerce_checkout_order_processed10Applies accepted bumps to the order.
woocommerce_thankyou5Draws the post-purchase offers.
woocommerce_after_single_product_summary15Draws the frequently-bought-together block.
woocommerce_after_cart_table10Draws the cart cross-sell block.
wp_enqueue_scripts10Enqueues each handler's assets.

Accepting or declining an offer goes through admin-ajax.php, registered for both signed-in and guest shoppers:

AJAX actionEffect
aiowc_accept_order_bumpAccept a checkout bump.
aiowc_accept_post_purchaseAccept a post-purchase offer.
aiowc_decline_post_purchaseDecline a post-purchase offer.
aiowc_add_fbt_bundleAdd a frequently-bought-together bundle to the cart.

Database schema​

TableHolds
{prefix}aiowc_upsell_offersOffers: title, type, trigger and offer product ids, discount, priority, position, conditions, status, and running impression, conversion and revenue counters.
{prefix}aiowc_upsell_ab_testsTests: offer id, the two variant ids, traffic split, status, winner and timestamps.
{prefix}aiowc_upsell_logsOne row per impression, acceptance and decline, with the variant and revenue.

Offer reads are cached in transients keyed aiowc_up_offer_* for an hour.

Background jobs​

HookIntervalWork
aiowc_upsell_ab_evaluatedailySettles A/B tests whose variants have passed ab_test_min_sample impressions.
aiowc_upsell_log_cleanupweeklyDeletes log rows older than 180 days, filterable through aiowc_upsell_log_retention_days.

Entitlement limits​

upsells is an on/off grant with no numeric cap. The numeric limits in the module are store settings and constants: the A/B minimum sample, the 180-day log retention and the one-hour offer cache.

Health check​

The module reports a warning when its tables are missing or WooCommerce is inactive. Otherwise it reports that it is functioning normally.

Known gaps​

  • The admin offer table reads a response shape the route does not return, so it is always empty.
  • The "Create Offer" control points at a product type that does not exist; there is no create, edit or delete UI for offers, and no A/B test or analytics screen.
  • fbt_algorithm is stored and returned but does not select an algorithm — frequently-bought-together suggestions come from offers whose trigger products match, whatever the value.