Two-Product Sales Funnel
Build focused, high-converting sales funnels for stores with 1 or 2 core products without exposing a traditional catalog or shop page.
Overview
Many modern digital stores, boutique manufacturers, and software companies offer only one or two primary products (such as a Starter vs. Pro edition, or a flagship device with an accessory bundle). For these businesses, a traditional multi-page catalog with category navigation and a separate cart view introduces unnecessary friction.
WooNooW provides a native Two-Product Funnel pattern that presents both offers directly on the frontpage or dedicated landing page and routes visitors straight to checkout using the native purchase-offer section.
flowchart TD
A[Visitor Lands on Frontpage] --> B{Hero & Value Proposition}
B --> C[Offer 1: Starter Edition (purchase-offer)]
B --> D[Offer 2: Pro Bundle (purchase-offer)]
C -->|Buy Now - Direct Route| E[Direct /checkout SPA]
D -->|Buy Now - Direct Route| E
E --> F[Dynamic Payment Gateway Evaluation]
F --> G[Order Received Receipt]
Architecture: No Catalog Required
A common misconception is that using Full SPA Mode forces your store to display a public product catalog or /shop grid.
- Full SPA Does Not Force a Shop: You can use Full SPA mode exclusively for your frontpage sales funnel and checkout. Simply do not create menu links to
/shopor product archive pages. - Funnel Bypass UX vs Cart Engine: The direct "Buy Now" flow bypasses the customer-facing
/cartpage to reduce abandonment. However, it still operates through WooCommerce's cart and session engine (POST /cart/buy). - Do NOT Delete WooCommerce Pages: Never delete standard WooCommerce core pages (
/cart,/checkout,/my-account). WooCommerce requires them internally for session handling, cookie management, payment gateway callbacks, and order receipts.
The 17th Native Section: purchase-offer
The purchase-offer section is WooNooW's native direct-to-checkout purchasing block. Unlike standard CTA buttons that link to static URLs, purchase-offer directly interfaces with WooCommerce inventory, pricing, variations, and cart sessions.
Section Schema & Configuration
| quantity | integer | 1 | Number of units added upon purchase (minimum positive integer 1). |
| cart_policy | 'replace' \| 'append' | 'replace' | Cart mutation policy when clicked. |
| button_text | string | 'Buy now' | Label displayed on the action button. |
| heading | string | "" | Optional heading rendered above the offer card. |
| layoutVariant | 'default' \| 'card' \| 'split' | 'default' | Visual layout variant (layoutVariant in JSON). |
Visual Layout Variants
default: Inline presentation with centered product information and action button.card: Contained card format with elevated borders, badge indicators, and prominent pricing. Ideal for side-by-side or stacked comparisons.split: Two-column layout displaying featured imagery on one side and product specs, attributes, and purchase controls on the other.
Live Authoritative Pricing & Inventory
WooNooW adheres to a strict rule: Layouts and AI generators do not dictate product pricing.
When the customer visits the page, the purchase-offer component dynamically queries the authoritative WooCommerce REST API:
- Current Price: Reflects current active product or sale prices and multi-currency conversion rates. Coupons are evaluated and applied during checkout.
- Regular Price & Strike-Through: Displays original MSRP when a discount or sale is active.
- Stock Status: Validates real-time inventory. If an item is out of stock, the button automatically disables and displays an "Out of stock" indicator.
- Editor Preview Mode: In the Visual Page Editor, the section displays an illustrative preview and the "Buy Now" button operates in safe preview mode without mutating the cart.
Cart Policies: replace vs append
The cart_policy setting dictates how the customer's cart behaves when they click the purchase button:
replace (Recommended for Direct Funnels)
- Replaces the current cart contents with this specific product and quantity, ensuring a clean 1-click checkout flow.
- Accidental Replacement Safeguard: If the customer already has multiple items or a different product in their cart, WooNooW invokes an explicit confirmation prompt:
"Your cart already contains items. Would you like to replace them with this purchase?"
- If confirmed, the prior cart items are cleared and the new item is set. If canceled, the prior cart remains intact.
append
- Adds the item and quantity to any existing items in the customer's cart and routes immediately to
/checkout.
Handling Variable Products & WooCommerce "Any"
If your core product has selectable variations (e.g. Size, Color, or License Tier):
- The
purchase-offersection dynamically renders attribute selector chips or dropdowns based on the parent product's configured variation attributes. - WooCommerce "Any" Attribute Matching: If attributes in WooCommerce are set to "Any" (meaning any value is valid for that variation), WooNooW automatically resolves a non-empty allowed selection (defaulting to the preset variation or first available option), which the buyer can review or adjust before clicking Buy.
- The component uses an attribute-scoring algorithm to find the exact matching variation ID and updates the displayed price and featured image accordingly.
Native Purchase Contract (POST /cart/buy)
When the customer clicks the purchase button, the frontend dispatches an atomic, safe intent request to the WooNooW REST API:
Request Payload Specifications
product_id(integer, required): Must be a strictly positive integer ($\ge 1$) corresponding to a published, purchasable WooCommerce product.variation_id(integer, optional): Non-negative integer ($\ge 0$). Required for variable products; must match a published, in-stock variation belonging to the parent product.attributes(object, optional): Canonical key-value record of attribute names/slugs to scalar values. Normalization (BuyIntent::normalize_attributes()) stripsattribute_prefixes, formats keys intoattribute_{slug}, normalizes values to lowercased trimmed strings, and sorts keys alphabetically (ksort) for deterministic payload hashing.quantity(integer, required): Number of units to add (strictly positive integer $\ge 1$).cart_policy(string, required):replace: Empties existing cart contents while keeping session alive, then adds offer item.append: Adds offer item alongside existing cart items.
intent_id(string, required): Unique UUID or UUID-like string between 16 and 64 characters (matching regex/^[0-9a-fA-F\-]{16,64}$/).return_path(string, optional): Local-only relative route URL. Sanitized viaBuyIntent::sanitize_return_path()by stripping sensitive query parameters (secret,token,order_key,nonce), rejecting backslashes, CRLF, protocols, and external domains (defaults to/).
Security, Headers & Concurrency Controls
- Nonce & Origin Validation: Requires a valid
wp_restnonce sent via theX-WP-Nonceheader or_wpnoncequery parameter. Rejects cross-origin mismatches whenOriginorRefererheaders are present. - Active Session Cookie: Requires an active customer/guest WooCommerce session cookie (
customer_idfromWC()->session). - Caching Headers: Sends
Cache-Control: no-store, no-cache, must-revalidate, max-age=0. Configure proxies/CDNs to respect this header and exclude personalized cart/checkout routes; application headers cannot override a misconfigured cache. - Fail-Closed Advisory Lock (
wn_buy_+ md5): Acquires a MySQL advisory lockwn_buy_ . md5($customer_id)with a 5-second timeout (configurable via filterwoonoow_buy_intent_lock_timeout). This lock is shared exclusively betweenPOST /cart/buyandPOST /checkout/submit; it is NOT acquired by native WooCommerce AJAX endpoints (/?wc-ajax=...). Test simulation filter:woonoow_buy_intent_simulate_lock_failure. - Session Refresh Under Lock: Under lock protection, session data is reloaded directly from the database (
BuyIntent::refresh_session()) to eliminate stale parallel race conditions. - Atomic Mutation & Rollback: A complete snapshot of cart state (
snapshot_cart()) is stored in memory before mutation. If product validation, variation matching, stock check, or the standard WooCommerce filterwoocommerce_add_to_cart_validationfails, the snapshot is fully restored (restore_cart_from_snapshot()). - Session-Bounded Idempotency: Stores up to 50 recent intents (
woonoow_buy_intents) in the active WooCommerce session. Identical retries with matching payload hash return the cached response without duplicate cart mutations. A differing payload using the sameintent_idreturns 409 Conflict (intent_conflict). - Session Lifetime Boundary: Idempotency is bound to the duration of the customer's WooCommerce session; it is not durable beyond session expiration or database purge.
Response Payload (200 OK)
Dynamic Gateways & Honest Checkout Contract
When the customer arrives at the WooNooW reactive checkout, payment gateways are dynamically evaluated based on capabilities:
Honest Gateway Contract: WooNooW never claims that all third-party payment gateways can be rendered inline within React. Gateways requiring complex inline form fields or PCI-scoped iframe elements utilize the secure native hosted payment handoff.
Payment Handoff Preserves the Cart
When a gateway with custom fields is selected (has_fields === true), the submission returns payment_handoff: true.
- The customer's cart is explicitly PRESERVED in the session so items are not lost if the customer abandons the external hosted payment screen or returns to make adjustments.
- In contrast, when
process_payment()accepts the order (result === 'success') or a free order is completed the cart is cleared viaWC()->cart->empty_cart(true). A successful offline/redirect result does not necessarily mean funds have settled.
Order Receipt != Settled Payment
When an order is submitted, the customer is routed to /order-received/:id?key=....
- Receipt Displays Current Status: For offline payment methods or pending offsite transactions, the order status is typically
on-holdorpending. - A rendered receipt confirms order creation and provides payment details; it does not signify that funds have been settled.
- Payment Confirmed Status:
- For Cash on Delivery (
payment_method === 'cod'),payment_confirmedistrueONLY when order status iscompleted(NOTprocessing). - For other payment methods,
payment_confirmedistrueif$order->is_paid() || $order->has_status('completed').
- For Cash on Delivery (
Architecture: SEO & Gateway Boundaries
Root, Entry, and Landing Page SEO Alignment
In Full SPA Mode, the client-side router mounts dynamically at the configured SPA page. For search engine optimization and consistent crawler indexing:
- Recommended Page Alignment: We strongly recommend setting your store root (
/), entry route, and landing pages to the same page (e.g., your primary frontpage). - SEO Limitations: Because client-side routing dynamically switches views, deep checkout or receipt URLs are rendered via JavaScript unless pre-rendered or caught by WooNooW's BrowserRouter rewrite rules. Aligning root, entry, and landing pages eliminates crawl ambiguity and canonical mismatch.
Manual Sandbox Gateway Acceptance Testing
Payment processors vary significantly in their webhook timing, iframe cross-origin requirements, and 3D Secure / SCA challenges. WooNooW does not claim arbitrary third-party payment SDKs can run inline in React or guarantee exactly-once payment processing across arbitrary gateways without configuration.
- Mandatory Testing: Always perform manual end-to-end sandbox acceptance testing for each active payment gateway (including testing card declines, 3DS flows, cancellations, and return redirects) before going live.
Full SPA Mode vs checkout_only Mode
When configuring your two-product funnel under WooNooW → Appearance → General:
- Full SPA Mode (Recommended): Both the landing sales page and the checkout run inside the fast, single-page application. Transitions between clicking "Buy" and seeing the checkout screen are instantaneous.
checkout_onlyMode: The landing sales page is rendered using your standard WordPress theme, but clicking a Buy Now link transitions into the React checkout. Note thatcheckout_onlydoes not automate purchasing; it simply mounts the SPA container at/checkout.
Related Documentation
- AI Frontpage Guide — Detailed steps for generating and importing this layout with AI.
- Checkout Feature Guide — End-to-end checkout lifecycle, security, and session management.
- Section Components Catalog — Detailed prop references for all 17 native sections.
Last updated Sep 14, 2026