Checkout
Streamlined purchasing experience in WooNooW with automated session hydration, dynamic payment gateway contracts, auto-registration, multi-currency validation, and fail-closed checkout security.
Overview
The WooNooW checkout is a reactive Single Page Application (SPA) that synchronizes directly with the authoritative WooCommerce cart and session engine. It supports guest checkout, automated customer account provisioning, multi-currency settlement, dynamic shipping rate calculation, invisible CAPTCHA verification, direct-buy funnel bypassing, and subscription renewal collection.
graph TD
A[Cart Review or Direct Buy Offer] --> B[GET /checkout/bootstrap]
B --> C[Customer & Address Details]
C --> D[Shipping Rate Calculation]
D --> E[Dynamic Payment Gateway Evaluation]
E --> F[Invisible CAPTCHA Token Generation]
F --> G[POST /checkout/submit]
G -->|Validation or Lock Failed| H[Preserve Cart & Reset CAPTCHA]
G -->|Custom Fields Gateway (payment_handoff)| L[Preserve Cart & Redirect to Native Pay URL]
G -->|Gateway Payment Failed| M[Preserve Payable Order & Cart for Retry]
G -->|Payment Succeeded| I[Empty Cart & Commit Session]
I -->|Guest Auto-Registered| J[Hard Page Reload to Order Received]
I -->|Existing / Logged In| K[SPA Navigate to Order Received]
Checkout Flow Lifecycle
Step 1: Bootstrap & Session Hydration
When the customer enters the checkout screen, the frontend issues a non-cacheable request to initialize personalized checkout state:
The bootstrap response delivers:
- Authoritative Cart Totals: Hydrated from the customer's server session (
items,subtotal,discount_total,tax_total,total,coupons). - Active Currency: Base currency, active currency code, symbol, formatting decimals, and exchange rate ID.
- Dynamic Checkout Fields: Standard and custom fields, respecting visibility rules and third-party addon filters.
- Geographic Data: Allowed billing/shipping countries, localized states, and default store country.
- Saved Addresses: Pre-populated address book entries if the user is authenticated.
- Payment Gateways: Available active gateways with capability and behavior metadata.
- Public Security Config: The configured CAPTCHA provider (
none,recaptcha,turnstile) and public site keys.
Step 2: Customer Details & Guest Auto-Registration
Customers provide billing contact information, shipping destinations, and order notes.
Automated Customer Registration
Auto-registration removes account-creation friction for new buyers. It is configured in WooNooW → Settings → Customers using the auto_register_members option (default: disabled).
When enabled and a guest submits an order with a billing email:
flowchart TD
Submit[Guest Submits Order] --> Check{Email Exists in WP?}
Check -->|Yes| LoginRequired[Reject Order: login_required - Fail Closed]
LoginRequired --> PreserveCart[Preserve Cart & Prompt Login]
Check -->|No| CreateUser[Create User via wp_insert_user]
CreateUser --> GenPass[Generate Secure Password]
GenPass --> SetCookie[Set Auth Cookie & Current User]
SetCookie --> FireHook[Fire woocommerce_created_customer Hook]
FireHook --> RespLogin[Return user_logged_in: true]
- Existing User Email (
login_required): If the billing email matches an existing registered WordPress account (get_user_by('email', $email)), WooNooW strictly rejects the order submission BEFORE creating an order:- Security Protection: Prevents guest impersonation, customer account takeover, and unauthorized linking of order histories. The customer must log in with their account credentials to proceed.
- Cart Preserved: The customer's cart and applied coupons remain intact while they log in.
- New User Email:
- A new WordPress user account is created with role
customer. - A secure 12-character random password is generated and stored temporarily in user meta
_woonoow_temp_passwordso WooCommerce can send the standard welcome email with credentials. - The customer is automatically authenticated via
wp_set_auth_cookie()andwp_set_current_user(). - The response returns
user_logged_in: true.
- A new WordPress user account is created with role
Frontend Auth Refresh Navigation
When user_logged_in: true is returned, client-side SPA routing is intentionally bypassed:
This ensures the browser's cookie jar is refreshed across the entire site, giving the customer instant access to their account dashboard and order history.
Step 3: Dynamic Shipping Rates
If physical products are in the cart (requires_shipping === true), the client requests available delivery options:
- Hook Integration: Fires
woonoow/shipping/before_calculateallowing shipping addons (such as Rajaongkir or Biteship) to register session destination data. - Zone Matching: Matches package destination against WooCommerce shipping zones (
WC_Shipping_Zones). - Custom Fallback: For external shipping calculators that do not use WooCommerce shipping rate instances, the submission endpoint accepts fallback parameters
shipping_costandshipping_title.
Step 4: Multi-Currency & Coupon Verification
Before order creation, WooNooW performs multi-currency validation:
- Exchange Rate Freshness: If the checkout currency differs from the store base currency, the system verifies that the exchange rate snapshot has not expired. Stale exchange rates abort submission with error code
currency_rate_stale. - Gateway Compatibility: Verifies that the selected payment method is permitted for the active currency (
GatewayCurrencyMapper). Incompatible gateways returncurrency_gateway_mismatch; unmapped gateways returncurrency_gateway_unavailable. - Coupon Currency Policy: Coupons restricted to specific currencies are validated (
CouponCurrencyPolicy). Invalid coupons returncoupon_currency_mismatch. - Order Audit Snapshot: During
woocommerce_checkout_create_order, an immutable multi-currency snapshot is saved to the order before totals are finalized.
Step 5: Checkout Security, Concurrency & Advisory Locks
To prevent automated order stuffing, race conditions, and fraudulent card testing, the checkout incorporates three security and concurrency layers:
- IP Sliding-Window Rate Limiting: If an IP exceeds
woonoow_rate_limit_orderswithinwoonoow_rate_limit_minutes, submission is rejected with: - Invisible CAPTCHA Verification:
- Cloudflare Turnstile or Google reCAPTCHA v3 executes silently before order submission.
- If CAPTCHA is required, a valid
captcha_tokenmust be present. - Tokens are strictly single-use and fail-closed.
- On any submission failure (such as a declined card, coupon validation error, or server error), the client immediately discards the token and resets the widget to prevent replay attacks.
- Fail-Closed Per-Session Advisory Lock (
wn_buy_+ md5):- Serializes checkout submission per active session using MySQL advisory lock
wn_buy_ . md5($customer_session_id)with a 5-second timeout (CheckoutController.php:365-374). - This lock is shared exclusively between
POST /cart/buyandPOST /checkout/submit; it is NOT acquired by native WooCommerce AJAX requests (/?wc-ajax=...). - Reloads session data from the database under lock (
BuyIntent::refresh_session()) before evaluating cart items or creating orders, preventing parallel submission race conditions.
- Serializes checkout submission per active session using MySQL advisory lock
Step 6: Dynamic Payment Gateways & Behavior Contract
During checkout bootstrap and order submission, WooNooW classifies payment methods into three clear operational behaviors (includes/Api/CheckoutController.php:1312-1325):
graph TD
GW[Gateway Selected] --> Check{Has Fields or Offline?}
Check -->|bacs, cheque, cod| Offline[offline: process_payment and redirect to receipt]
Check -->|has_fields === true| Hosted[hosted_pay: redirect to native hosted pay URL]
Check -->|Standard Offsite| Redirect[redirect: redirect to payment processor URL]
Payment Handoff Preserves the Cart
When a gateway requiring 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 adjust their order.
- The cart is emptied when
process_paymentaccepts the order (result === 'success'), or after completing a free order. Offline/redirect acceptance is not proof of settlement; use the receipt status and WooCommerce payment confirmation.
Native Gateway Return URL Filter
When gateways finish processing and return to WooCommerce's order received endpoint:
- WooNooW hooks into
woocommerce_get_checkout_order_received_url(priority 20, 2 args inTemplateOverride.php:20, 90-98). - Rewrites the thank-you URL to the reloadable SPA route
/checkout/order-received/{id}?key={key}strictly for orders tagged with_woonoow_session_idwhenspa_mode === 'full'. - Standard storefront or non-WooNooW orders without the session tag retain default WooCommerce thank-you URLs.
Thank-You Receipt != Settled Payment
When an order is submitted, the customer is directed to /order-received/:id?key=....
- The receipt displays the live order status (e.g.
on-holdfor bank transfers orpendingfor asynchronous gateways). - An order receipt is a transaction acknowledgment, not proof of settled funds. Merchant fulfillment should verify actual payment capture in WooCommerce.
- Order View & Payment Confirmation (
GET /checkout/order/:idinCheckoutController.php:189-297):- Validates access via order
key(constant-timehash_equals()) OR authenticated customer owner (get_current_user_id() === $order->get_customer_id()) OR administrator (manage_woocommerce). - The
payment_confirmedboolean:- 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 (
- Validates access via order
Step 7: Order Placement, Idempotency & Cart Lifecycle
Order submission is processed through POST /woonoow/v1/checkout/submit.
Checkout Idempotency (checkout_intent_id)
- Optional Intent ID: Clients may submit an optional
checkout_intent_id(UUID format matching regex/^[0-9a-fA-F\-]{16,64}$/). - Stable Hash Computation: Computes a deterministic payload hash (
get_payload_intent_hash()), specifically excluding volatile fields:captcha_token,order_id,order_key, andcheckout_intent_id. - Session-Bounded Success Replay: Stores up to 50 recent successful checkout intents (
woonoow_checkout_intents) inWC()->session.- If an identical payload is submitted with the same
checkout_intent_id, the server replays the original success response without creating duplicate orders or double-charging. - If a differing payload is submitted with an already-used
checkout_intent_id, the server aborts with 400 errorcheckout_intent_conflict. - Session Lifetime Boundary: Idempotency is bounded strictly to the customer's active WooCommerce session lifetime; it is not durable beyond session expiration or purge.
- If an identical payload is submitted with the same
Successful Submission Cart Cleanup
When an order is created and payment succeeds (result === 'success'):
WC()->cart->empty_cart(true)clears all items, shipping packages, and coupon records from the server session.WC()->session->save_data()commits the cleared state to the database.- If a guest user auto-registered during checkout, the previous guest session key (
$guest_session_id) is deleted viaWC()->session->delete_session($guest_session_id)to prevent orphan session revival. - The frontend resets the client-side cart store and invalidates TanStack React Query caches (
cart,account-orders).
Failed Submission & Sequential Retry Contract
If gateway processing fails or throws an exception:
- Payable Order Preserved: The created order is preserved in status
failedorpending(it is NOT deleted). - Failure Response Contract: The endpoint returns:
- Sequential Retry Reuse: The frontend retries by submitting the same payload augmented with
order_idandorder_key. - Fingerprint Validation & Per-Order Lock: The controller verifies exact fingerprint match (currency, line items, billing/shipping addresses, coupons, and shipping method) and acquires a dedicated per-order lock
wnw_order_pay_{order_id}, safely reusing the existing order rather than creating duplicate unpaid orders. - Cart Preserved: The customer's cart and applied coupons are NOT emptied. The server session and client basket remain intact so the customer can correct payment details without losing items.
Direct Buy Intent (POST /cart/buy) & Sales Funnels
WooNooW provides a high-converting Direct Buy Now path used by the native Purchase Offer section:
sequenceDiagram
actor Buyer
participant Section as Purchase Offer Section
participant API as POST /cart/buy
participant Checkout as /checkout SPA
Buyer->>Section: Click "Buy Now"
Note over Section: If cart_policy === "replace" and cart has items: confirm replacement
Section->>API: Dispatch safe purchase intent
API->>API: Validate product, stock & cart policy
API-->>Section: Return updated cart & checkout_url
Section->>Checkout: Client-side navigate to /checkout
Request Contract (POST /woonoow/v1/cart/buy)
product_id(integer, required): Published WooCommerce product ID (must be strictly positive $> 0$).variation_id(integer, optional): Non-negative variation ID for variable products ($\ge 0$, default0).attributes(object, optional): Canonical key-value map of variation attribute slugs to scalar values. Normalized viaBuyIntent::normalize_attributes()(lowercased, trimmed, and sorted alphabetically) for deterministic payload hashing.quantity(integer, required): Units to purchase (strictly positive integer $\ge 1$, default1).cart_policy(string, required):replace(default): Clears the existing cart before adding this offer. Triggers explicit browser confirmation (window.confirm) if the cart already holds other items.append: Adds this offer alongside existing cart contents.
intent_id(string, required): Unique UUID v4 idempotency key matching regex/^[0-9a-fA-F\-]{16,64}$/.return_path(string, optional): Relative local URL. Sensitive query parameters (token,secret,order_key,nonce) are automatically stripped before storage (defaults to/).
Security, Headers & Concurrency Controls
- Nonce & Origin: Requires valid
wp_restnonce (X-WP-Nonceheader or_wpnonce) and same-origin verification. - Active Session Cookie: Requires an active customer/guest WooCommerce session cookie.
- Strict Caching Headers: Sets
Cache-Control: no-store, no-cache, must-revalidate, max-age=0vianocache_headers(). - Shared Advisory Lock (
wn_buy_+ md5): Acquires fail-closed MySQL advisory lock with 5s timeout (configurable via filterwoonoow_buy_intent_lock_timeout). Shared exclusively betweenPOST /cart/buyandPOST /checkout/submit(not native WooCommerce AJAX). Test simulation filter:woonoow_buy_intent_simulate_lock_failure. - Session Refresh Under Lock: Refreshes session data from database (
BuyIntent::refresh_session()) to eliminate stale parallel state. - Atomic Mutation & Rollback: Cart snapshot (
snapshot_cart()) restored on any validation or addition failure (restore_cart_from_snapshot()). Evaluates standard WooCommerce add-to-cart filterwoocommerce_add_to_cart_validation. - Session-Bounded Idempotency: Remembers up to 50 recent buy intents (
woonoow_buy_intents) inWC()->session. Identical retries return cached result; conflicting payload hashes return 409 Conflict (intent_conflict). Not durable beyond active WooCommerce session lifetime.
Variable Products & WooCommerce "Any"
If a product has variations where attributes are set to "Any", WooNooW automatically resolves a non-empty allowed attribute selection (defaulting to the preset variation or first available option), which the buyer can review or adjust before clicking Buy.
Funnel Bypass UX vs The Cart Engine
Direct Buy Now bypasses the customer-facing /cart interface for friction-free purchasing, but it operates through WooCommerce's underlying cart and session engine.
- Do NOT Delete Standard WooCommerce Pages: Never delete
/cart,/checkout, or/my-account. They are required for cart session hydration, tax computation, and order completion.
Pay-Order Flow & Subscription Renewals
WooNooW provides a dedicated payment processing endpoint for existing orders, including manual renewals and unpaid invoices:
Access Control
Access to pay an existing order is strictly validated:
- Order Key Validation: For guests,
keymust match$order->get_order_key()using constant-timehash_equals(). - Authenticated Ownership: Logged-in customers can pay orders where
get_current_user_id() === $order->get_customer_id(). - Administrative Privileges: Store administrators with
manage_woocommercecapability can initiate payment on any order.
Renewal Payment Window Enforcement
When processing payment for a subscription renewal order (order_type === 'renewal'):
- The controller checks
SubscriptionManager::is_renewal_payment_window_open($order). - If the window has expired, renewal orders are transitioned to
cancelledand the customer is prompted to purchase a new subscription.
Architectural Boundaries & Practical Guidance
Session Lifetime Limits & Durability Realities
- Session-Bounded Idempotency: Idempotency records for both
/cart/buyand/checkout/submitare stored within WooCommerce's session cache (woonoow_buy_intentsandwoonoow_checkout_intents, capped at 50 entries each). They do not persist across session purge, cookie expiration, or customer session abandonment. - No Blanket Claims: WooNooW does not claim durability beyond the WooCommerce session lifetime, nor does it guarantee exactly-once payment processing across arbitrary third-party payment gateways.
Root, Entry, and Landing Page SEO Alignment
- In Full SPA Mode, client-side routing dynamically serves checkout and receipt views.
- Recommendation: Configure your store root (
/), entry route, and marketing landing pages to the same page (e.g. frontpage). This prevents crawler confusion and canonical link fragmentation.
Manual Sandbox Gateway Acceptance Testing
- Third-party payment gateways handle inline fields, redirects, webhooks, and 3D Secure / SCA differently. WooNooW does not claim arbitrary third-party payment SDKs can run inline in React without testing.
- Mandatory Testing: Always conduct manual end-to-end sandbox acceptance testing for each active payment gateway (including card declines, payment cancellations, and return redirects) prior to production deployment.
Related Documentation
- Two-Product Sales Funnel — Direct purchasing and 1-click checkout patterns.
- Security Settings Configuration — Rate limiting parameters and invisible CAPTCHA credentials.
- SPA Mode Configuration — Full SPA vs
checkout_onlymode. - Cart REST API Reference — Standard cart endpoints and session lifecycle.
Last updated Sep 14, 2026