Docs Raffle for WooCommerce

Lucky Wheel

Lucky Wheel / Spin to Win#

Interactive HTML5 canvas prize wheel with server-side outcome determination. Works in two modes: product-tied (quick setup on any raffle product) and standalone (dedicated admin page, usable anywhere in your store — cart, checkout, thank-you, popup, or shortcode).


Two Ways to Create a Wheel#

Product WheelStandalone Wheel
Created viaProduct edit screen → PRO FeaturesWooCommerce → Lucky Wheels → Add New
Tied to a productYes — one raffle productNo — independent
Display on product pageAutomatic (before form, after form, or after summary)Via Display Rules or shortcode
Cart / Checkout / Thank-youNoYes
Popup / ModalNoYes
Email gateNoYes
Coupon scopeAuto-restricted to that productConfigurable — any product, category, or store-wide
Shortcode[raffle_wheel id="PRODUCT_ID"][raffle_wheel id="WHEEL_ID"]

Product Wheel (Quick Setup)#

Best for raffle owners who want a wheel on their product page with minimal configuration.

  1. Edit a raffle product.
  2. In the PRO Features section, check Lucky Wheel.
  3. Choose Quick setup (configure slices inline) or Use existing wheel (link a standalone wheel).

Standalone Wheel#

Best for store-wide marketing: cart abandonment recovery, checkout incentives, lead generation, or post-purchase re-engagement.

  1. Go to WooCommerce → Lucky Wheels → Add New.
  2. Give it a title (e.g. “Summer Sale Spinner”).
  3. Configure slices, display rules, coupon settings, and spin limits.
  4. Publish.

Configuring Slices#

Each slice of the wheel represents one possible outcome. A wheel needs at least 2 slices.

FieldDescription
ColourVisual colour on the wheel (colour picker)
LabelText displayed on the slice (e.g., “10% Off”, “Free Ticket!”)
WeightProbability weight. Higher number = more likely. Weights are relative to all slices.
Prize typeNo prize, Discount %, Discount (fixed), Free ticket, Custom message
Prize valueThe value: percentage, currency amount, or custom text

Free Ticket (standalone wheels)#

When using a standalone wheel, the Free Ticket prize type shows an additional Target Product dropdown. This lets you select which product the 100% discount coupon applies to. If left empty, the coupon has no product restriction.

Default slices#

When enabling a wheel for the first time, these defaults are pre-populated:

LabelTypeWeightEffect
10% OffDiscount %2010% coupon
Try AgainNo prize35Better luck next time
25% OffDiscount %1025% coupon
Better LuckNo prize30Better luck next time
Free Ticket!Free ticket5100% discount coupon
5% OffDiscount %255% coupon

Display Rules (Standalone Wheels)#

Configure where standalone wheels appear automatically, without shortcodes.

Product pages#

OptionBehaviour
Specific productsSelect one or more products. The wheel renders on those product pages.
All products in categoriesSelect categories. The wheel renders on every product in those categories.
Do not showThe wheel does not auto-display on any product page.

When shown on product pages, the wheel renders before the add-to-cart form by default.

WooCommerce pages#

OptionBehaviour
Cart pageRenders above the cart. Works with both classic (shortcode) and block-based cart.
Checkout pageRenders above the checkout form. Works with both classic and block-based checkout.
Thank-you pageRenders on the order confirmation page. Supports a minimum order total threshold — the wheel only shows if the order meets the minimum.

Block & Classic compatibility#

All placements detect whether WooCommerce is using the block-based cart/checkout (default since WooCommerce 8.3) or the legacy shortcode pages and hook in correctly for both.


Show the wheel as a popup triggered by user behaviour.

Settings#

SettingDescription
Enable popupToggle on/off
TriggerDelay (seconds after page load), Scroll (% scrolled), Exit intent (mouse leaves viewport)
DelaySeconds to wait (for delay trigger)
Scroll %Percentage of page scrolled (for scroll trigger)
Page scopeAll pages, WooCommerce pages only, or Shop & product pages only
CooldownDays before the popup shows again to the same visitor (stored in localStorage)

How it works#

  1. The popup renders as a native HTML <dialog> element in the page footer.
  2. The selected trigger fires on the appropriate event.
  3. The dialog opens with a backdrop overlay and entrance animation.
  4. The close button (×) or clicking outside dismisses it.
  5. After closing, the cooldown prevents it from showing again for the configured number of days.

Coupon Settings#

All auto-generated coupons share these characteristics:

  • Single-use (1 redemption)
  • Configurable expiry (default: 7 days, overridable per wheel or via global settings)
  • Optional minimum order amount
  • Described as “Lucky Wheel prize — [wheel name]“

Coupon scope (standalone wheels)#

ScopeBehaviour
Store-wideNo product/category restriction
Specific productCoupon only valid for the selected product
Specific categoryCoupon only valid for products in the selected category

For product-tied wheels, coupons are automatically scoped to that raffle product.

Auto-cleanup#

Expired wheel-generated coupons are automatically trashed by a daily WP-Cron job. This can be toggled on/off in the global Pro settings.


Email Gate & Lead Generation#

Require visitors to enter their email address before they can spin. Turns the wheel into a lead generation tool.

Enabling#

  1. Edit a standalone wheel.
  2. In the Spin Limits meta box, check Require email before spinning.
  3. Optionally enable the consent checkbox and customise the consent text (e.g. “I agree to receive marketing emails”).

How it works#

  1. The wheel renders with an email form overlay instead of the Spin button.
  2. The visitor enters their email (and ticks consent if required).
  3. The email is validated and checked for duplicates (same email cannot spin the same wheel twice).
  4. On success, the overlay disappears and the Spin button becomes active.
  5. If the visitor wins a coupon, the coupon code is emailed to them using WooCommerce’s branded email templates.

When enabled, a checkbox with custom text appears below the email input. The form cannot be submitted without ticking it. Consent status is recorded in the database.

Integration hooks#

After an email is collected, the action rfwc_pro_wheel_email_collected fires with four parameters:

do_action( 'rfwc_pro_wheel_email_collected', $email, $wheel_id, $row_id, $consent );

Use this to integrate with Mailchimp, Mailpoet, ConvertKit, or any other email service.

Admin: Collected Emails#

Go to WooCommerce → Lucky Wheels → Collected Emails to view all collected emails.

FeatureDescription
Filter by wheelDropdown to show emails for a specific wheel
Pagination30 emails per page
CSV exportOne-click download of all emails (or filtered by wheel)

CSV columns: Email, Wheel, Coupon Code, Consent (Yes/No), IP, Date.


Thank-You Page / Post-Purchase#

Show the wheel on the order confirmation page as a re-engagement tool: “You just ordered — spin for a reward on your next purchase!”

Order-aware features#

FeatureDescription
Purchase thresholdOnly show the wheel if the order total meets a configurable minimum
One spin per orderPrevents refresh abuse — each order can only spin each wheel once (tracked in order meta)
Customer-tied couponsWon coupons are email-restricted to the customer’s billing email
Raffle-aware modeIf the order contains raffle products with their own wheel, that product-specific wheel shows first

Sound Effects#

When enabled, the wheel plays sounds using the Web Audio API (no external files):

EventSound
SpinningTick-tick as the pointer passes each slice
WinAscending fanfare
LoseDescending tone

Sound can be toggled per wheel or set as a global default.


Spin Tracking#

User typeStorage
Logged-in usersUser meta (_rfwc_pro_wheel_spins)
GuestsWooCommerce session
Thank-you pageOrder meta (_rfwc_pro_order_spun_wheels)

The server enforces the max spins limit — even if the frontend button is visible, exceeding the limit returns an error.


Shortcode#

[raffle_wheel id="123"]
AttributeValuesDefaultDescription
idPost IDRequiredProduct ID (for product wheels) or Wheel ID (for standalone wheels)

The shortcode handler automatically detects whether the ID belongs to a product or a standalone wheel and renders accordingly.


Global Pro Settings#

Go to WooCommerce → Settings → Raffle → Pro to configure store-wide defaults.

SettingDefaultDescription
Default Max Spins1Max spins per user for new wheels (0 = unlimited)
Default Coupon Expiry7 daysHow long wheel coupons last
Default Sound EffectsOnEnable sound by default
Auto-delete Expired CouponsOnDaily cron to trash expired wheel coupons
Default Email GateOffRequire email by default

Per-wheel settings always override global defaults. Empty/unset per-wheel values fall back to the global value.


How Spinning Works (Technical)#

  1. Customer clicks Spin! (or submits email first if email gate is active).
  2. An AJAX request is sent to the server with the wheel/product ID (and order ID if on thank-you page).
  3. The server validates: wheel exists, user hasn’t exceeded max spins, email gate passed, order not already spun.
  4. A winning slice is picked using weighted random selection (random_int — cryptographically secure).
  5. If the prize requires a coupon, a WooCommerce coupon is created with the configured settings.
  6. The server returns the winning slice index and a rotation angle.
  7. The frontend animates the wheel (4.5 second easing animation) to land on the winning slice.
  8. A result card appears showing the outcome — with a copy-to-clipboard button for coupon codes.

Security: The outcome is always determined server-side. The frontend animation is purely visual — the wheel always lands on the server-determined slice.


Prize Types#

TypeWhat happens
No prize”Better luck next time” message. No action taken.
Discount %A single-use WooCommerce coupon with the specified percentage off.
Discount (fixed)A single-use fixed-amount coupon.
Free ticketA 100% discount coupon for 1 unit (product wheels: that product; standalone: configurable target product).
Custom messageDisplays the configured text. No coupon generated. Useful for “Come back tomorrow” or “Follow us on Instagram” type prizes.

CSS Classes#

Wheel#

ElementCSS Class
Container.rfwc-pro-lucky-wheel
Title.rfwc-pro-lucky-wheel__title
Canvas container.rfwc-pro-lucky-wheel__container
Canvas.rfwc-pro-lucky-wheel__canvas
Pointer.rfwc-pro-lucky-wheel__pointer
Spin button.rfwc-pro-lucky-wheel__spin-btn
Result container.rfwc-pro-lucky-wheel__result
Win card.rfwc-pro-lucky-wheel__result-card--win
Lose card.rfwc-pro-lucky-wheel__result-card--lose
Coupon code.rfwc-pro-lucky-wheel__coupon-code

Email Gate#

ElementCSS Class
Container.rfwc-pro-wheel-email-gate
Prompt text.rfwc-pro-wheel-email-gate__prompt
Form wrapper.rfwc-pro-wheel-email-gate__form
Email input.rfwc-pro-wheel-email-gate__input
Consent label.rfwc-pro-wheel-email-gate__consent
Submit button.rfwc-pro-wheel-email-gate__submit
Error message.rfwc-pro-wheel-email-gate__error
ElementCSS Class
Dialog.rfwc-pro-wheel-popup
Inner wrapper.rfwc-pro-wheel-popup-inner
Close button.rfwc-pro-wheel-popup-close

Files Reference#

FilePurpose
class-rfwc-pro-lucky-wheel.phpCore wheel class — product mode, rendering, AJAX spin, prize generation
class-rfwc-pro-lucky-wheel-cpt.phpStandalone wheel CPT registration, admin columns
class-rfwc-pro-lucky-wheel-metaboxes.phpEdit screen meta boxes (slices, display rules, coupon, limits, shortcode)
class-rfwc-pro-lucky-wheel-placements.phpAuto-display on cart/checkout/thank-you (block + classic), popup rendering
class-rfwc-pro-lucky-wheel-email-gate.phpEmail collection, custom table, AJAX, coupon emailing, admin page
class-rfwc-pro-settings.phpPro global settings (WooCommerce → Settings → Raffle → Pro)
lucky-wheel.jsFrontend canvas drawing, animation, spin AJAX, email gate JS
admin-lucky-wheel.jsAdmin slice repeater, source toggle, prize type toggle
lucky-wheel-popup.jsPopup trigger logic (delay, scroll, exit intent), localStorage cooldown
lucky-wheel.cssFrontend wheel + email gate styles
lucky-wheel-popup.cssPopup dialog styles
admin.cssAdmin meta box styles

Action & Filter Hooks#

HookTypeParametersDescription
rfwc_pro_wheel_email_collectedAction$email, $wheel_id, $row_id, $consentFires after email is collected (email gate)
rfwc_pro_wheel_spin_completedAction$wheel_id, $prize_data, $session_emailFires after a standalone wheel spin completes