Skip to content
  1. Home
  2. Fishbone Talk

how-to · · 8 min read

how to a/b test a spin-to-win wheel on woocommerce

A spin-to-win wheel is a discount in a costume. Work out what a spin costs, test it against a plain popup fairly, and count it in WooCommerce.

By Big Bad Agency

A spin-to-win wheel is the popup where a visitor types in their email, spins, and “wins” a coupon. They’re everywhere on WooCommerce stores, and most of the advice about them is from people who sell them.

We don’t make one. So this post doesn’t tell you whether to install one. It tells you how to find out whether the one you have is earning its keep, which is a question most stores never actually answer.

The short version: a wheel is a discount in a costume. Testing it properly means separating the costume from the discount, because they’re two different things and only one of them is the wheel.

first, work out what one spin costs you

Every slice on the wheel is a coupon. So before you test anything, work out the average coupon a spin hands out. That’s the price of the wheel, and it’s the number most stores have never written down.

These numbers are made up for illustration. Use your own.

Say the wheel has eight equal slices:

slicehow manywhat it gives
5% off35% off the order
10% off210% off the order
15% off115% off the order
free shipping1you pay the postage
try again1nothing

If every slice is equally likely, the average discount per spin is (3 × 5% + 2 × 10% + 1 × 15%) ÷ 8 = 6.25%, plus a one-in-eight chance you pay the shipping.

On a $60 order where shipping costs you $8, that’s $3.75 of discount plus $1.00 of shipping on average. So about $4.75 per redeemed coupon. It only costs you when someone actually uses the code, but when they do, that’s what the wheel spent.

Two things fall out of doing this sum.

The wheel’s picture might not be its odds. Most wheel tools let you weight slices, so the big 15% wedge can be set to land almost never. That’s a setting, and it’s yours to choose. But if the wheel looks like one in eight and is really one in a hundred, the picture is lying to your customers. Either keep the slices honest or make the odds visible. We’d pick honest.

You can now build a fair control. Which is the whole next section.

the test most stores run is the wrong one

The usual test is “wheel on” against “wheel off”. That measures two things at once:

  1. the wheel’s mechanic: the spin, the game, the little moment of “I won something”, and
  2. the discount the wheel hands out.

A store with no popup at all is giving away 0%. A store with a wheel is giving away about 6.25% plus some shipping. Of course the wheel collects more emails and more first orders. So would any popup offering 6% off. You haven’t learned anything about the wheel.

The fair test holds the discount still and changes only the mechanic:

  • A (control): a plain email popup offering a flat discount equal to the wheel’s average. In the example that’s 6% off your first order (round it to what you’d actually print).
  • B (variant): the wheel.

Now the only difference is the spin. If B wins, the game is earning something on top of the discount. If A and B come out even, you’re paying for animation. If A wins, the wheel is costing you, which happens. Some shoppers read a wheel as a gimmick, and some just close it.

what to count (it isn’t email signups)

Signups are the number the wheel is best at and the one that matters least. A wheel can double signups by handing a coupon to everyone, and the coupon then gets used on an order that would have happened anyway.

Count these, for each variant, over the same dates:

  • orders that used the variant’s coupon
  • revenue after discounts on those orders
  • total discount given on those orders
  • margin after the discount, if you know your costs. If you don’t yet, profit per order is the place to start, because a coupon test without costs only tells you about revenue.

And one number for the whole store: revenue per visitor across both groups. A popup that wins on coupon orders but drives people off the site is still a loss.

tag every coupon by variant

To count any of that, WooCommerce has to know which popup an order came from. The coupon code is the cleanest tag you’ve got, because WooCommerce saves the codes used on every order.

So give each variant its own prefix:

  • control codes start WELCOME-
  • wheel codes start SPIN-

If your wheel tool generates a unique code per spin, set its prefix to SPIN-. If it can only hand out fixed codes, make one per slice, like SPIN5, SPIN10 and so on. Either way, nothing from the plain popup can start with SPIN.

count it with a few lines of php

WooCommerce’s own Analytics → Coupons report shows each code’s orders and discount total, and for a handful of fixed codes that’s enough. Once you have unique codes per spin, there are hundreds of them, so it’s easier to group by prefix yourself.

Save this as ab-coupons.php somewhere outside your web root and run it with WP-CLI: wp eval-file ab-coupons.php. It only reads orders. It changes nothing.

<?php
// Group paid orders by coupon prefix for one date range.
// Read-only. Run with: wp eval-file ab-coupons.php

$prefixes = array( 'WELCOME-', 'SPIN' ); // control, variant
$range    = '2026-10-01...2026-10-28';   // whole weeks, both arms running

$orders = wc_get_orders( array(
	'limit'        => -1,
	'status'       => array( 'wc-processing', 'wc-completed' ),
	'date_created' => $range,
) );

$totals = array();
foreach ( $prefixes as $p ) {
	$totals[ $p ] = array( 'orders' => 0, 'revenue' => 0.0, 'discount' => 0.0 );
}

foreach ( $orders as $order ) {
	foreach ( $order->get_coupon_codes() as $code ) {
		$code = strtoupper( $code );
		foreach ( $prefixes as $p ) {
			if ( 0 === strpos( $code, $p ) ) {
				$totals[ $p ]['orders']  += 1;
				// What the customer paid, minus tax and the shipping you charged.
				$totals[ $p ]['revenue'] += (float) $order->get_total()
					- (float) $order->get_total_tax()
					- (float) $order->get_shipping_total();
				$totals[ $p ]['discount'] += (float) $order->get_discount_total();
				break 2; // count each order once, against the first matching code
			}
		}
	}
}

foreach ( $totals as $p => $t ) {
	$avg = $t['orders'] ? $t['revenue'] / $t['orders'] : 0;
	printf(
		"%-10s orders %4d   revenue %10.2f   discount %9.2f   avg order %8.2f\n",
		$p, $t['orders'], $t['revenue'], $t['discount'], $avg
	);
}

A few things it deliberately doesn’t do:

  • It ignores refunds. If a lot of coupon orders come back, subtract them before you call a winner.
  • It uses the order’s creation date, not the date it was paid.
  • On a big store, 'limit' => -1 loads every order in the range at once. For tens of thousands of orders, page through with 'paged' instead.

Free-shipping slices show up as orders whose discount total is zero but whose shipping you paid, because a free-shipping coupon doesn’t add to the discount total. If you want those costed, add what the postage actually cost you on those orders. WooCommerce doesn’t know that number.

split the traffic properly

Both popups have to run at the same time, to a random half of visitors each. Running the wheel in October and the plain popup in November tells you about October and November.

If your popup tool has a built-in split test, use it. If it doesn’t, the simplest split that survives page caching is done in the browser. Assign each visitor a side once, remember it, and show only that side’s popup:

<script>
(function () {
  var key = 'bb_popup_variant';
  var v = null;
  try { v = localStorage.getItem(key); } catch (e) {}
  if (v !== 'a' && v !== 'b') {
    v = Math.random() < 0.5 ? 'a' : 'b';
    try { localStorage.setItem(key, v); } catch (e) {}
  }
  document.documentElement.classList.add('popup-' + v);
})();
</script>
<style>
  /* Replace with your two popups' real wrapper selectors. */
  html.popup-a .your-wheel-popup  { display: none !important; }
  html.popup-b .your-plain-popup  { display: none !important; }
</style>

Put it in the <head> so the class lands before either popup draws. Hiding with CSS only stops the popup from showing. If a tool fires its own tracking on page load anyway, that’s its business, and it’s another reason to trust the coupon count over the tool’s dashboard.

Don’t split on the server with a PHP cookie if you use full-page caching. The first visitor’s variant gets cached and served to everyone.

how long to run it

Run whole weeks, so both sides get the same mix of weekdays and weekends, and don’t stop it the day one side pulls ahead. Early leads in small tests swap back and forth all the time.

Our own rough rule, not a formula: if each side has a couple of dozen coupon orders and the gap between them is three or four orders, that’s luck. Keep going. The smaller your store, the longer this takes, and on a very small store the honest answer may be that you can’t tell them apart in any reasonable time. That’s a real result too. It means the wheel isn’t doing anything big enough to see, so pick whichever you’d rather show your customers.

what this test can’t tell you

  • Whether the discount is worth giving at all. Both arms give away the same amount. To test the discount itself, run a popup with a coupon against a popup without one. That’s a separate test, and quantity discounts goes through the same “what does this discount actually cost me” sum for bulk tiers.
  • Long-term value. A wheel might collect emails that buy once and unsubscribe. If you can, check repeat orders from each group a few months later.
  • Anything about your list’s quality, beyond whether those people placed an order.

If the wheel wins the fair test, keep it, and you’ll know why. If it ties, you’ve found a cheaper popup that does the same job.

Disagree? Good.

Write to a person: hello@bigbad.agency. We answer within a business day.

Start a project