Skip to main content
The checkout flow guides users through a streamlined three-step process to complete their purchase, with visual progress tracking and order summary at each stage.

Overview

The checkout process consists of three distinct steps:
1

Shopping Cart

Review items and quantities before proceeding
2

Shipping Information

Enter delivery address and contact details
3

Payment

Provide payment method and complete purchase

Checkout Stepper Component

All checkout pages include a visual progress indicator:
src/pages/cart/Cart.jsx
<CheckoutStepper step={1} />
The stepper highlights the current step and shows completed/upcoming steps, helping users understand their position in the checkout flow.

Step 1: Shopping Cart

Covered in detail in the Shopping Cart documentation. Key actions:
  • Review all cart items
  • Adjust quantities or remove items
  • View order summary with totals
  • Click “Proceed to Checkout” to continue

Step 2: Shipping Information

Page Overview

The shipping page collects delivery information with a clean form interface:
src/pages/checkoutShipping/CheckoutShipping.jsx
export const CheckoutShipping = () => {
  const navigate = useNavigate();
  const { user } = useAuth0();

  const shipping = useUserStore((state) => state.shipping);
  const setShipping = useUserStore((state) => state.setShipping);
  const hydrateFromAuth0 = useUserStore((state) => state.hydrateFromAuth0);

  useEffect(() => {
    if (user) {
      hydrateFromAuth0(user);
    }
  }, [user]);

  const handleSubmit = (e) => {
    e.preventDefault();
    navigate("/checkout/payment");
  };

  return (
    <div className="container shipping-container">
      <CheckoutStepper step={2} />
      <h2>Shipping Information</h2>
      {/* Form content */}
    </div>
  );
};

Form Fields

Full Name

Customer’s complete name for delivery

Address

Street address for shipment

City

City for delivery location

State Management

Shipping data is stored in the user store with persistence:
src/store/useUserStore.js
shipping: {
  fullName: "",
  address: "",
  city: "",
},

setShipping: (data) =>
  set((state) => ({
    shipping: { ...state.shipping, ...data },
  }))

Form Handling

src/pages/checkoutShipping/CheckoutShipping.jsx
const handleChange = (e) => {
  const { name, value } = e.target;
  setShipping({ [name]: value });
};

<form onSubmit={handleSubmit}>
  <input
    name="fullName"
    placeholder="Full name"
    value={shipping.fullName}
    onChange={handleChange}
    required
  />

  <input
    name="address"
    placeholder="Address"
    value={shipping.address}
    onChange={handleChange}
    required
  />

  <input
    name="city"
    placeholder="City"
    value={shipping.city}
    onChange={handleChange}
    required
  />

  <button type="submit">
    Continue to Payment
  </button>
</form>

Auth0 Integration

If the user is authenticated via Auth0, their name is automatically populated in the Full Name field.
src/store/useUserStore.js
hydrateFromAuth0: (user) =>
  set((state) => ({
    shipping: {
      ...state.shipping,
      fullName: state.shipping.fullName || user?.name || "",
    },
  }))

Layout with Order Summary

The page uses a two-column layout:
src/pages/checkoutShipping/CheckoutShipping.jsx
<div className="row">
  <div className="col-md-6">
    {/* Shipping form */}
  </div>
  
  <div className="col-md-5 offset-md-1">
    <OrderSummary />
  </div>
</div>

Step 3: Payment

Payment Form

The payment page collects credit card information with validation:
src/pages/checkoutpayment/CheckoutPayment.jsx
export const CheckoutPayment = () => {
  const navigate = useNavigate();
  const { isAuthenticated, loginWithRedirect } = useAuth0();
  const [processing, setProcessing] = useState(false);
  const [errors, setErrors] = useState({});
  
  const payment = useUserStore((state) => state.payment);
  const setPayment = useUserStore((state) => state.setPayment);
  const addOrder = useUserStore((state) => state.addOrder);

  const cart = useCartStore((state) => state.cart);
  const clearCart = useCartStore((state) => state.clearCart);

  const [formData, setFormData] = useState(payment);

  // Form handling and validation...
};

Payment Fields

Card Number

16-digit card number with auto-formatting (spaces every 4 digits)

Expiry Date

MM/YY format with validation for expired cards

CVC

3-digit security code

Input Formatting

Payment inputs are automatically formatted as users type:
src/pages/checkoutpayment/CheckoutPayment.jsx
const handleChange = (e) => {
  const { name, value } = e.target;
  let formattedValue = value;

  if (name === "cardNumber") {
    const digits = value.replace(/\D/g, "").substring(0, 16);
    formattedValue = digits.replace(/(\d{4})(?=\d)/g, "$1 ");
  }
  
  if (name === "expiryDate") {
    const digits = value.replace(/\D/g, "").substring(0, 4);
    formattedValue =
      digits.length > 2
        ? `${digits.substring(0, 2)}/${digits.substring(2, 4)}`
        : digits;
  }
  
  if (name === "cvc") {
    formattedValue = value.replace(/\D/g, "").substring(0, 3);
  }

  setFormData((prev) => {
    const updated = { ...prev, [name]: formattedValue };
    setPayment(updated);
    return updated;
  });
};

Validation

Comprehensive validation ensures data integrity:
src/pages/checkoutpayment/CheckoutPayment.jsx
const validateExpiry = (value) => {
  if (value.length < 5) return "Format: MM/YY";
  
  const [month, year] = value.split("/").map(Number);
  const currentYear = new Date().getFullYear() % 100;
  const currentMonth = new Date().getMonth() + 1;
  
  if (month < 1 || month > 12) return "Invalid month";
  if (year < currentYear) return "Year expired";
  if (year === currentYear && month < currentMonth) return "Month expired";
  
  return null;
};

const handlePayment = (e) => {
  e.preventDefault();

  const newErrors = {};
  
  if (formData.cardNumber.length < 19)
    newErrors.cardNumber = "Full card number required";
  
  const expiryError = validateExpiry(formData.expiryDate);
  if (expiryError) newErrors.expiryDate = expiryError;
  
  if (formData.cvc.length < 3) newErrors.cvc = "CVC required";

  if (Object.keys(newErrors).length > 0) {
    setErrors(newErrors);
    return;
  }

  // Process payment...
};
All validation errors are displayed inline below the respective input fields for immediate user feedback.

Authentication Check

Users must be authenticated to complete payment:
src/pages/checkoutpayment/CheckoutPayment.jsx
if (!isAuthenticated) {
  loginWithRedirect({
    appState: { returnTo: "/checkout/payment" },
  });
  return;
}
After authentication, users are redirected back to the payment page with their cart intact.

Order Processing

Creating the Order

When payment is submitted, the system:
1

Generate Order ID

Create unique 8-character alphanumeric order identifier
2

Show Processing State

Display “Processing…” on submit button for 1.5 seconds
3

Save Order

Add order to user’s order history with all items and totals
4

Clear Cart

Empty the shopping cart after successful order
5

Navigate to Success

Redirect to success page with order ID
src/pages/checkoutpayment/CheckoutPayment.jsx
if (cart.length === 0) return navigate("/");

const orderId = Math.random().toString(36).substring(2, 10).toUpperCase();

setProcessing(true);
setTimeout(() => {
  addOrder({
    id: orderId,
    date: new Date().toISOString(),
    items: [...cart],
    total: cart.reduce((acc, item) => acc + item.price * item.qty, 0),
  });
  
  clearCart();
  navigate("/payment/success", { state: { orderId } });
}, 1500);

Order Storage

Orders are stored in the user store with persistence:
src/store/useUserStore.js
orders: [],

addOrder: (order) =>
  set((state) => ({
    orders: [...state.orders, order],
  }))

Order Summary Component

Both shipping and payment pages display a real-time order summary:
<div className="col-md-5">
  <OrderSummary />
</div>
The summary shows:
  • All cart items with quantities
  • Subtotal calculation
  • Shipping cost (Free)
  • Final total
The order summary updates automatically if users navigate back to edit their cart.

User Experience Features

Progress Tracking

Visual Stepper

Always visible progress indicator

Back Navigation

Users can navigate backward to edit information

Forward Only

Cannot skip steps - must complete in order

Data Persistence

All shipping and payment form data is persisted to localStorage, allowing users to resume checkout after closing the browser.

Responsive Design

The checkout flow uses responsive Bootstrap grid layouts:
  • Desktop: Side-by-side form and order summary
  • Mobile: Stacked layout with form above summary
<div className="row">
  <div className="col-md-7">
    {/* Form */}
  </div>
  <div className="col-md-5">
    {/* Summary */}
  </div>
</div>

Error Handling

Validation Errors

src/pages/checkoutpayment/CheckoutPayment.jsx
<input
  className={`form-control ${errors.cardNumber ? "is-invalid" : ""}`}
  name="cardNumber"
  value={formData.cardNumber}
  onChange={handleChange}
  required
/>
<div className="error-container">{errors.cardNumber}</div>

Empty Cart Protection

src/pages/checkoutpayment/CheckoutPayment.jsx
if (cart.length === 0) return navigate("/");
If the cart is empty when accessing payment page, users are automatically redirected to the home page.

Best Practices

Form Handling

  • Always validate on both client side (immediate feedback) and before submission
  • Auto-format inputs to match expected patterns (card numbers, dates)
  • Display inline error messages below fields

State Management

  • Use persistent stores for form data to survive page refreshes
  • Clear sensitive payment data after order completion
  • Keep cart in sync across all checkout steps

User Experience

  • Show processing states during async operations
  • Prevent double-submission with disabled buttons
  • Maintain order summary visible throughout checkout

Security

  • Require authentication before payment
  • Validate expiry dates against current date
  • Never store complete card numbers in localStorage
The checkout flow is designed to minimize cart abandonment with clear progress indicators, persistent data, and inline validation.

Build docs developers (and LLMs) love