FluentCart cart and checkout
Keep the browser declarative: it may request an item, quantity, coupon, address,
and method, but the server must resolve every price and eligibility decision.
Read checkout-contract.md before adding a
custom item, fee, checkout field, or nonstandard client.
Use the actual transport
In Free 1.6.0 normal cart mutations use authenticated-by-nonce admin AJAX:
- action=fluent_cart_checkout_routes plus fc_checkout_action
- action=fluent_cart_place_order for order placement
The X-WP-Nonce header is accepted for action fluentcart or wp_rest. It provides
CSRF intent, not user identity or object ownership. Anonymous checkout remains
public.
The registered REST namespace contains checkout and public product routes, but
no active core /cart/add, /cart/update, or /cart/remove route. Do not implement a
headless client from generic documentation alone.
Extend cart items safely
Prefer normal ProductVariation items. For a custom/ghost item:
- Resolve a server-owned offer from the requested opaque item ID.
- Validate availability, currency, quantity, customer eligibility, and mode.
- Return a complete normalized item/variation from the documented custom-item
filters.
- Recompute the item on every quantity/revalidation path.
- Never accept item_price, subtotal, discount, recurring amount, tax class,
downloadable entitlement, or product title as authoritative client values.
- Add a stable discriminator to prevent unrelated custom items from merging.
Relevant surfaces include fluent_cart/cart/validate_custom_item,
fluent_cart/cart_item_product_variation, and the custom-item quantity/change
hooks in Cart/CartResource. Verify the exact callback arguments at 1.6.0 before
registering.
Add fees through Cart
Use Cart::addFee() with a stable key and addon-owned source. Keep calculation
pure and deterministic for the current cart context. Use removeFee() or
removeFeesBySource() when the condition no longer applies. Amounts are integer
minor units.
Do not persist a fee by editing estimated_total or a browser fragment. Fees are
recomputed and cached during the request; avoid recursion from
fluent_cart/cart/fees and call clearFeeCache() only when the underlying context
really changes.
Validate at order placement
- Use fluent_cart/checkout/validate_before_process for whole-request rejection
and return true or WP_Error.
- Use fluent_cart/checkout/validate_data for field-shaped errors after normal
checkout normalization.
- Recheck product, stock, coupon, address, shipping method, tax, gateway, and
customer rules server-side.
- Preserve existing errors and never expose provider secrets or internal
exception details.
- Keep validation free of irreversible side effects; checkout can retry.
Order placement serializes concurrent first submissions with a MySQL named lock
keyed by cart_hash. Do not create a parallel order outside CheckoutApi merely to
obtain an order ID; that defeats the duplicate-submit protection.
Treat cart identity correctly
- fct_cart_hash/cookie is a bearer pointer to a non-completed cart, not an
authorization credential.
- Bind customer-only data to the resolved Customer/WP user, not only cart_hash.
- Clear Resource request caches between simulated requests/users in tests.
- Do not log complete cart hashes, payment tokens, full addresses, or checkout
payloads.
- Expire or detach completed/stale carts through core behavior.
Test the failure paths
Test anonymous and logged-in carts, stale hash, replayed request, two concurrent
place-order requests, zero payment, subscription-only restrictions, invalid
coupon, changed price/stock, invalid shipping method, payment create failure,
and retry after the transaction becomes failed.
Cross-references
- Use fluentcart-payment-gateways for the PaymentInstance handoff.
- Use fluentcart-coupons-discounts for coupon rules and allocation.
- Use fluentcart-rest-headless for a custom client or endpoint.
References
1---2name: fluentcart-cart-checkout3description: Implements and audits FluentCart cart mutation, custom/ghost items, fees, coupons, checkout fields, validation, shipping recalculation, order placement, and duplicate-submit protection. Use when working with CartResource, Cart, cart_hash or fct_cart_hash, fluent_cart_checkout_routes, fluent_cart_place_order, fluent_cart/cart/* or fluent_cart/checkout/* hooks, instant checkout, headless checkout, or any code that must prevent the browser from choosing price, entitlement, stock, shipping, tax, or ownership.4---56# FluentCart cart and checkout78Keep the browser declarative: it may request an item, quantity, coupon, address,9and method, but the server must resolve every price and eligibility decision.1011Read [checkout-contract.md](references/checkout-contract.md) before adding a12custom item, fee, checkout field, or nonstandard client.1314## Use the actual transport1516In Free 1.6.0 normal cart mutations use authenticated-by-nonce admin AJAX:1718- action=fluent_cart_checkout_routes plus fc_checkout_action19- action=fluent_cart_place_order for order placement2021The X-WP-Nonce header is accepted for action fluentcart or wp_rest. It provides22CSRF intent, not user identity or object ownership. Anonymous checkout remains23public.2425The registered REST namespace contains checkout and public product routes, but26no active core /cart/add, /cart/update, or /cart/remove route. Do not implement a27headless client from generic documentation alone.2829## Extend cart items safely3031Prefer normal ProductVariation items. For a custom/ghost item:32331. Resolve a server-owned offer from the requested opaque item ID.342. Validate availability, currency, quantity, customer eligibility, and mode.353. Return a complete normalized item/variation from the documented custom-item36 filters.374. Recompute the item on every quantity/revalidation path.385. Never accept item_price, subtotal, discount, recurring amount, tax class,39 downloadable entitlement, or product title as authoritative client values.406. Add a stable discriminator to prevent unrelated custom items from merging.4142Relevant surfaces include fluent_cart/cart/validate_custom_item,43fluent_cart/cart_item_product_variation, and the custom-item quantity/change44hooks in Cart/CartResource. Verify the exact callback arguments at 1.6.0 before45registering.4647## Add fees through Cart4849Use Cart::addFee() with a stable key and addon-owned source. Keep calculation50pure and deterministic for the current cart context. Use removeFee() or51removeFeesBySource() when the condition no longer applies. Amounts are integer52minor units.5354Do not persist a fee by editing estimated_total or a browser fragment. Fees are55recomputed and cached during the request; avoid recursion from56fluent_cart/cart/fees and call clearFeeCache() only when the underlying context57really changes.5859## Validate at order placement6061- Use fluent_cart/checkout/validate_before_process for whole-request rejection62 and return true or WP_Error.63- Use fluent_cart/checkout/validate_data for field-shaped errors after normal64 checkout normalization.65- Recheck product, stock, coupon, address, shipping method, tax, gateway, and66 customer rules server-side.67- Preserve existing errors and never expose provider secrets or internal68 exception details.69- Keep validation free of irreversible side effects; checkout can retry.7071Order placement serializes concurrent first submissions with a MySQL named lock72keyed by cart_hash. Do not create a parallel order outside CheckoutApi merely to73obtain an order ID; that defeats the duplicate-submit protection.7475## Treat cart identity correctly7677- fct_cart_hash/cookie is a bearer pointer to a non-completed cart, not an78 authorization credential.79- Bind customer-only data to the resolved Customer/WP user, not only cart_hash.80- Clear Resource request caches between simulated requests/users in tests.81- Do not log complete cart hashes, payment tokens, full addresses, or checkout82 payloads.83- Expire or detach completed/stale carts through core behavior.8485## Test the failure paths8687Test anonymous and logged-in carts, stale hash, replayed request, two concurrent88place-order requests, zero payment, subscription-only restrictions, invalid89coupon, changed price/stock, invalid shipping method, payment create failure,90and retry after the transaction becomes failed.9192## Cross-references9394- Use fluentcart-payment-gateways for the PaymentInstance handoff.95- Use fluentcart-coupons-discounts for coupon rules and allocation.96- Use fluentcart-rest-headless for a custom client or endpoint.9798## References99100- Official cart/checkout hooks: <https://dev.fluentcart.com/hooks/actions/cart-checkout/>101- Official fee tutorial: <https://dev.fluentcart.com/modules/fee-system/>102- Official ghost product tutorial: <https://dev.fluentcart.com/modules/ghost-product-selling/>103- Verified Free source paths:104 - fluent-cart/api/Checkout/CheckoutApi.php105 - fluent-cart/api/Resource/FrontendResource/CartResource.php106 - fluent-cart/app/Models/Cart.php107 - fluent-cart/app/Hooks/Cart/WebCheckoutHandler.php108 - fluent-cart/app/Http/Routes/WebRoutes.php109 - fluent-cart/app/Http/Controllers/CheckoutController.php110 - fluent-cart/app/Helpers/CartHelper.php